fix(backend): resolve refresh-token expiry deadlock, missing Jira on normal campaign approval, discarded threat-actor campaign start_date

- /auth/refresh now allows a short grace window past expiry and checks
  the blacklist, so an active session's silent refresh no longer fails
  the instant its own token expires
- normal manager /approve flow now creates Jira tickets for the campaign
  and its already-linked tests, matching the admin-only /activate path
- GenerateFromActorPayload now accepts start_date and threads it through
  to the new campaign instead of silently discarding it
This commit is contained in:
kitos
2026-07-07 13:42:26 +02:00
parent fefe70baed
commit bd26b09827
6 changed files with 236 additions and 31 deletions
@@ -1,5 +1,7 @@
"""Router-level tests for the campaign manager-approval workflow."""
from unittest.mock import patch
from app.models.campaign import Campaign, CampaignTest
from app.models.technique import Technique
from app.models.test import Test
@@ -51,6 +53,30 @@ def test_manager_can_approve_and_sets_start_date(api, db, red_lead_user, red_lea
assert body["start_date"] is not None
def test_manager_approval_creates_jira_tickets(api, db, red_lead_user, red_lead_headers, manager_headers):
"""The normal manager-approval path must create Jira tickets too, not just
the admin-only emergency /activate override — this was the Block 1 gap:
campaigns approved through the standard flow never got a Jira ticket."""
campaign = _make_draft_campaign(db, red_lead_user.id)
api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_lead_headers)
with patch("app.services.jira_service.get_campaign_jira_key", return_value=None) as mock_get_key, \
patch("app.services.jira_service.auto_create_campaign_issue", return_value="PT-100") as mock_create_campaign, \
patch("app.services.jira_service.get_test_jira_key", return_value=None), \
patch("app.services.jira_service.auto_create_test_issue") as mock_create_test:
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
mock_get_key.assert_called_once()
mock_create_campaign.assert_called_once()
mock_create_test.assert_called_once()
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)