Add UK-postcode format validity to the Postcode value object 🟩

A pure, offline regex check (no network) so a frozen value object stays pure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-06-29 15:24:18 +00:00
parent fa6357a5cf
commit 23dfb3f899
2 changed files with 26 additions and 0 deletions

View file

@ -1,7 +1,13 @@
from __future__ import annotations
import re
from dataclasses import dataclass
# UK postcode format, matched against the canonical (no-space, upper) value.
# A pure structural check — it does not confirm the postcode actually exists
# (that would need a network lookup, which a value object must not do).
_UK_POSTCODE_RE = re.compile(r"[A-Z]{1,2}\d[A-Z\d]?\d[A-Z]{2}")
@dataclass(frozen=True)
class Postcode:
@ -13,3 +19,8 @@ class Postcode:
def __str__(self) -> str:
return self.value
def is_valid(self) -> bool:
"""Whether the canonical value is a well-formed UK postcode (format
only no existence check, so it is pure and offline)."""
return _UK_POSTCODE_RE.fullmatch(self.value) is not None

View file

@ -57,3 +57,18 @@ def test_postcode_is_frozen() -> None:
# act / assert
with pytest.raises(dataclasses.FrozenInstanceError):
postcode.value = "OTHER" # type: ignore[misc]
@pytest.mark.parametrize(
"raw",
["sw1a 1aa", "M1 1AA", "b33 8th", "cr2 6xh", "dn55 1pt", "ec1a 1bb", "w1a 0ax"],
)
def test_valid_uk_postcode_formats_are_valid(raw: str) -> None:
# act / assert — a pure format check on the canonical value, no network.
assert Postcode(raw).is_valid() is True
@pytest.mark.parametrize("raw", ["", " ", "NONSENSE", "12345", "SW1A 1A", "ZZ"])
def test_malformed_or_empty_postcodes_are_invalid(raw: str) -> None:
# act / assert
assert Postcode(raw).is_valid() is False