Files
spencermandClaude Fable 5.1 37d9a0cb50 Phase 2: core domain, policy engine, packet API, Clerical screen
Migration 0003: projects, aliases, renumbers, units, packets and every child table,
change orders, assets, documents, ITPs, station limits, department weld flag.
Policy package: Principal, PacketState, Action rules, custody, preconditions,
CO hold, order of checks, can/blocked/holder on reads. docs/POLICY.md with the
tightenings table for Seth. Write pipeline: FOR UPDATE, CAS on status and dept,
If-Match row versions, server-assigned numbers, field CAS, version bump, packet
log, audit, NOTIFY, signatures, IT reasons, filed lock and HMAC seal.
API: /api/packets (header, transitions, work orders, pages, docs, move, remove,
BOM, shorts, welds, heat tags, heat table, stage, checklist, clock, warehouse
stage, Q.C., change orders, log, seal), /api/projects, /api/units, /api/assets,
/_blob. Frontend: Clerical home, projects and units, packet page with pdf.js
and QR codes, hand draft, heat map, send to warehouse, department or Q.C.
Tests: policy matrix, API, concurrency, schema, projects; Playwright Clerical
draft scenario. Verified on docker-1 with the CI stack and the Samba DC.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-16 09:31:45 -06:00

37 lines
1.3 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""clean_text and the input limits."""
from __future__ import annotations
from packetstation.text import RX_HEAT, RX_LOT, RX_PROJECT, RX_STAMP, clean_deep, clean_opt, clean_text, reason_ok
def test_clean_text_strips_controls_and_invisible_marks() -> None:
assert clean_text(" a\u0000b​c‮d⁦e\u007f ") == "abcde"
assert clean_text("tab\tkept\nand\r\nnewlines") == "tab\tkept\nand\r\nnewlines"
assert clean_text(None) == ""
assert clean_text(42) == "42"
def test_clean_text_normalises_and_cuts() -> None:
assert clean_text("é") == "é" # NFC
assert clean_text("x" * 50, 10) == "x" * 10
def test_clean_opt_and_reason() -> None:
assert clean_opt(" ", 10) is None
assert clean_opt(" a ", 10) == "a"
assert reason_ok("short") is None
assert reason_ok(" ticket INC-1234 ") == "ticket INC-1234"
def test_clean_deep() -> None:
out = clean_deep({"a": [" x\u0000 ", {"b": "​y"}], "n": 3})
assert out == {"a": ["x", {"b": "y"}], "n": 3}
def test_regexes() -> None:
assert RX_HEAT.match("AB12 3/4-#7") and not RX_HEAT.match("-x") and not RX_HEAT.match("a" * 41)
assert RX_STAMP.match("W-12") and not RX_STAMP.match("W 12")
assert RX_PROJECT.match("L670") and not RX_PROJECT.match("-L")
assert RX_LOT.match("7A") and RX_LOT.match("001") and not RX_LOT.match("0001")