import json import sys import unittest from copy import deepcopy from decimal import Decimal from pathlib import Path ROOT = Path(__file__).parents[1] sys.path.insert(0, str(ROOT / "tools")) from entities import ( REQUIRED_ENTITY_IDS, V0_1_PARAMETERS, association_ok, load_register, load_terms, require_entity, validate_register, validate_terms, ) from portfolio import validate_record class EntityRegisterTest(unittest.TestCase): def test_register_contains_exactly_the_six_entities(self): _, entities = load_register(ROOT) self.assertEqual(set(REQUIRED_ENTITY_IDS), set(entities)) self.assertEqual("provider", entities["entity:railiance"]["role"]) self.assertIsNone(entities["entity:railiance"]["credit_limit_eur"]) def test_unknown_entity_id_is_rejected(self): with self.assertRaisesRegex(ValueError, "unknown entity id"): require_entity("entity:unknown") def test_terms_v0_1_match_published_parameters(self): terms = load_terms(ROOT) self.assertEqual(Decimal("0.20"), terms["markup_rate"]) self.assertEqual(Decimal("0.00"), terms["railiance_self_markup_rate"]) self.assertEqual(10, terms["payment_term_days"]) self.assertEqual(Decimal("0.05"), terms["interest_rate_per_year"]) self.assertEqual(Decimal("1000.00"), terms["default_credit_limit_eur"]) self.assertEqual(Decimal("50.00"), terms["restricted_monthly_consumption_eur"]) self.assertEqual("entity:railiance", terms["procuring_entity_id"]) self.assertEqual(V0_1_PARAMETERS, {key: terms[key] for key in V0_1_PARAMETERS}) def test_wrong_markup_fails_closed(self): payload = json.loads((ROOT / "data/terms/procurement-v0.1.json").read_text()) payload["markup_rate"] = "0.25" with self.assertRaisesRegex(ValueError, "markup_rate"): validate_terms(payload) def test_extra_entity_fails_closed(self): payload = json.loads((ROOT / "data/entities/register.json").read_text()) payload["entities"].append({ "id": "entity:other", "display_name": "Other", "role": "consumer", "credit_limit_eur": "1000.00", "account_ref": None, "status": "active", }) with self.assertRaisesRegex(ValueError, "unknown ids"): validate_register(payload) class AssociationTest(unittest.TestCase): def test_untagged_record_is_rejected(self): with self.assertRaisesRegex(ValueError, "untagged record"): association_ok({"procuring_entity_id": "entity:railiance"}) def test_explicit_gap_is_accepted(self): association_ok({ "financial_entity_id": None, "procuring_entity_id": "entity:railiance", "entity_gap": "no allocation driver", }) def test_each_registered_entity_is_accepted(self): for entity_id in REQUIRED_ENTITY_IDS: association_ok({ "financial_entity_id": entity_id, "procuring_entity_id": "entity:railiance", "entity_gap": None, }) def test_inventory_records_are_associated(self): for path in (ROOT / "data/resources").glob("*.json"): record = json.loads(path.read_text()) with self.subTest(path=path): validate_record(record) def test_missing_entity_on_a_copy_is_rejected(self): record = deepcopy(json.loads(next((ROOT / "data/resources").glob("*.json")).read_text())) record["financial_entity_id"] = None record["entity_gap"] = None with self.assertRaisesRegex(ValueError, "untagged record"): validate_record(record)