init
This commit is contained in:
121 files changed
+17641
No files matched your search
@@ -0,0 +1,100 @@
|
||||
"""Source mapping: the parts that work without a network."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
|
||||
from app.settings_schema import JiraSourceSettings, LaundrySourceSettings
|
||||
from app.sources.jira import JiraSource, start_date_clause
|
||||
from app.sources.laundry import parse_cookie_header, parse_my_slots
|
||||
|
||||
JIRA_CONFIG = JiraSourceSettings(
|
||||
enabled=True,
|
||||
base_url="https://example.atlassian.net",
|
||||
email="me@example.com",
|
||||
api_token="token",
|
||||
)
|
||||
|
||||
|
||||
def test_start_date_clause_handles_custom_fields_and_names():
|
||||
assert start_date_clause("customfield_10015") == "cf[10015]"
|
||||
assert start_date_clause("Start date") == '"Start date"'
|
||||
|
||||
|
||||
def test_jira_jql_includes_the_extra_filter():
|
||||
source = JiraSource(JIRA_CONFIG.model_copy(update={"extra_jql": "assignee = currentUser()"}))
|
||||
jql = source._jql()
|
||||
assert "statusCategory != Done" in jql
|
||||
assert "assignee = currentUser()" in jql
|
||||
source.close()
|
||||
|
||||
|
||||
def test_jira_issue_maps_to_a_todo_item():
|
||||
source = JiraSource(JIRA_CONFIG)
|
||||
item = source._to_item(
|
||||
{
|
||||
"key": "ABC-1",
|
||||
"fields": {
|
||||
"summary": "Fix the thing",
|
||||
"duedate": "2026-10-20",
|
||||
"customfield_10015": "2026-10-15",
|
||||
"status": {"name": "In Progress", "statusCategory": {"key": "indeterminate"}},
|
||||
"priority": {"name": "High"},
|
||||
"project": {"name": "Platform"},
|
||||
"labels": ["backend"],
|
||||
},
|
||||
}
|
||||
)
|
||||
source.close()
|
||||
assert item.external_id == "ABC-1"
|
||||
assert item.title == "Fix the thing"
|
||||
assert item.url == "https://example.atlassian.net/browse/ABC-1"
|
||||
assert item.due_date == date(2026, 10, 20)
|
||||
assert item.start_date == date(2026, 10, 15)
|
||||
assert item.priority == "High"
|
||||
assert item.is_done is False
|
||||
assert item.days_overdue(date(2026, 10, 22)) == 2
|
||||
|
||||
|
||||
def test_cookie_header_accepts_both_forms():
|
||||
assert parse_cookie_header("abc123") == {"sessionid": "abc123"}
|
||||
parsed = parse_cookie_header("csrftoken=x; sessionid=abc123")
|
||||
assert parsed["sessionid"] == "abc123"
|
||||
|
||||
|
||||
LAUNDRY_HTML = """
|
||||
<div class="date-machines" id="Чт, 15 окт.">
|
||||
<div class="machines">
|
||||
<div class="floor-title">Этаж 1</div>
|
||||
<div class="machine">
|
||||
<div class="machine-title">Машинка 3</div>
|
||||
<div class="period my finished"><div>09:00-12:00</div></div>
|
||||
<div class="period my current"><div>18:00-21:00</div></div>
|
||||
<div class="period"><div>21:00-00:00</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
|
||||
def test_laundry_grid_yields_only_my_upcoming_slots():
|
||||
label, slots = parse_my_slots(LAUNDRY_HTML)
|
||||
assert label == "Чт, 15 окт."
|
||||
assert len(slots) == 2
|
||||
upcoming = [slot for slot in slots if slot.is_upcoming]
|
||||
assert len(upcoming) == 1
|
||||
assert upcoming[0].time_range == "18:00-21:00"
|
||||
assert upcoming[0].machine == "Машинка 3"
|
||||
assert upcoming[0].location == "Этаж 1"
|
||||
|
||||
|
||||
def test_laundry_config_requires_a_session_cookie():
|
||||
from app.sources.base import SourceError
|
||||
|
||||
try:
|
||||
from app.sources.laundry import LaundrySource
|
||||
|
||||
LaundrySource(LaundrySourceSettings(enabled=True, session_cookie=""))
|
||||
except SourceError:
|
||||
return
|
||||
raise AssertionError("expected a SourceError")
|
||||
Reference in new issue
Block a user