"""§22.2 registry parse + apply: validation, type-immutability, upsert.""" from __future__ import annotations import tempfile from pathlib import Path import pytest from app import db, registry from app.config import Config def _db(): cfg = Config( gitea_url="x", gitea_bot_user="x", gitea_bot_token="x", gitea_org="x", registry_repo="registry", oauth_client_id="x", oauth_client_secret="x", app_url="x", secret_key="x", database_path=Path(tempfile.mkdtemp(prefix="reg-")) / "t.db", owner_gitea_login="x", webhook_secret="x", ) db.run_migrations(cfg) if db._CONN is not None: db._CONN.close() db._CONN = None db.init(cfg) return cfg VALID = """ deployment: name: Open Human Model tagline: A model of human flourishing projects: - id: default name: Open Human Model type: document content_repo: meta visibility: public """ def test_parse_valid_registry(): doc = registry.parse_registry(VALID) assert doc.deployment_name == "Open Human Model" assert doc.deployment_tagline == "A model of human flourishing" assert len(doc.projects) == 1 p = doc.projects[0] assert (p.id, p.type, p.content_repo, p.visibility) == ("default", "document", "meta", "public") assert p.initial_state == "super-draft" def test_parse_initial_state_defaults_per_type(): doc = registry.parse_registry( "projects:\n - {id: a, name: A, type: bdd, content_repo: a}\n" ) assert doc.projects[0].initial_state == "active" # bdd default @pytest.mark.parametrize("bad,msg", [ ("projects: []\n", "at least one"), ("projects:\n - just-a-string\n", "must be a mapping"), ("projects:\n - {id: 'Bad Slug', name: A, type: document, content_repo: a}\n", "valid slug"), ("projects:\n - {id: a, name: A, type: nope, content_repo: a}\n", "invalid type"), ("projects:\n - {id: a, name: A, type: document}\n", "content_repo"), ("projects:\n - {id: a, name: A, type: document, content_repo: a, visibility: x}\n", "visibility"), ("projects:\n - {id: a, name: A, type: document, content_repo: a}\n - {id: a, name: B, type: document, content_repo: b}\n", "duplicate"), ]) def test_parse_rejects_invalid(bad, msg): with pytest.raises(registry.RegistryError) as e: registry.parse_registry(bad) assert msg in str(e.value) def test_apply_upserts_projects_and_deployment(): _db() doc = registry.parse_registry(VALID) registry.apply_registry(doc, registry_sha="regsha1", default_id="default") # §22 three-tier: the project carries the grouping-tier fields; the # per-corpus type/initial_state live on its default collection. prow = db.conn().execute( "SELECT name, content_repo, visibility, registry_sha FROM projects WHERE id='default'" ).fetchone() assert prow["name"] == "Open Human Model" assert prow["content_repo"] == "meta" assert prow["registry_sha"] == "regsha1" crow = db.conn().execute( "SELECT type, initial_state FROM collections WHERE id='default'" ).fetchone() assert crow["type"] == "document" assert crow["initial_state"] == "super-draft" drow = db.conn().execute("SELECT name, tagline FROM deployment WHERE id=1").fetchone() assert drow["name"] == "Open Human Model" assert drow["tagline"] == "A model of human flourishing" def test_apply_rejects_type_change_on_existing_project(): _db() registry.apply_registry(registry.parse_registry(VALID), "s1", default_id="default") changed = VALID.replace("type: document", "type: specification") registry.apply_registry(registry.parse_registry(changed), "s2", default_id="default") # skipped # §22.4a immutable type — now enforced on the collection. t = db.conn().execute("SELECT type FROM collections WHERE id='default'").fetchone()["type"] assert t == "document" # immutable — unchanged # The deployment row IS still advanced even though the type change was skipped. drow = db.conn().execute("SELECT registry_sha FROM deployment WHERE id=1").fetchone() assert drow["registry_sha"] == "s2"