| [8aa20c7] | 1 | #!/usr/bin/env python3
|
|---|
| 2 |
|
|---|
| 3 | # tests/test_flexoid.py
|
|---|
| 4 | """
|
|---|
| 5 | Test suite for id_factory.FlexOID.
|
|---|
| 6 |
|
|---|
| 7 | I verify that FlexOID behaves deterministically, validates itself strictly,
|
|---|
| 8 | and evolves correctly through version and state transitions.
|
|---|
| 9 | """
|
|---|
| 10 |
|
|---|
| 11 | import re
|
|---|
| 12 | from datetime import datetime, date
|
|---|
| 13 | import pytest
|
|---|
| 14 | from logging import Logger
|
|---|
| [3389960] | 15 | from flexoentity import FlexOID, canonical_seed, Domain
|
|---|
| [8aa20c7] | 16 |
|
|---|
| 17 |
|
|---|
| 18 | logger = Logger(__file__)
|
|---|
| 19 |
|
|---|
| 20 | # ──────────────────────────────────────────────
|
|---|
| 21 | # Basic construction and validation
|
|---|
| 22 | # ──────────────────────────────────────────────
|
|---|
| 23 |
|
|---|
| 24 | def test_valid_flexoid_parsing():
|
|---|
| 25 | fid = FlexOID("GEN-I251101-ABCDEF123456@001D")
|
|---|
| 26 | assert isinstance(fid, str)
|
|---|
| [6ad031b] | 27 | assert fid.domain_id == "GEN"
|
|---|
| [8aa20c7] | 28 | assert fid.entity_type == "I"
|
|---|
| 29 | assert fid.date_str == "251101"
|
|---|
| 30 | assert fid.version == 1
|
|---|
| 31 | assert fid.state_code == "D"
|
|---|
| 32 | assert fid.prefix.endswith("ABCDEF123456")
|
|---|
| 33 | assert str(fid).endswith("@001D")
|
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 | def test_invalid_flexoid_raises():
|
|---|
| 37 | with pytest.raises(ValueError):
|
|---|
| 38 | FlexOID("INVALIDFORMAT")
|
|---|
| 39 | with pytest.raises(ValueError):
|
|---|
| 40 | FlexOID("GEN-I251101-ABCDEF@1D") # bad version width
|
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 | def test_regex_is_strict():
|
|---|
| 44 | pat = FlexOID.OID_PATTERN
|
|---|
| 45 | assert re.match(pat, "GEN-I251101-123456ABCDEF@001A")
|
|---|
| 46 | assert not re.match(pat, "gen-item251101-123456@001A") # lowercase not allowed
|
|---|
| 47 | assert not re.match(pat, "GEN-I251101-123456@01A") # wrong digit count
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 | # ──────────────────────────────────────────────
|
|---|
| 51 | # Generation and deterministic hashing
|
|---|
| 52 | # ──────────────────────────────────────────────
|
|---|
| [3389960] | 53 | def test_generate_is_not_deterministic(fixed_datetime):
|
|---|
| [8aa20c7] | 54 | fid1 = FlexOID.generate("GEN", "I", "D", "test content")
|
|---|
| 55 | fid2 = FlexOID.generate("GEN", "I", "D", "test content")
|
|---|
| 56 |
|
|---|
| [3389960] | 57 | assert fid1 != fid2
|
|---|
| 58 | assert fid1.domain_id == fid2.domain_id == "GEN"
|
|---|
| 59 | assert fid1.entity_type == fid2.entity_type == "I"
|
|---|
| 60 | assert fid1.version == fid2.version == 1
|
|---|
| 61 | assert fid1.state_code == fid2.state_code == "D"
|
|---|
| 62 |
|
|---|
| 63 | def test_fingerprint_is_stable():
|
|---|
| 64 | d1 = Domain.with_domain_id("GEN", fullname="A", description="B")
|
|---|
| 65 | d2 = Domain.from_dict(d1.to_dict())
|
|---|
| 66 |
|
|---|
| 67 | assert d1.fingerprint == d2.fingerprint
|
|---|
| [8aa20c7] | 68 |
|
|---|
| 69 | def test_safe_generate_collision(monkeypatch):
|
|---|
| [3389960] | 70 | first = FlexOID.generate("GEN", "I", "D", "abc")
|
|---|
| 71 |
|
|---|
| 72 | class DummyRepo:
|
|---|
| 73 | def get(self, key):
|
|---|
| 74 | if key == str(first):
|
|---|
| 75 | return True
|
|---|
| 76 | return None
|
|---|
| [8aa20c7] | 77 |
|
|---|
| 78 | repo = DummyRepo()
|
|---|
| [3389960] | 79 |
|
|---|
| [8aa20c7] | 80 | fid = FlexOID.safe_generate("GEN", "I", "D", "abc", repo=repo)
|
|---|
| [3389960] | 81 |
|
|---|
| [8aa20c7] | 82 | assert isinstance(fid, FlexOID)
|
|---|
| [3389960] | 83 | assert fid != first
|
|---|
| [8aa20c7] | 84 | assert fid.state_code == "D"
|
|---|
| 85 |
|
|---|
| 86 | # ──────────────────────────────────────────────
|
|---|
| 87 | # State and version transitions
|
|---|
| 88 | # ──────────────────────────────────────────────
|
|---|
| 89 |
|
|---|
| 90 | def test_with_state_creates_new_instance():
|
|---|
| 91 | fid = FlexOID("GEN-I251101-ABCDEF123456@001D")
|
|---|
| 92 | fid2 = fid.with_state("A")
|
|---|
| 93 | assert fid != fid2
|
|---|
| 94 | assert fid.version == fid2.version
|
|---|
| 95 | assert fid2.state_code == "A"
|
|---|
| 96 | assert str(fid2).endswith("@001A")
|
|---|
| 97 |
|
|---|
| 98 |
|
|---|
| 99 | def test_next_version_increments_properly():
|
|---|
| 100 | fid = FlexOID("GEN-I251101-ABCDEF123456@001A")
|
|---|
| 101 | fid2 = FlexOID.next_version(fid)
|
|---|
| 102 | assert fid2.version == 2
|
|---|
| 103 | assert fid2.state_code == fid.state_code
|
|---|
| 104 | assert fid.prefix == fid2.prefix
|
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 | def test_from_oid_and_version_sets_exact_version():
|
|---|
| 108 | fid = FlexOID("GEN-I251101-ABCDEF123456@001A")
|
|---|
| 109 | fid2 = FlexOID.from_oid_and_version(fid, 10)
|
|---|
| 110 | assert fid2.version == 10
|
|---|
| 111 | assert fid2.state_code == "A"
|
|---|
| 112 |
|
|---|
| 113 |
|
|---|
| 114 | def test_clone_new_base_starts_at_one(fixed_datetime):
|
|---|
| 115 | fid = FlexOID.clone_new_base("GEN", "I", "D", "clone text")
|
|---|
| 116 | assert fid.version == 1
|
|---|
| 117 | assert fid.state_code == "D"
|
|---|
| 118 |
|
|---|
| 119 |
|
|---|
| 120 | # ──────────────────────────────────────────────
|
|---|
| 121 | # Canonical seed behavior
|
|---|
| 122 | # ──────────────────────────────────────────────
|
|---|
| 123 |
|
|---|
| [ef964d8] | 124 | def test_canonical_seed_for_strings():
|
|---|
| [8aa20c7] | 125 | s1 = " Hello world "
|
|---|
| 126 | s2 = "Hello world"
|
|---|
| 127 | assert canonical_seed(s1) == canonical_seed(s2)
|
|---|
| 128 |
|
|---|
| 129 | # ──────────────────────────────────────────────
|
|---|
| 130 | # Parsed structure and representations
|
|---|
| 131 | # ──────────────────────────────────────────────
|
|---|
| 132 |
|
|---|
| 133 | def test_parsed_returns_expected_dict():
|
|---|
| 134 | fid = FlexOID("GEN-I251101-ABCDEF123456@007S")
|
|---|
| [fd1913f] | 135 | data = fid.to_dict()
|
|---|
| [6ad031b] | 136 | assert data["domain_id"] == "GEN"
|
|---|
| [8aa20c7] | 137 | assert data["entity_type"] == "I"
|
|---|
| 138 | assert data["version"] == 7
|
|---|
| 139 | assert data["state"] == "S"
|
|---|
| 140 | assert isinstance(data["date"], date)
|
|---|
| 141 |
|
|---|
| 142 |
|
|---|
| 143 | def test_repr_is_readable():
|
|---|
| 144 | fid = FlexOID("GEN-I251101-ABCDEF123456@001D")
|
|---|
| 145 | s = repr(fid)
|
|---|
| 146 | assert "FlexOID" in s
|
|---|
| 147 | assert "GEN-I" in s
|
|---|