fix(campaigns): start_date modal + hide future-campaign tests from queue
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

Backend: activate endpoint returns 409 with structured warning when
start_date is in the future; accepts force=true to bypass.
test_crud_service: always excludes tests from draft campaigns with future
start_date so they do not appear in the team queue prematurely.

Frontend: catches 409 on activate and shows amber confirmation modal
with Keep scheduled / Activate now anyway options.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kitos
2026-06-04 14:05:58 +02:00
parent f8418bc7ea
commit 4c230caa32
4 changed files with 91 additions and 19 deletions

View File

@@ -19,7 +19,7 @@ from app.models.enums import TestState
from app.models.technique import Technique
from app.models.test import Test
from app.models.test_template import TestTemplate
from app.models.campaign import CampaignTest
from app.models.campaign import Campaign, CampaignTest
from app.models.audit import AuditLog
from app.utils import escape_like
@@ -36,7 +36,12 @@ def list_tests(
offset: int = 0,
limit: int = 50,
) -> list[Test]:
"""Return a paginated list of tests with optional filters."""
"""Return a paginated list of tests with optional filters.
Tests that belong to a campaign still in 'draft' status AND with a
start_date in the future are always excluded — they should not appear
in the team's queue until the campaign is activated on its start date.
"""
query = db.query(Test).options(joinedload(Test.technique))
if state:
@@ -61,6 +66,22 @@ def list_tests(
linked = db.query(CampaignTest.test_id).distinct().subquery()
query = query.filter(~Test.id.in_(linked))
# Always hide tests from scheduled campaigns that haven't started yet.
# A "scheduled-but-not-yet-active" campaign = draft status + start_date in future.
now = datetime.utcnow()
future_draft_tests = (
db.query(CampaignTest.test_id)
.join(Campaign, Campaign.id == CampaignTest.campaign_id)
.filter(
Campaign.status == "draft",
Campaign.start_date.isnot(None),
Campaign.start_date > now,
)
.distinct()
.subquery()
)
query = query.filter(~Test.id.in_(future_draft_tests))
return query.order_by(Test.created_at.desc()).offset(offset).limit(limit).all()