Files

231 lines
7.6 KiB
Python
Raw Permalink Normal View History

import json
from datetime import UTC, date, datetime
from pathlib import Path
import pytest
from pydantic import ValidationError
from gewu.domain.contracts import (
AutoAcceptPolicy,
Confidence,
Locator,
NodeType,
ProjectRecord,
ProjectStatus,
ProposalReceipt,
ProposalState,
ReliabilityGrade,
Scope,
SearchRequest,
SourceRevisionRecord,
)
def test_project_contract_rejects_unknown_fields() -> None:
with pytest.raises(ValidationError):
ProjectRecord.model_validate(
{
"project_id": "dome",
"title": "Dome",
"status": ProjectStatus.ACTIVE,
"north_star": "Understand interactive environments",
"key_questions": ("What works?",),
"autonomy_policy_id": "pilot",
"untrusted_extra": "ignored?",
}
)
def test_project_status_supports_pause_without_losing_registry_record() -> None:
project = ProjectRecord(
project_id="dome",
title="Dome",
status=ProjectStatus.PAUSED,
north_star="Understand interactive environments",
key_questions=("What works?",),
autonomy_policy_id="pilot",
)
assert (
ProjectRecord.model_validate_json(project.model_dump_json()).status == ProjectStatus.PAUSED
)
@pytest.mark.parametrize("project_id", ["Dome", "two_words", "project.slug"])
def test_project_ids_must_be_lowercase_slugs(project_id: str) -> None:
with pytest.raises(ValidationError):
ProjectRecord(
project_id=project_id,
title="Invalid project ID",
status=ProjectStatus.ACTIVE,
north_star="A stable project lens.",
key_questions=("What matters?",),
autonomy_policy_id="policy",
)
def test_scope_requires_explicit_all_projects_flag() -> None:
with pytest.raises(ValidationError):
Scope(projects=("all",))
assert Scope(projects=("all",), all_projects=True)
@pytest.mark.parametrize("unsafe_id", ["../private", "bad/id", " leading-space"])
def test_scope_rejects_unsafe_project_ids(unsafe_id: str) -> None:
with pytest.raises(ValidationError):
Scope(projects=(unsafe_id,))
def test_search_facets_are_typed_and_reject_duplicate_node_types() -> None:
request = SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
node_types=(NodeType.PROBLEM, NodeType.TECHNIQUE),
tags=("attendance", "real-time"),
audience=("product",),
use_for=("comparison",),
domain=("production",),
confidences=(Confidence.MEDIUM,),
as_of_from=date(2024, 1, 1),
as_of_to=date(2024, 12, 31),
stale_only=True,
budget_tokens=1000,
)
assert request.stale_only
assert request.tags == ("attendance", "real-time")
assert request.domain == ("production",)
assert request.confidences == (Confidence.MEDIUM,)
assert request.as_of_from == date(2024, 1, 1)
with pytest.raises(ValidationError, match="must not be later"):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
as_of_from=date(2025, 1, 1),
as_of_to=date(2024, 12, 31),
budget_tokens=1000,
)
with pytest.raises(ValidationError, match="confidences must be unique"):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
confidences=(Confidence.MEDIUM, Confidence.MEDIUM),
budget_tokens=1000,
)
with pytest.raises(ValidationError):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
node_types=(NodeType.PROBLEM, NodeType.PROBLEM),
budget_tokens=1000,
)
with pytest.raises(ValidationError):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
tags=("attendance", "attendance"),
budget_tokens=1000,
)
with pytest.raises(ValidationError):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
domain=(" production",),
budget_tokens=1000,
)
def test_search_allows_empty_query_only_for_a_typed_browse() -> None:
request = SearchRequest(
scope=Scope(projects=("dome-performance",)),
node_types=(NodeType.PROBLEM,),
budget_tokens=1000,
)
assert request.query == ""
with pytest.raises(ValidationError, match="search requires text"):
SearchRequest(
scope=Scope(projects=("dome-performance",)),
budget_tokens=1000,
)
with pytest.raises(ValidationError, match="search requires text"):
SearchRequest(
query=" ",
scope=Scope(projects=("dome-performance",)),
budget_tokens=1000,
)
def test_pdf_locator_uses_one_based_page_and_nonempty_span() -> None:
with pytest.raises(ValidationError):
Locator(kind="pdf_page_span", block_id="p1-b1", start=8, end=8, page=0)
def test_unarchived_source_requires_a_url_and_cannot_claim_artifact_bytes() -> None:
base = {
"source_id": "project:bookmark",
"revision_id": "project:bookmark:r1",
"media_type": "application/octet-stream",
"byte_length": 0,
"captured_at": datetime.now(UTC),
"original_url": "https://example.org/paper",
"title": "Referenced paper",
"license": "unspecified",
"source_type": "paper",
"reliability_grade": ReliabilityGrade.MEDIUM,
"ingested_for": ("dome-performance",),
"archive_status": "reference-only",
}
record = SourceRevisionRecord.model_validate(base)
assert record.artifact_sha256 is None
with pytest.raises(ValidationError):
SourceRevisionRecord.model_validate({**base, "byte_length": 10})
with pytest.raises(ValidationError):
SourceRevisionRecord.model_validate({**base, "original_url": None})
def test_receipt_is_immutable_and_versioned() -> None:
receipt = ProposalReceipt(
proposal_id="proposal-1",
revision=1,
payload_digest="a" * 64,
state=ProposalState.ACCEPTED,
outcome="accepted",
commit_id="b" * 64,
decided_at=datetime.now(UTC),
)
assert receipt.schema_version == 1
with pytest.raises(ValidationError):
receipt.revision = 2 # type: ignore[misc]
def test_pilot_registry_fixture_matches_project_and_policy_contracts() -> None:
fixture_path = (
Path(__file__).resolve().parents[2] / "schema" / "fixtures" / "pilot-projects.json"
)
def reject_duplicate_keys(pairs: list[tuple[str, object]]) -> dict[str, object]:
result: dict[str, object] = {}
for key, value in pairs:
if key in result:
raise ValueError(f"duplicate JSON key: {key}")
result[key] = value
return result
fixture = json.loads(
fixture_path.read_text(encoding="utf-8"), object_pairs_hook=reject_duplicate_keys
)
projects = tuple(
ProjectRecord.model_validate_json(json.dumps(record), strict=True)
for record in fixture["projects"]
)
policies = tuple(
AutoAcceptPolicy.model_validate_json(json.dumps(record), strict=True)
for record in fixture["test_policies"]
)
assert {project.project_id for project in projects} == {
"dome-performance",
"interactive-video",
}
assert {policy.project_id for policy in policies} == {
"dome-performance",
"interactive-video",
"shared",
}