Files
ai-agent/backend/test_forms.py
Gabriel Vidal 0ff9e40242 refactor(backend): split main.py into domain packages with app.state injection
main.py (5146 lines, 106 routes) becomes an assembly only: one package per
domain — core, files, settings, dashboards, conversations, diff,
notifications, forms, runs, accounts, workers, models, cron, agents,
projects, services, goals, memories, plans, templates — each exposing an
APIRouter; the flat domain modules move into their package behind a barrel
that keeps the old `import conversations` / `import projects` spellings.

The shared singletons (store, meta_store, hub, indexer, …) are built once by
core.state.build_state() and attached to app.state.ai; routes take them as
the `deps: State` dependency and helpers as an explicit `deps: AppState`.
conversations/pricing.py carries the per-model rates out of the parser.

Verified: route table and OpenAPI byte-identical; 90 read endpoints
golden-diffed against the monolith on a copy of the live data (identical);
write routes smoke-tested; 66 backend tests pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 23:55:47 +02:00

106 lines
4.7 KiB
Python

"""forms.valid_fields / check_answers — the display blocks (markdown, html)."""
import unittest
import forms
class HtmlBlockTest(unittest.TestCase):
def test_html_block_is_display_only(self):
out = forms.valid_fields([
{"type": "html", "label": "Chart", "html": "<svg><rect/></svg>"},
{"key": "ok", "label": "Fine?", "type": "checkbox"},
])
blk = out[0]
self.assertEqual(blk["type"], "html")
self.assertEqual(blk["key"], "chart")
self.assertEqual(blk["html"], "<svg><rect/></svg>")
self.assertFalse(blk["required"])
self.assertFalse(blk["sandbox"])
self.assertNotIn("text", blk)
# Never answered: ignored by answer validation and the recap.
self.assertEqual(forms.check_answers(out, {"chart": "x", "ok": True}), {"ok": True})
self.assertEqual(forms.answer_lines(out, {"ok": True}), ["- **Fine?** — yes"])
def test_script_implies_sandbox_and_height_is_kept(self):
out = forms.valid_fields([
{"type": "html", "html": "<div id=c></div><SCRIPT>draw()</SCRIPT>", "height": "320.0"},
])
self.assertTrue(out[0]["sandbox"])
self.assertEqual(out[0]["height"], 320)
self.assertEqual(out[0]["key"], "html-1")
self.assertEqual(out[0]["label"], "")
def test_explicit_sandbox_and_text_alias(self):
out = forms.valid_fields([{"type": "html", "text": "<b>hi</b>", "sandbox": True}])
self.assertEqual(out[0]["html"], "<b>hi</b>")
self.assertTrue(out[0]["sandbox"])
self.assertNotIn("height", out[0])
def test_style_sheet_implies_sandbox(self):
out = forms.valid_fields([{"type": "html", "html": "<style>b{color:red}</style><b>hi</b>"}])
self.assertTrue(out[0]["sandbox"])
out = forms.valid_fields([{"type": "html", "html": "<b style='color:red'>hi</b>"}])
self.assertFalse(out[0]["sandbox"])
def test_empty_or_oversized_html_rejected(self):
with self.assertRaises(ValueError):
forms.valid_fields([{"type": "html", "label": "x"}])
with self.assertRaises(ValueError):
forms.valid_fields([{"type": "html", "html": "<p>" + "a" * (600 * 1024)}])
def test_markdown_block_unchanged(self):
out = forms.valid_fields([{"type": "markdown", "text": "**hi**"}])
self.assertEqual(out[0]["text"], "**hi**")
self.assertNotIn("html", out[0])
if __name__ == "__main__":
unittest.main()
class OtherOptionTest(unittest.TestCase):
FIELDS = [
{"key": "stack", "label": "Stack", "type": "radio", "required": True,
"options": ["Vite", "Next"]},
{"key": "feat", "label": "Features", "type": "multiselect",
"options": ["auth", "db"]},
{"key": "pick", "label": "Pick", "type": "select", "options": ["a", "b"],
"other": True},
]
def test_other_defaults_to_required(self):
out = forms.valid_fields(self.FIELDS)
self.assertTrue(out[0]["other"]) # required ⇒ on
self.assertFalse(out[1]["other"]) # optional ⇒ off
self.assertTrue(out[2]["other"]) # explicit
off = forms.valid_fields([dict(self.FIELDS[0], other=False)])
self.assertFalse(off[0]["other"])
def test_free_text_accepted_only_with_other(self):
out = forms.valid_fields(self.FIELDS)
ans = forms.check_answers(out, {"stack": "Astro", "pick": "zzz", "feat": ["auth"]})
self.assertEqual(ans, {"stack": "Astro", "pick": "zzz", "feat": ["auth"]})
with self.assertRaises(ValueError):
forms.check_answers(out, {"stack": "Vite", "feat": ["nope"]})
def test_sentinel_counts_as_unanswered(self):
out = forms.valid_fields(self.FIELDS)
with self.assertRaises(ValueError):
forms.check_answers(out, {"stack": forms.OTHER})
ans = forms.check_answers(out, {"stack": "Vite", "pick": forms.OTHER,
"feat": [forms.OTHER, "db"]})
self.assertEqual(ans, {"stack": "Vite", "feat": ["db"]})
lines = forms.answer_lines(out, ans)
self.assertEqual(lines, ["- **Stack** — Vite", "- **Features** — db"])
class AssetsTest(unittest.TestCase):
def test_apply_assets_recurses_and_keeps_unknown(self):
spec = [{"type": "html", "html": '<img src="{{asset:shot.png}}"> {{ asset:nope }}',
"nested": {"x": ["{{asset:shot.png}}"]}}]
out = forms.apply_assets(spec, {"shot.png": "/api/upload-file?path=b/shot.png"})
self.assertEqual(out[0]["html"],
'<img src="/api/upload-file?path=b/shot.png"> {{ asset:nope }}')
self.assertEqual(out[0]["nested"]["x"], ["/api/upload-file?path=b/shot.png"])