generated from coulomb/repo-seed
Replace the ad-hoc coordination-domain spine with the Repo Classification Standard: 14 market domains, classification columns on managed_repos, and workplans anchored by repo_id (topic_id optional). - Add Alembic migration d8e9f0a1b2c3 with data backfill and workstream→workplan rename - Add api/classification.py validation and register-from-classification tooling - Expose workplan-first REST/MCP surface with legacy workstream aliases - Add C-24 consistency rule and legacy domain frontmatter mapping - Update dashboard repos page with category/capability/stake filters - Update orientation docs; mark STATE-WP-0065 finished
67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
"""Tests for api.classification validation module (STATE-WP-0065 P2)."""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from api.classification import (
|
|
ClassificationData,
|
|
validate_classification,
|
|
)
|
|
|
|
|
|
def _valid_block(**overrides) -> dict:
|
|
base = {
|
|
"category": "tooling",
|
|
"domain": "infotech",
|
|
"secondary_domains": [],
|
|
"capability_tags": ["platform"],
|
|
"business_stake": ["technology", "operations"],
|
|
"business_mechanics": ["coordination"],
|
|
}
|
|
base.update(overrides)
|
|
return base
|
|
|
|
|
|
class TestValidateClassification:
|
|
def test_valid_block_passes(self):
|
|
errors, warnings = validate_classification(_valid_block())
|
|
assert errors == []
|
|
|
|
def test_missing_category_fails(self):
|
|
block = _valid_block()
|
|
del block["category"]
|
|
errors, _ = validate_classification(block)
|
|
assert any("category" in err for err in errors)
|
|
|
|
def test_invalid_category_fails(self):
|
|
errors, _ = validate_classification(_valid_block(category="not-a-category"))
|
|
assert any("category" in err for err in errors)
|
|
|
|
def test_invalid_domain_fails(self):
|
|
errors, _ = validate_classification(_valid_block(domain="not-a-domain"))
|
|
assert any("domain" in err for err in errors)
|
|
|
|
def test_unknown_capability_tag_warns(self):
|
|
_, warnings = validate_classification(_valid_block(capability_tags=["totally-made-up-tag"]))
|
|
assert any("capability_tag" in warn for warn in warnings)
|
|
|
|
def test_invalid_business_stake_fails(self):
|
|
errors, _ = validate_classification(_valid_block(business_stake=["not-a-stake"]))
|
|
assert any("business_stake" in err for err in errors)
|
|
|
|
def test_secondary_domain_repeats_primary_fails(self):
|
|
errors, _ = validate_classification(
|
|
_valid_block(domain="infotech", secondary_domains=["infotech"])
|
|
)
|
|
assert any("repeats the primary domain" in err for err in errors)
|
|
|
|
|
|
class TestClassificationData:
|
|
def test_round_trip_dict(self):
|
|
block = _valid_block(classified_at="2026-06-22", classified_by="human", version="1.0")
|
|
data = ClassificationData.from_block(block)
|
|
payload = data.to_dict()
|
|
assert payload["category"] == "tooling"
|
|
assert payload["domain"] == "infotech"
|
|
assert payload["classified_by"] == "human"
|
|
assert payload["standard_version"] == "1.0" |