87 lines
3.1 KiB
Python
87 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Focused contracts for unresolved birth-time candidate profiles."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
SCRIPTS = os.path.join(os.path.dirname(__file__), "..", "scripts")
|
|
if SCRIPTS not in sys.path:
|
|
sys.path.insert(0, SCRIPTS)
|
|
|
|
from flexible_birth_time_profile import ( # noqa: E402
|
|
FlexibleBirthTimeProfileError,
|
|
build_flexible_birth_time_profile,
|
|
)
|
|
|
|
|
|
def _candidate(index: int, count: int) -> dict:
|
|
candidate_time = f"08:{index:02d}"
|
|
return {
|
|
"candidate_id": f"fictional-{index}",
|
|
"candidate_time": candidate_time,
|
|
"evidence": {
|
|
"D1.ascendant": "Aries",
|
|
"D10.ascendant": "Capricorn" if index < count - 1 else "Aquarius",
|
|
},
|
|
"trace": [{"kind": "fictional_candidate", "reference": f"fixture://{candidate_time}"}],
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize("count", [2, 15])
|
|
def test_profile_supports_two_to_fifteen_candidates_with_real_minute_variation(count: int) -> None:
|
|
candidates = [_candidate(index, count) for index in range(count)]
|
|
|
|
profile = build_flexible_birth_time_profile(
|
|
candidates,
|
|
source_reference={"review_id": "fictional-review"},
|
|
)
|
|
reversed_profile = build_flexible_birth_time_profile(
|
|
list(reversed(candidates)),
|
|
source_reference={"review_id": "fictional-review"},
|
|
)
|
|
|
|
assert profile == reversed_profile
|
|
assert profile["birth_time_window"]["candidate_count"] == count
|
|
assert profile["stable_evidence"] == {"D1.ascendant": "Aries"}
|
|
assert set(profile["sensitive_evidence"]) == {"D10.ascendant"}
|
|
for minute_values in profile["sensitive_evidence"].values():
|
|
assert len({json.dumps(value, sort_keys=True) for value in minute_values.values()}) > 1
|
|
|
|
|
|
@pytest.mark.parametrize("count", [1, 16])
|
|
def test_profile_rejects_candidate_counts_outside_two_to_fifteen(count: int) -> None:
|
|
with pytest.raises(FlexibleBirthTimeProfileError, match="candidate_count_must_be_two_to_fifteen"):
|
|
build_flexible_birth_time_profile(
|
|
[_candidate(index, max(count, 2)) for index in range(count)],
|
|
source_reference={"review_id": "fictional-review"},
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("status", ["approved", "confirmed"])
|
|
def test_profile_rejects_approved_or_confirmed_source_reference(status: str) -> None:
|
|
with pytest.raises(FlexibleBirthTimeProfileError, match="approved_or_confirmed_source_reference_forbidden"):
|
|
build_flexible_birth_time_profile(
|
|
[_candidate(0, 2), _candidate(1, 2)],
|
|
source_reference={"review_id": "fictional-review", "status": status},
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"field",
|
|
["approved_birth_time", "final_birth_time", "winner", "approval", "approval_authority"],
|
|
)
|
|
def test_profile_rejects_birth_minute_authority_fields(field: str) -> None:
|
|
candidates = [_candidate(0, 2), _candidate(1, 2)]
|
|
candidates[0]["evidence"][field] = "08:00"
|
|
|
|
with pytest.raises(FlexibleBirthTimeProfileError, match="candidate_window_authority_forbidden"):
|
|
build_flexible_birth_time_profile(
|
|
candidates,
|
|
source_reference={"review_id": "fictional-review"},
|
|
)
|