1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
from datetime import UTC, datetime
from fastapi.testclient import TestClient
from srht_contrib.main import create_app
from srht_contrib.models import ContributionEvent
def test_read_only_contribution_routes_are_public_and_write_routes_require_api_key(settings, db_engine, session_factory) -> None:
app = create_app(settings, engine=db_engine, session_factory=session_factory)
with TestClient(app) as open_client:
response = open_client.get("/health")
public_contributions = open_client.get("/api/contributions/~ccleberg?from=2026-03-28&to=2026-03-30")
public_stats = open_client.get("/api/contributions/~ccleberg/stats?from=2026-03-28&to=2026-03-30")
assert response.status_code == 200
assert public_contributions.status_code == 200
assert public_stats.status_code == 200
with TestClient(app) as unauthorized:
unauthorized_response = unauthorized.post("/api/contributions/poll?actor=~ccleberg")
assert unauthorized_response.status_code == 401
with TestClient(app) as invalid:
invalid.headers.update({"X-API-Key": "wrong-key"})
invalid_response = invalid.get("/api/repositories")
assert invalid_response.status_code == 401
def test_contributions_api_returns_zero_filled_range(client: TestClient, db_session) -> None:
db_session.add(
ContributionEvent(
service="todo",
event_type="ticket_created",
actor="~ccleberg",
repo_name=None,
resource_id="1",
external_uid="todo:ticket:1:created",
occurred_at=datetime(2026, 3, 30, 10, 0, tzinfo=UTC),
weight=1.0,
raw_payload_json=None,
)
)
db_session.commit()
response = client.get("/api/contributions/~ccleberg?from=2026-03-28&to=2026-03-30")
assert response.status_code == 200
assert response.json()["days"] == [
{"date": "2026-03-28", "count": 0, "score": 0.0},
{"date": "2026-03-29", "count": 0, "score": 0.0},
{"date": "2026-03-30", "count": 1, "score": 1.0},
]
def test_contribution_stats_api(client: TestClient, db_session) -> None:
db_session.add_all(
[
ContributionEvent(
service="todo",
event_type="ticket_created",
actor="~ccleberg",
repo_name=None,
resource_id="1",
external_uid="todo:ticket:1:created",
occurred_at=datetime(2026, 3, 29, 10, 0, tzinfo=UTC),
weight=1.0,
raw_payload_json=None,
),
ContributionEvent(
service="todo",
event_type="ticket_comment",
actor="~ccleberg",
repo_name=None,
resource_id="1",
external_uid="todo:ticket:1:comment:2",
occurred_at=datetime(2026, 3, 30, 11, 0, tzinfo=UTC),
weight=0.5,
raw_payload_json=None,
),
]
)
db_session.commit()
response = client.get("/api/contributions/~ccleberg/stats?from=2026-03-28&to=2026-03-30")
assert response.status_code == 200
assert response.json()["total_events"] == 2
assert response.json()["total_score"] == 1.5
assert response.json()["longest_streak"] == 2
assert response.json()["current_streak"] == 2
def test_invalid_date_input_returns_400(client: TestClient) -> None:
response = client.get("/api/contributions/~ccleberg?from=2026-13-01&to=2026-03-30")
assert response.status_code == 400
assert "Invalid date format" in response.json()["detail"]
def test_contribution_routes_use_settings_backed_alias_resolution(settings, db_engine, session_factory) -> None:
alias_settings = settings.model_copy(update={"actor_aliases_json": {"~ccleberg": ["[email protected]"]}})
app = create_app(alias_settings, engine=db_engine, session_factory=session_factory)
with session_factory() as session:
session.add(
ContributionEvent(
service="todo",
event_type="ticket_created",
actor="~ccleberg",
repo_name=None,
resource_id="1",
external_uid="todo:alias:1",
occurred_at=datetime(2026, 3, 30, 10, 0, tzinfo=UTC),
weight=1.0,
raw_payload_json=None,
)
)
session.commit()
with TestClient(app) as client:
client.headers.update({"X-API-Key": alias_settings.api_key})
response = client.get("/api/contributions/[email protected]?from=2026-03-30&to=2026-03-30")
assert response.status_code == 200
assert response.json()["actor"] == "~ccleberg"
|