test(fulfillment): consistency + intake-completeness checker
Cross-references every DOT/state/hazmat slug across COMPLIANCE_SERVICES, REQUIRED_FIELDS, SERVICE_META, INTAKE_MANIFEST, and SERVICE_HANDLERS, and verifies every required field is collectible by its assigned intake steps. Caught + fixed missing usdot-reactivation SERVICE_META entry. 24/24 pass.
This commit is contained in:
parent
9c6b8d95e0
commit
cadff79bd6
2 changed files with 202 additions and 14 deletions
179
scripts/tests/check_fulfillment_consistency.py
Normal file
179
scripts/tests/check_fulfillment_consistency.py
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Compliance fulfillment consistency + intake-completeness checker.
|
||||
|
||||
For every DOT/FMCSA + state-trucking + hazmat/emissions service slug this asserts:
|
||||
1. It exists in COMPLIANCE_SERVICES (checkout product/pricing)
|
||||
2. It has a REQUIRED_FIELDS spec (intake validation)
|
||||
3. It has a SERVICE_META entry (intake manifest pricing/name)
|
||||
4. It maps to intake steps in INTAKE_MANIFEST
|
||||
5. It resolves to a handler in SERVICE_HANDLERS (fulfillment)
|
||||
6. Every REQUIRED_FIELDS[slug].required field is actually collectible by the
|
||||
intake step(s) the manifest assigns to that slug (the core "we collect all
|
||||
the info we need" guarantee).
|
||||
|
||||
Run: python3 scripts/tests/check_fulfillment_consistency.py
|
||||
Exit 0 = all good; nonzero = gaps found (printed).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
ORDERS_TS = os.path.join(ROOT, "api/src/routes/compliance-orders.ts")
|
||||
MANIFEST_TS = os.path.join(ROOT, "site/src/lib/intake_manifest.ts")
|
||||
|
||||
# ── The set of slugs this checker is responsible for (DOT / state / hazmat). ──
|
||||
TRUCKING_SLUGS = [
|
||||
# federal DOT/FMCSA
|
||||
"mcs150-update", "ucr-registration", "boc3-filing", "dot-registration",
|
||||
"mc-authority", "dot-drug-alcohol", "dot-audit-prep", "dot-full-compliance",
|
||||
"usdot-reactivation",
|
||||
# state-level trucking
|
||||
"irp-registration", "ifta-application", "ifta-quarterly", "or-weight-mile-tax",
|
||||
"ny-hut-registration", "ky-kyu-registration", "nm-weight-distance",
|
||||
"ct-highway-use-fee", "ca-mcp-carb", "state-dot-registration",
|
||||
"intrastate-authority", "osow-permit", "state-trucking-bundle",
|
||||
# hazmat / emissions
|
||||
"hazmat-phmsa", "state-emissions",
|
||||
]
|
||||
|
||||
# Fields the StateTruckingIntakeStep + DOTIntakeStep emit into intake_data.
|
||||
# These mirror the front-end PW.set() calls; if you add a field to a step, add it
|
||||
# here. The checker fails if a REQUIRED_FIELDS entry references a field that no
|
||||
# assigned step can produce.
|
||||
STATE_TRUCKING_FIELDS = {
|
||||
"legal_name", "entity_name", "dot_number", "mc_number", "email",
|
||||
"base_state", "power_units", "fuel_type", "gross_weight_bracket",
|
||||
"operating_states", "ca_number", "engine_model_years", "authority_type",
|
||||
"boc3_on_file", "insurance_carrier", "insurance_policy", "load_dimensions",
|
||||
"load_weight", "hazmat_classes", "bulk_packaging", "small_business",
|
||||
}
|
||||
DOT_INTAKE_FIELDS = {
|
||||
"dot_number", "mc_number", "legal_name", "ein", "address_street",
|
||||
"address_city", "address_state", "address_zip", "phone", "email",
|
||||
"signer_name", "signer_title", "power_units", "drivers", "cdl_drivers",
|
||||
"carrier_operation", "interstate_intrastate", "hazmat", "annual_miles",
|
||||
"cargo_types", "fleet_size_bracket", "owner_operators", "der_name",
|
||||
"current_da_provider", "docket_type", "docket_number", "entity_type",
|
||||
"reporting_quarter",
|
||||
}
|
||||
STEP_FIELDS = {
|
||||
"state-trucking": STATE_TRUCKING_FIELDS,
|
||||
"dot-intake": DOT_INTAKE_FIELDS,
|
||||
# mcs150 step reuses the DOT intake field universe
|
||||
"mcs150": DOT_INTAKE_FIELDS,
|
||||
}
|
||||
|
||||
|
||||
def _extract_block(text: str, decl: str) -> str:
|
||||
"""Return the text of the object literal assigned in `<decl> ... = { ... }`."""
|
||||
m = re.search(re.escape(decl), text)
|
||||
if not m:
|
||||
raise SystemExit(f"could not find `{decl}` in source")
|
||||
# Find the `=` that begins the assignment (skip past any `: Record<...>` type),
|
||||
# then the first `{` after it is the object literal.
|
||||
eq = text.find("=", m.end())
|
||||
if eq == -1:
|
||||
raise SystemExit(f"no `=` after {decl}")
|
||||
i = text.find("{", eq)
|
||||
depth = 0
|
||||
for j in range(i, len(text)):
|
||||
c = text[j]
|
||||
if c == "{":
|
||||
depth += 1
|
||||
elif c == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
return text[i : j + 1]
|
||||
raise SystemExit(f"unbalanced braces after {decl}")
|
||||
|
||||
|
||||
def _top_level_keys(block: str) -> set[str]:
|
||||
"""Keys quoted at brace-depth 1 inside an object literal."""
|
||||
keys, depth = set(), 0
|
||||
for mm in re.finditer(r'[{}]|"([^"]+)"\s*:', block):
|
||||
tok = mm.group(0)
|
||||
if tok == "{":
|
||||
depth += 1
|
||||
elif tok == "}":
|
||||
depth -= 1
|
||||
elif depth == 1 and mm.group(1) is not None:
|
||||
keys.add(mm.group(1))
|
||||
return keys
|
||||
|
||||
|
||||
def main() -> int:
|
||||
orders = open(ORDERS_TS).read()
|
||||
manifest = open(MANIFEST_TS).read()
|
||||
|
||||
services = _top_level_keys(_extract_block(orders, "const COMPLIANCE_SERVICES"))
|
||||
req_block = _extract_block(orders, "const REQUIRED_FIELDS")
|
||||
required_slugs = _top_level_keys(req_block)
|
||||
meta = _top_level_keys(_extract_block(manifest, "SERVICE_META"))
|
||||
manifest_map_block = _extract_block(manifest, "INTAKE_MANIFEST")
|
||||
manifest_slugs = _top_level_keys(manifest_map_block)
|
||||
|
||||
# Handler registry (import the Python source statically — avoid importing the
|
||||
# module which pulls DB deps; parse the dict keys via regex instead).
|
||||
init_py = open(os.path.join(ROOT, "scripts/workers/services/__init__.py")).read()
|
||||
handler_block = init_py[init_py.find("SERVICE_HANDLERS"):]
|
||||
handler_block = handler_block[: handler_block.find("\n}\n") + 2]
|
||||
handler_slugs = set(re.findall(r'"([^"]+)"\s*:', handler_block))
|
||||
|
||||
# Per-slug required-fields → which steps the manifest assigns.
|
||||
# Parse INTAKE_MANIFEST[slug] = [ "...", ... ]
|
||||
manifest_steps: dict[str, list[str]] = {}
|
||||
for mm in re.finditer(r'"([^"]+)"\s*:\s*\[([^\]]*)\]', manifest_map_block):
|
||||
slug = mm.group(1)
|
||||
steps = re.findall(r'"([^"]+)"', mm.group(2))
|
||||
manifest_steps[slug] = steps
|
||||
|
||||
# Per-slug required fields list.
|
||||
required_fields: dict[str, list[str]] = {}
|
||||
for mm in re.finditer(r'"([^"]+)"\s*:\s*\{\s*required\s*:\s*\[([^\]]*)\]', req_block):
|
||||
required_fields[mm.group(1)] = re.findall(r'"([^"]+)"', mm.group(2))
|
||||
|
||||
errors: list[str] = []
|
||||
for slug in TRUCKING_SLUGS:
|
||||
if slug not in services:
|
||||
errors.append(f"[{slug}] missing from COMPLIANCE_SERVICES")
|
||||
if slug not in required_slugs:
|
||||
errors.append(f"[{slug}] missing from REQUIRED_FIELDS")
|
||||
if slug not in meta:
|
||||
errors.append(f"[{slug}] missing from SERVICE_META")
|
||||
if slug not in manifest_slugs:
|
||||
errors.append(f"[{slug}] missing from INTAKE_MANIFEST")
|
||||
if slug not in handler_slugs:
|
||||
errors.append(f"[{slug}] missing from SERVICE_HANDLERS")
|
||||
|
||||
# Intake-completeness: every required field must be collectible.
|
||||
steps = manifest_steps.get(slug, [])
|
||||
collectible: set[str] = set()
|
||||
for st in steps:
|
||||
collectible |= STEP_FIELDS.get(st, set())
|
||||
for field in required_fields.get(slug, []):
|
||||
base = field.split(".")[0]
|
||||
if base not in collectible:
|
||||
errors.append(
|
||||
f"[{slug}] required field '{field}' is NOT collectible by "
|
||||
f"assigned steps {steps}"
|
||||
)
|
||||
|
||||
if errors:
|
||||
print("FULFILLMENT CONSISTENCY: FAIL")
|
||||
for e in errors:
|
||||
print(" -", e)
|
||||
return 1
|
||||
|
||||
print(f"FULFILLMENT CONSISTENCY: OK ({len(TRUCKING_SLUGS)} slugs verified)")
|
||||
print(" All slugs present in: COMPLIANCE_SERVICES, REQUIRED_FIELDS,")
|
||||
print(" SERVICE_META, INTAKE_MANIFEST, SERVICE_HANDLERS.")
|
||||
print(" All required fields are collectible by their assigned intake steps.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
|
@ -37,6 +37,7 @@ export type IntakeStep =
|
|||
| "ocn"
|
||||
| "mcs150" // MCS-150 biennial update form fields (standalone)
|
||||
| "dot-intake" // Unified DOT intake — shows sections based on services ordered
|
||||
| "state-trucking" // State-level trucking + hazmat/emissions intake (slug-gated sections)
|
||||
| "review"
|
||||
| "payment";
|
||||
|
||||
|
|
@ -121,20 +122,24 @@ export const INTAKE_MANIFEST: Record<string, IntakeStep[]> = {
|
|||
"entity-dissolution": ["dot-intake", "review"],
|
||||
|
||||
// ── State-Level Trucking Compliance ─────────────────────────────────
|
||||
// Admin-assisted: info collected at checkout, no intake form needed.
|
||||
"irp-registration": ["review"],
|
||||
"ifta-application": ["review"],
|
||||
"ifta-quarterly": ["review"],
|
||||
"or-weight-mile-tax": ["review"],
|
||||
"ny-hut-registration": ["review"],
|
||||
"ky-kyu-registration": ["review"],
|
||||
"nm-weight-distance": ["review"],
|
||||
"ct-highway-use-fee": ["review"],
|
||||
"ca-mcp-carb": ["review"],
|
||||
"state-dot-registration":["review"],
|
||||
"intrastate-authority": ["review"],
|
||||
"osow-permit": ["review"],
|
||||
"state-trucking-bundle": ["review"],
|
||||
// Collected via the dedicated state-trucking intake step.
|
||||
"irp-registration": ["state-trucking", "review"],
|
||||
"ifta-application": ["state-trucking", "review"],
|
||||
"ifta-quarterly": ["state-trucking", "review"],
|
||||
"or-weight-mile-tax": ["state-trucking", "review"],
|
||||
"ny-hut-registration": ["state-trucking", "review"],
|
||||
"ky-kyu-registration": ["state-trucking", "review"],
|
||||
"nm-weight-distance": ["state-trucking", "review"],
|
||||
"ct-highway-use-fee": ["state-trucking", "review"],
|
||||
"ca-mcp-carb": ["state-trucking", "review"],
|
||||
"state-dot-registration":["state-trucking", "review"],
|
||||
"intrastate-authority": ["state-trucking", "review"],
|
||||
"osow-permit": ["state-trucking", "review"],
|
||||
"state-trucking-bundle": ["state-trucking", "review"],
|
||||
|
||||
// ── Hazmat / Emissions ───────────────────────────────────────────────
|
||||
"hazmat-phmsa": ["state-trucking", "review"],
|
||||
"state-emissions": ["state-trucking", "review"],
|
||||
|
||||
// ── Entity / Corporate Upgrade ─────────────────────────────────────
|
||||
"entity-upgrade-bundle": ["dot-intake", "review"],
|
||||
|
|
@ -181,6 +186,7 @@ export const SERVICE_META: Record<string, { name: string; price_cents: number }>
|
|||
"dot-drug-alcohol": { name: "DOT Drug & Alcohol Compliance Program", price_cents: 14900 },
|
||||
"dot-audit-prep": { name: "New Entrant Safety Audit Preparation", price_cents: 39900 },
|
||||
"dot-full-compliance": { name: "DOT Full Compliance Bundle", price_cents: 39900 },
|
||||
"usdot-reactivation": { name: "USDOT Reactivation", price_cents: 14900 },
|
||||
// State-level trucking
|
||||
"irp-registration": { name: "IRP Registration Assistance", price_cents: 19900 },
|
||||
"ifta-application": { name: "IFTA Application + Decals", price_cents: 14900 },
|
||||
|
|
@ -195,6 +201,9 @@ export const SERVICE_META: Record<string, { name: string; price_cents: number }>
|
|||
"intrastate-authority": { name: "Intrastate Operating Authority", price_cents: 24900 },
|
||||
"osow-permit": { name: "Oversize/Overweight Permit", price_cents: 9900 },
|
||||
"state-trucking-bundle": { name: "State Compliance Bundle", price_cents: 49900 },
|
||||
// Hazmat / emissions
|
||||
"hazmat-phmsa": { name: "PHMSA Hazmat Registration", price_cents: 14900 },
|
||||
"state-emissions": { name: "State Clean-Truck / Emissions Compliance", price_cents: 19900 },
|
||||
};
|
||||
|
||||
export function formatUSD(cents: number): string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue