Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion test/rates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@

import trz_model as M # noqa: E402

SKILL_DIR = os.path.normpath(os.path.join(HERE, "..", "skills", "trz-expert"))
sys.path.insert(0, os.path.join(SKILL_DIR, "scripts"))
import rates as SR # noqa: E402

REFERENCES = os.path.normpath(os.path.join(
HERE, "..", "skills", "trz-expert", "references"))
RATES_FILE = os.path.join(REFERENCES, "stavki.md")
Expand Down Expand Up @@ -132,6 +136,14 @@ def extract(pattern):
("control sum of the employee contributions",
r"Лични вноски, трета категория \|\s*\*\*([\d.]+)%\*\*",
M.EMPLOYEE_TOTAL),
# scripts/rates.py's FLAT_PATTERNS["employer_no_tzpb_pct"] uses this exact pattern
# to feed audit.py's F5 (ТЗПБ). Kept here too, against the model's own sum of the
# three funds it stands for, so the value is cross-checked, not only "matched
# somewhere" - the new FLAT_PATTERNS loop below only proves the pattern matches
# once, never that the number is right.
("control sum of the employer contributions, excluding ТЗПБ",
r"без ТЗПБ, трета категория \|\s*\*\*([\d.]+)%\*\*",
M.r2(M.EMPLOYER_SOCIAL + M.EMPLOYER_UPF + M.EMPLOYER_HEALTH)),
("rounding allowance when summing the five contributions",
r"осигурителния доход с до \*\*([\d.]+)\*\*",
0.03),
Expand Down Expand Up @@ -261,8 +273,57 @@ def main():
f"and drop it from this list.")
failed.append(label)

print()
# scripts/rates.py is audit.py's own extraction layer - a second, independent set of
# patterns against the same reference text, not covered by CHECKS above. Only two of
# its ten FLAT_PATTERNS entries are read by audit.py today (employer_no_tzpb_pct,
# social_expense_threshold_eur); the rest are reserved for checks not wired up yet.
# Without this loop a stavki.md restructure could silently break one of those eight -
# load_flat() fails closed (the name is just absent from its result, per its own
# docstring), so audit.py would never crash, and no other suite exercises them.
#
# This loop only proves each pattern matches EXACTLY ONCE (load_flat()'s own
# contract) - it does not compare the extracted number against anything, so a
# pattern that matches the wrong row would still print `ok`. Where a CHECKS entry
# above happens to use the identical pattern text, that entry is the real value
# check; employer_no_tzpb_pct now has one for exactly this reason. A FLAT_PATTERNS
# name added later without a matching CHECKS entry is "exists" but not "correct".
flat = SR.load_flat(SKILL_DIR)
for name in SR.FLAT_PATTERNS:
label = f"scripts/rates.py FLAT_PATTERNS[{name!r}]"
if name in flat:
print(f" ok {label:58} {flat[name]}")
else:
print(f" NOT FOUND {label}")
print(" pattern matched zero or more than once in the current "
"reference tree - a check reading it would get None and refuse "
"silently, per rates.py's fail-closed contract")
failed.append(label)

# PERIOD_PATTERNS need a period known to exist. max_insurable genuinely has two
# different rows for 2026 (the CHECKS above cross-check both against
# trz_model.py); min_wage_month does not change mid-year, so both months here read
# the SAME row for it - still worth keeping, since a row that vanished entirely
# would still fail either way, but it is not a second regime the way max_insurable
# is. for_period() itself has no "matches more than once" guard the way extract()
# and load_flat() do (it returns the first match via re.finditer), so this loop
# cannot catch two conflicting rows for the same period the way the FLAT_PATTERNS
# loop above would - only that *a* row exists.
for name in SR.PERIOD_PATTERNS:
for year, month in ((2026, 1), (2026, 8)):
label = f"scripts/rates.py PERIOD_PATTERNS[{name!r}] for {year}-{month:02d}"
v = SR.for_period(SKILL_DIR, name, year, month)
if v is None:
print(f" NOT FOUND {label}")
print(" for_period() returned None for a period the skill "
"is meant to cover")
failed.append(label)
else:
print(f" ok {label:58} {v}")

print("=" * 78)
total = len(CHECKS) + len(PHRASES) + len(UNCONFIRMED)
total = len(CHECKS) + len(PHRASES) + len(UNCONFIRMED) + len(SR.FLAT_PATTERNS) + \
2 * len(SR.PERIOD_PATTERNS)
if failed:
print(f"FAILED: {len(failed)} of {total}")
print("A rate in the reference file has drifted from test/trz_model.py. Fix "
Expand Down