-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_pipeline.py
More file actions
191 lines (154 loc) · 7.94 KB
/
Copy pathtest_pipeline.py
File metadata and controls
191 lines (154 loc) · 7.94 KB
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
"""End-to-end pipeline: poll -> filter -> score -> notify, for real (roadmap M4, ADR-0025).
Wires every subsystem this milestone built through the ported Greenhouse adapter against the
recorded GitLab fixture — the actual vertical slice, running against real (if trimmed) ATS
response data and a real example profile, not synthetic stand-ins for either.
"""
from __future__ import annotations
import json
from pathlib import Path
from unittest.mock import patch
from openjobradar.config import load_profile
from openjobradar.pipeline import run_for_org
from openjobradar.scoring import StubScorer, build_facet_context, get_builtin_rubric
from openjobradar.tenancy import InMemoryStore, TenantContext, TenantRepository
from openjobradar.tenancy.watchlist import WatchlistEntry
FIXTURES = Path(__file__).parent / "fixtures"
EXAMPLES = Path(__file__).resolve().parents[1] / "examples" / "profiles"
ALICE = TenantContext.from_sub("alice-sub-0000001")
BOB = TenantContext.from_sub("bob-sub-00000002")
_CHANNELS = {"email": {"enabled": True}}
def _load_fixture(name: str):
return json.loads((FIXTURES / name).read_text(encoding="utf-8"))
def _run(ctx, org, repo, profile, at, **overrides):
kwargs = {
"repo": repo,
"scorer": StubScorer(),
"profile": profile,
"profile_facets": build_facet_context(profile),
"rubric": get_builtin_rubric("data_analytics_ml"),
"channels_config": _CHANNELS,
"alert_threshold": 70,
"digest_threshold": 40,
"dry_run": True,
"at": at,
**overrides,
}
return run_for_org(ctx, org, **kwargs)
def _gitlab_org() -> WatchlistEntry:
return WatchlistEntry(
slug="gitlab", name="GitLab", ats_type="greenhouse", ats_board_id="gitlab",
description="DevSecOps platform.",
)
def test_pipeline_runs_the_real_gitlab_board_through_a_real_profile() -> None:
profile = load_profile(EXAMPLES / "jordan-lee.v2.yaml")
repo = TenantRepository(InMemoryStore())
org = _gitlab_org()
fixture = _load_fixture("greenhouse_sample.json")
with patch("openjobradar.poll.adapters.greenhouse.get_json", return_value=fixture):
result = _run(ALICE, org, repo, profile, "2026-08-23T12:00:00Z")
assert result.org_slug == "gitlab"
assert result.fetched == 2
assert len(result.outcomes) == 2
for outcome in result.outcomes:
assert outcome.stage in (
"hard_filtered", "location_filtered", "values_filtered", "scored",
)
assert outcome.stage != "already_seen" # first run: nothing seen yet
def test_second_cycle_marks_everything_already_seen() -> None:
profile = load_profile(EXAMPLES / "jordan-lee.v2.yaml")
repo = TenantRepository(InMemoryStore())
org = _gitlab_org()
fixture = _load_fixture("greenhouse_sample.json")
with patch("openjobradar.poll.adapters.greenhouse.get_json", return_value=fixture):
_run(ALICE, org, repo, profile, "2026-08-23T12:00:00Z")
second = _run(ALICE, org, repo, profile, "2026-08-23T13:00:00Z")
assert all(outcome.stage == "already_seen" for outcome in second.outcomes)
def test_scored_postings_persist_and_carry_a_verdict() -> None:
profile = load_profile(EXAMPLES / "jordan-lee.v2.yaml")
repo = TenantRepository(InMemoryStore())
org = _gitlab_org()
fixture = _load_fixture("greenhouse_sample.json")
with patch("openjobradar.poll.adapters.greenhouse.get_json", return_value=fixture):
result = _run(ALICE, org, repo, profile, "2026-08-23T12:00:00Z")
scored = result.scored
assert scored, "expected at least one posting to clear every filter and reach scoring"
for outcome in scored:
assert outcome.score is not None and 0 <= outcome.score.score <= 100
assert outcome.notify is not None
assert outcome.notify.verdict in ("alert", "digest", "suppressed")
stored = repo.get(ALICE, "postings", outcome.posting.stable_id)
assert stored is not None
assert stored["verdict"] == outcome.notify.verdict
assert stored["score"] == outcome.score.score
def test_pipeline_state_is_isolated_per_tenant() -> None:
profile = load_profile(EXAMPLES / "jordan-lee.v2.yaml")
repo = TenantRepository(InMemoryStore())
org = _gitlab_org()
fixture = _load_fixture("greenhouse_sample.json")
with patch("openjobradar.poll.adapters.greenhouse.get_json", return_value=fixture):
alice_result = _run(ALICE, org, repo, profile, "2026-08-23T12:00:00Z")
# Bob never ran the pipeline. His first run must still see everything fresh -- Alice's
# dedupe ledger must not leak into his "already seen" check.
bob_result = _run(BOB, org, repo, profile, "2026-08-23T12:00:00Z")
assert alice_result.scored, "test needs at least one scored posting to probe isolation on"
# A stranger who never ran the pipeline reads nothing -- neither Alice's rows nor her
# dedupe state are addressable from outside her own TenantContext.
stranger = TenantContext.from_sub("stranger-tenant-aaaaaaaa")
for outcome in alice_result.scored:
assert repo.get(stranger, "postings", outcome.posting.stable_id) is None
stranger_pass = _run(stranger, org, repo, profile, "2026-08-23T12:00:00Z")
assert all(outcome.stage != "already_seen" for outcome in stranger_pass.outcomes)
assert all(outcome.stage != "already_seen" for outcome in bob_result.outcomes)
def test_location_filter_actually_rejects_an_onsite_posting_for_a_remote_only_profile() -> None:
"""The real fixture happens to be all-remote (nothing to reject on location); this proves
the filter stage is live, not a no-op, using a minimal synthetic posting for the one axis
the recorded fixture doesn't happen to exercise."""
profile = load_profile(EXAMPLES / "jordan-lee.v2.yaml") # remote_only: true
repo = TenantRepository(InMemoryStore())
org = _gitlab_org()
onsite_only = {
"jobs": [
{
"id": 999,
"title": "Platform Engineer",
"location": {"name": "New York, NY — onsite 5 days/week"},
"absolute_url": "https://boards.greenhouse.io/gitlab/jobs/999",
"content": "<p>Come into our NYC office every day.</p>",
}
]
}
with patch("openjobradar.poll.adapters.greenhouse.get_json", return_value=onsite_only):
result = _run(ALICE, org, repo, profile, "2026-08-23T12:00:00Z")
assert len(result.outcomes) == 1
assert result.outcomes[0].stage == "location_filtered"
def test_dry_run_never_produces_a_live_alert_silently() -> None:
"""Never-silent-death (ADR-0017/0019) extended to the pipeline: even a high-scoring dry-run
posting comes back as a real 'alert' verdict with is_dry_run=True, never suppressed."""
profile = load_profile(EXAMPLES / "jordan-lee.v2.yaml")
repo = TenantRepository(InMemoryStore())
org = _gitlab_org()
# StubScorer's overlap logic needs a JD stuffed with the profile's own tech stack/domain
# terms to score high — this is deliberately engineered to land in the "alert" band.
high_signal = {
"jobs": [
{
"id": 1,
"title": "Staff Data Engineer",
"location": {"name": "Remote"},
"absolute_url": "https://boards.greenhouse.io/gitlab/jobs/1",
"content": (
"<p>Python dbt Snowflake Airflow healthcare data interoperability "
"human services analytics Staff Data Engineer Senior Data Engineer</p>"
),
}
]
}
with patch("openjobradar.poll.adapters.greenhouse.get_json", return_value=high_signal):
result = _run(
ALICE, org, repo, profile, "2026-08-23T12:00:00Z",
dry_run=True, alert_threshold=50, digest_threshold=10,
)
assert len(result.scored) == 1
outcome = result.scored[0]
assert outcome.notify.verdict == "alert"
assert outcome.notify.is_dry_run is True