fix(backend): sprint 5 post-review — name fallback, isinstance guards, 400 tests

- create_simulation: name falls back to template.name when template_id provided
  and name is absent/empty (AC-27.1)
- templates POST/PATCH: isinstance(list) check on technique_ids/tactic_ids
  before resolving, returns 400 with clear message
- 5 new tests: unknown technique_id → 400 (POST+PATCH), unknown tactic_id → 400
  (POST+PATCH), name fallback to template.name
- mypy: merged template branch into if/else to eliminate union-attr false positives

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Knacky
2026-05-28 07:04:25 +02:00
parent 33a0ca30bb
commit 55f993fa24
4 changed files with 91 additions and 10 deletions

View File

@@ -43,17 +43,7 @@ def create_simulation(eid: int):
data = request.get_json(silent=True) or {}
name = (data.get("name") or "").strip()
if not name:
return jsonify({"error": "name is required"}), 400
template_id = data.get("template_id")
sim = Simulation(
engagement_id=eid,
name=name,
status=SimulationStatus.PENDING,
created_at=datetime.now(UTC),
created_by_id=g.current_user.id,
)
if template_id is not None:
from backend.app.models.simulation_template import SimulationTemplate
@@ -61,11 +51,30 @@ def create_simulation(eid: int):
tmpl = db.session.get(SimulationTemplate, template_id)
if tmpl is None:
return jsonify({"error": "Template not found"}), 404
if not name:
name = tmpl.name
sim = Simulation(
engagement_id=eid,
name=name,
status=SimulationStatus.PENDING,
created_at=datetime.now(UTC),
created_by_id=g.current_user.id,
)
sim.description = tmpl.description
sim.commands = tmpl.commands
sim.prerequisites = tmpl.prerequisites
sim.techniques = list(tmpl.techniques or [])
sim.tactic_ids = list(tmpl.tactic_ids or [])
else:
if not name:
return jsonify({"error": "name is required"}), 400
sim = Simulation(
engagement_id=eid,
name=name,
status=SimulationStatus.PENDING,
created_at=datetime.now(UTC),
created_by_id=g.current_user.id,
)
db.session.add(sim)
db.session.commit()