"""Router-level tests for the campaign manager-approval workflow.""" from app.models.campaign import Campaign, CampaignTest from app.models.technique import Technique from app.models.test import Test from app.models.enums import TestState def _make_draft_campaign(db, owner_id): tech = Technique(mitre_id="T1059", name="Command Line", tactic="execution", platforms=["windows"]) db.add(tech) db.flush() campaign = Campaign(name="Router Test Campaign", type="custom", status="draft", created_by=owner_id) db.add(campaign) db.flush() test = Test(technique_id=tech.id, name="T1059 test", state=TestState.draft, created_by=owner_id) db.add(test) db.flush() db.add(CampaignTest(campaign_id=campaign.id, test_id=test.id, order_index=0)) db.commit() db.refresh(campaign) return campaign def test_lead_can_submit_own_campaign(api, db, red_lead_user, red_lead_headers): campaign = _make_draft_campaign(db, red_lead_user.id) resp = api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_lead_headers) assert resp.status_code == 200 assert resp.json()["status"] == "pending_approval" def test_red_tech_cannot_submit(api, db, red_lead_user, red_tech_headers): campaign = _make_draft_campaign(db, red_lead_user.id) resp = api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_tech_headers) assert resp.status_code == 403 def test_manager_can_approve_and_sets_start_date(api, db, red_lead_user, red_lead_headers, manager_headers): campaign = _make_draft_campaign(db, red_lead_user.id) api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_lead_headers) resp = api( "post", f"/api/v1/campaigns/{campaign.id}/approve", manager_headers, json={"start_date": "2026-09-01T00:00:00"}, ) assert resp.status_code == 200 body = resp.json() assert body["status"] == "active" assert body["start_date"] is not None def test_lead_cannot_approve(api, db, red_lead_user, red_lead_headers): campaign = _make_draft_campaign(db, red_lead_user.id) api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_lead_headers) resp = api( "post", f"/api/v1/campaigns/{campaign.id}/approve", red_lead_headers, json={"start_date": "2026-09-01T00:00:00"}, ) assert resp.status_code == 403 def test_admin_can_approve_as_manager_backup(api, db, red_lead_user, red_lead_headers, auth_headers): campaign = _make_draft_campaign(db, red_lead_user.id) api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_lead_headers) resp = api( "post", f"/api/v1/campaigns/{campaign.id}/approve", auth_headers, json={"start_date": "2026-09-01T00:00:00"}, ) assert resp.status_code == 200 def test_manager_can_reject_with_reason(api, db, red_lead_user, red_lead_headers, manager_headers): campaign = _make_draft_campaign(db, red_lead_user.id) api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_lead_headers) resp = api( "post", f"/api/v1/campaigns/{campaign.id}/reject", manager_headers, json={"reason": "Needs more detail"}, ) assert resp.status_code == 200 assert resp.json()["status"] == "draft" def test_manager_reject_without_reason_rejected_by_validation(api, db, red_lead_user, red_lead_headers, manager_headers): campaign = _make_draft_campaign(db, red_lead_user.id) api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_lead_headers) resp = api( "post", f"/api/v1/campaigns/{campaign.id}/reject", manager_headers, json={"reason": ""}, ) assert resp.status_code == 400