mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-18 15:50:25 +02:00
* feat: bundle standalone taste distillation and application workflows * docs: fix imported taste skill markdown lint * docs: align Turkish agent catalog with taste skills * refactor: make ECC the canonical reusable video engine * fix: preserve video duration when applying image overlays * fix: preserve background colors in image compositing * fix: report best-effort duration targets and shortfalls * feat: ship verified Fusion presets with compatibility provenance * feat(tasteforge): preserve native edits in application bundles * feat(tasteforge): compile local preservation without hosted input * fix: update js-yaml to patched 4.3.2 * test: report bounded Stop wrapper failure diagnostics * fix(tasteforge): fail closed on unsafe output names, missing overlays and cadence - cli: default report and spec paths are derived from pack name and profile genre; require the manifest's name pattern before using either as a filename part so a traversal string cannot write outside cwd/out. - apply_local: a pack without cadence.json, or with no measured shots and no explicit mean_shot, raises instead of silently planning 1.0s shots and reporting a measured cadence. - legacy apply: a missing overlay aborts before any paid upload; forge() would have rejected it after every take was generated. - requirements-live: pin fal-client>=0.13.0, the first release whose subscribe() accepts client_timeout. Addresses the five P1 findings from the independent review of #3033. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015fxHRsydPqEcYngGbqkgt1 --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
106 lines
3.9 KiB
Python
106 lines
3.9 KiB
Python
"""Failing-first tests for the tasteforge schema subset validator.
|
|
|
|
Contract (from the recovered TasteForge gen4 source, canonicalized):
|
|
- hand-rolled JSON-Schema subset: type, required, properties, items, enum,
|
|
minimum/minimum, minItems, pattern; no third-party dependency.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1] / "scripts"
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
|
|
from tasteforge import schema # noqa: E402
|
|
|
|
|
|
class ValidatorTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.simple = {
|
|
"type": "object",
|
|
"required": ["name", "count"],
|
|
"properties": {
|
|
"name": {"type": "string", "pattern": "^[a-z][a-z0-9_-]*$"},
|
|
"count": {"type": "integer", "minimum": 0},
|
|
"tags": {"type": "array", "items": {"type": "string"}, "minItems": 1},
|
|
"mode": {"type": "string", "enum": ["local", "dry-run"]},
|
|
},
|
|
}
|
|
|
|
def test_accepts_valid_instance(self):
|
|
problems = schema.validate(
|
|
{"name": "flashethereal", "count": 3, "tags": ["a"], "mode": "local"},
|
|
self.simple,
|
|
)
|
|
self.assertEqual(problems, [])
|
|
|
|
def test_rejects_missing_required(self):
|
|
problems = schema.validate({"count": 3}, self.simple)
|
|
self.assertTrue(any("required" in p and "name" in p for p in problems))
|
|
|
|
def test_rejects_wrong_type(self):
|
|
problems = schema.validate({"name": "x", "count": "three"}, self.simple)
|
|
self.assertTrue(any("count" in p and "type" in p for p in problems))
|
|
|
|
def test_rejects_bad_pattern(self):
|
|
problems = schema.validate({"name": "Bad Name!", "count": 0}, self.simple)
|
|
self.assertTrue(any("name" in p and "pattern" in p for p in problems))
|
|
|
|
def test_rejects_bad_enum(self):
|
|
problems = schema.validate({"name": "x", "count": 0, "mode": "live"}, self.simple)
|
|
self.assertTrue(any("mode" in p and "enum" in p for p in problems))
|
|
|
|
def test_rejects_below_minimum(self):
|
|
problems = schema.validate({"name": "x", "count": -1}, self.simple)
|
|
self.assertTrue(any("count" in p and "minimum" in p for p in problems))
|
|
|
|
def test_rejects_bad_items_and_min_items(self):
|
|
problems = schema.validate({"name": "x", "count": 0, "tags": [1, 2]}, self.simple)
|
|
self.assertTrue(any("tags[0]" in p for p in problems))
|
|
problems = schema.validate({"name": "x", "count": 0, "tags": []}, self.simple)
|
|
self.assertTrue(any("tags" in p and "minItems" in p for p in problems))
|
|
|
|
def test_non_object_root_rejected(self):
|
|
problems = schema.validate(["not", "an", "object"], self.simple)
|
|
self.assertTrue(problems)
|
|
|
|
|
|
class ExportedSchemasTests(unittest.TestCase):
|
|
EXPORTED = [
|
|
"TASTE_PROFILE_SCHEMA",
|
|
"PACK_MANIFEST_SCHEMA",
|
|
"GRADE_SCHEMA",
|
|
"CADENCE_SCHEMA",
|
|
"SPEC_SCHEMA",
|
|
"TIMELINE_EVENT_SCHEMA",
|
|
"APPLICATION_REPORT_SCHEMA",
|
|
"PROVENANCE_SCHEMA",
|
|
]
|
|
|
|
def test_all_exported_schemas_exist_and_are_objects(self):
|
|
for name in self.EXPORTED:
|
|
with self.subTest(schema=name):
|
|
s = getattr(schema, name)
|
|
self.assertIsInstance(s, dict)
|
|
self.assertEqual(s.get("type"), "object")
|
|
self.assertIn("required", s)
|
|
self.assertIn("properties", s)
|
|
|
|
def test_application_report_forbids_provider_generation(self):
|
|
s = schema.APPLICATION_REPORT_SCHEMA
|
|
self.assertEqual(
|
|
s["properties"]["provider"].get("enum"), ["none"],
|
|
"application reports must only ever claim provider=none in this lane",
|
|
)
|
|
self.assertEqual(
|
|
s["properties"]["dry_run"].get("enum"), [True],
|
|
"application reports must never claim a live provider run",
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|