Compliance — Acceptance Test Cases
This page is the manual QA pack for the EPA Part 84 compliance feature in TuffOps. Test cases are grouped by surface (catalog, equipment, certs, events, repair, override, chronic, export). Each case has stable IDs so external tools can link to them.
Run every test in a tenant where:
- Compliance is enabled (
Setting::complianceEnabled()returns true). - The Part 84 effective date has passed.
- At least one in-scope refrigerant (e.g. R-410A) is classified as AIM regulated substance.
- At least one device model uses that refrigerant.
- At least one customer with one in-scope unit (full charge ≥ 15 lb) exists.
Refrigerant catalog
TC-COMP-001 — Classify a substitute by GWP
Goal: Verify that setting GWP and basis classifies a substitute under §84.106(a)(2).
Steps:
- Sign in as a user with
compliance.manage_refrigerant_catalog. - Open Refrigerant Types, select New.
- Set Common name to "R-454B", ASHRAE designation to "R-454B".
- Leave both classification flags unticked.
- Set GWP to
466and GWP basis toar5. - Save.
Expected: The new row appears in the index, GWP shown as 466, basis as ar5. The Part 84 remediation queue does not list this row.
TC-COMP-002 — Mutually exclusive AIM and ODS flags
Goal: Verify that AIM and ODS-only flags cannot both be set.
Steps:
- Open an existing refrigerant in edit mode.
- Tick AIM regulated substance.
- Try to tick ODS-only carve-out.
Expected: The ODS checkbox auto-disables when AIM is checked (and vice versa). Submitting with both ticked is rejected server-side with a validation error.
TC-COMP-003 — Delete blocked when in use
Goal: Verify that a refrigerant in use cannot be deleted.
Steps:
- Pick a refrigerant linked to a device model, unit, item, or charge event.
- Try to delete it from the index.
Expected: Delete returns an error explaining the refrigerant is in use. Untick Active instead — that succeeds and the refrigerant disappears from new pickers.
Recovery equipment
TC-COMP-010 — Create a recovery cylinder with hydro test schedule
Goal: Verify cylinder creation, including the DOT hydro retest schedule.
Steps:
- Sign in with
compliance.manage_recovery_equipment. - Open Recovery Equipment, select New.
- Make: "Appion", Model: "G5 Twin", Serial: "AP-2026-001".
- Cert standard: AHRI 740, Cert number: "AHRI-12345", Date issued: today.
- Last hydrostatic test: 1 year ago. Hydro retest due: 4 years from today.
- Save.
Expected: Cylinder appears in the index. Cert and hydro fields show in the row. No warning pill yet (hydro 4 years out).
TC-COMP-011 — Hydro retest warning pill
Goal: Verify the daily compliance scan flags an upcoming hydro retest.
Steps:
- Edit the cylinder from TC-COMP-010.
- Set Hydro retest due to 30 days from today.
- Save and reload the index.
Expected: The cylinder's status pill flips to a warning state ("Hydro due ≤ 30d" or similar based on the configured lead-day window).
TC-COMP-012 — Archive cylinder
Goal: Verify archive removes a cylinder from new-pick lists without affecting history.
Steps:
- Open the cylinder, select Archive.
- Open the Charge Events index.
- Try to log a removal that would pick this cylinder.
Expected: The cylinder no longer appears in the recovery-equipment dropdown. Historical events that referenced it still display its make/model/serial.
Technician certifications
TC-COMP-020 — Create a §608 certification
Goal: Verify cert creation with file upload.
Steps:
- Sign in with
compliance.manage_certifications. - Open Technician Certifications, select New.
- Pick a technician. Cert type: "EPA §608 Universal". Cert number: "608U-12345".
- Date issued: 2 years ago. Expiration: blank (lifetime).
- Upload a sample JPG cert card under 10 MB.
- Save.
Expected: Cert appears in the index. The technician's cert badge appears on subsequent charge events.
TC-COMP-021 — Unique constraint on (user, cert_type)
Goal: Verify a tech cannot have two certs of the same type.
Steps:
- Try to create a second §608 Universal cert for the same technician from TC-COMP-020.
Expected: Save fails with "The user has already been taken" or similar. The existing cert must be edited, not duplicated.
TC-COMP-022 — File upload over 10 MB rejected
Goal: Verify the file size limit.
Steps:
- Edit an existing cert.
- Try to upload a 12 MB image as the cert card.
Expected: Upload fails with a size-limit error. The existing attachment is preserved.
Charge events
TC-COMP-030 — Read-only ledger
Goal: Verify the charge events index has no create / edit / delete affordances.
Steps:
- Sign in with
compliance.view. - Open Charge Events.
Expected: No New, Edit, or Delete buttons or row actions. Only filter form, table, and pagination. Banner reads "EPA charge events ledger. Read-only feed…".
TC-COMP-031 — Filter by status reveals voided events
Goal: Verify status filter behavior.
Steps:
- With at least one voided event in the system, open Charge Events with no filters.
- Note that voided events are absent (default
Active only). - Switch Status to All (active + voided), apply.
Expected: Voided events appear with strikethrough text and a red Voided pill. Hovering the pill shows the voider, void time, and void reason.
TC-COMP-032 — Unit-scoped filter pill
Goal: Verify the unit-scoped pill from the per-unit compliance panel.
Steps:
- Open a unit's edit page that has at least one charge event.
- From the unit's compliance panel, select the View all affordance for charge events.
Expected: Charge Events index opens with a sticky context pill showing the unit. The unit filter is hidden from the form (preserved as a hidden input). Clear unit filter removes the pill.
Leak-repair episodes
TC-COMP-040 — Episode opens automatically on red calc
Goal: Verify that crossing the §84.106(d) threshold opens an episode.
Steps:
- Pick an in-scope unit with no active episode.
- Log charge events totaling enough top-offs to push the rolling 12-month leak rate above the threshold for the unit's equipment category.
- Open the unit's compliance panel.
Expected: A new active leak-repair episode appears on the panel. The Leak Repairs index lists it with discovery date, leak rate, and threshold snapshot.
TC-COMP-041 — Repair record + initial verification
Goal: Verify the standard repair-record + initial verification flow.
Steps:
- Sign in with
compliance.manage_leak_repair. - Open the episode from TC-COMP-040.
- Repair record: pick a method, set Repair completed at to today, pick repair tech.
- Save.
- Initial verification: today's date, method, Result =
Passed. - Save.
Expected: Repair due date stops being overdue (if it was). Initial verification shows green. Follow-up due card now shows a date 10 days from initial verification.
TC-COMP-042 — Follow-up + close
Goal: Verify follow-up verification and closing the episode.
Steps:
- From TC-COMP-041, set follow-up verification: today, method, Result =
Passed. - Save.
- The Close this episode card appears. Pick close reason
repair_verified, save.
Expected: Episode flips to closed state. Leak Repairs index removes it from open list. Unit's compliance panel no longer shows an active episode.
TC-COMP-043 — Next-add presumption stamping
Goal: Verify automatic stamping of the §84.106(d)(2) presumption.
Steps:
- With the closed episode from TC-COMP-042, log a small refrigerant addition that does not push the leak rate back over threshold.
- Open the closed episode.
Expected: Next-add presumption card shows "Repair is presumed compliant under §84.106(d)(2) — next add landed below threshold." presumption_at is stamped. Confirming calc ID is shown.
TC-COMP-044 — Retrofit / retire plan path
Goal: Verify the alternative §82.157(c)(7) close path.
Steps:
- Open a fresh episode without recording a repair.
- In Alternative path card, pick Plan type =
retrofit, set Committed at = today. - Save.
Expected: Completion due (auto) populates with today + 1 year. The episode header shows "Retire / retrofit plan committed."
Override requests
TC-COMP-050 — Approve a cert-gating override
Goal: Verify the approve flow writes a compliance_overrides row and posts the event.
Steps:
- Configure cert-gating mode to
warn. - Sign in as a tech whose §608 cert just expired.
- Save a charge event from a work order.
- Sign in as a different supervisor with
compliance.override_cert_gating. - Open Compliance Override Requests, select the new request, type a 30-character reason, select Approve.
Expected: Request flips to approved. A compliance_overrides row exists linking the request and the supervisor. The originating charge event leaves pending_compliance_review and counts toward the unit's leak-rate calc.
TC-COMP-051 — Reject voids the originating event
Goal: Verify the reject flow voids the event.
Steps:
- Repeat TC-COMP-050 setup with a fresh override request.
- As the supervisor, type a 30-character reason and select Reject & void event.
Expected: Request flips to rejected. The originating charge event is voided; void reason captures the rejection text. The event no longer appears in Active only filter on the ledger.
TC-COMP-052 — Four-eyes self-decision blocked
Goal: Verify the technician cannot decide their own request.
Steps:
- Grant the technician from TC-COMP-050 the
compliance.override_cert_gatingpermission. - As that technician, open the request.
Expected: Page shows "Four-eyes blocked. You cannot approve or reject a request whose subject technician is yourself." Approve and reject cards do not render.
TC-COMP-053 — Permission required block
Goal: Verify a supervisor without the kind-specific permission cannot decide.
Steps:
- Sign in as a supervisor with
compliance.viewbut notcompliance.override_cert_gating. - Open a pending cert-gating override request.
Expected: Page shows "Permission required. Deciding this request requires the compliance.override_cert_gating permission." Approve and reject cards do not render.
Chronic-leaker classification
TC-COMP-060 — Classification fires at 125%
Goal: Verify automatic classification on threshold crossing.
Steps:
- Pick an in-scope unit with full charge ≥ 15 lb.
- Log calendar-year top-offs totaling 125% or more of full charge.
- Open the unit's compliance panel.
Expected: Amber chronic-leaker banner appears with the calendar year, loss percentage, threshold (125%), classification date, and report due date (March 1 of the following year). A row appears in regulatory_chronic_leaker_records.
TC-COMP-061 — Mark as EPA-reported
Goal: Verify the report-filed mark and on-time / late stamp.
Steps:
- From TC-COMP-060, sign in with
compliance.manage_chronic_leaker_record. - Select Mark as EPA-reported, confirm the prompt.
Expected: Banner flips to a blue badge "EPA-reported on time YYYY-MM-DD" (assuming today is before the report due date). The button hides. The record's epa_report_submitted_at and epa_report_filed_by_user_id are set.
TC-COMP-062 — Withdraw classification
Goal: Verify the supervisor withdrawal flow.
Steps:
- Sign in with
compliance.override_chronic_leaker_classification. - From the unit's compliance panel, expand Supervisor: withdraw chronic classification.
- Type a reason of at least 20 characters.
- Select Withdraw classification for :year.
Expected: The current year's classification is voided on the ledger. Banner is removed from the unit's panel. Prior-year records (if any) are preserved.
TC-COMP-063 — Eligibility floor blocks small units
Goal: Verify that units under 15 lb full charge never classify.
Steps:
- Pick an in-scope unit with full charge of 14 lb.
- Log calendar-year top-offs totaling 200% of full charge.
Expected: No chronic-leaker banner appears. No regulatory_chronic_leaker_records row is created. The leak-rate calc may still be red and may open a leak-repair episode, but the chronic clock is silent.
Records export
TC-COMP-070 — Standard annual export
Goal: Verify the export ZIP contains the expected CSVs and a manifest.
Steps:
- Open Charge Events.
- Set From = Jan 1 of the current year, To = Dec 31 of the current year, Status =
All. - Apply the filter, then select Download CSV records.
- Open the downloaded ZIP.
Expected: ZIP contains the full fixed set of CSVs — including charge_events.csv, leak_rate_calculations.csv, leak_repairs.csv, leak_inspections.csv, leak_repair_extensions.csv, chronic_leakers.csv, retrofit_retire_requests.csv, unit_full_charge_records.csv, unit_full_charge_revisions.csv, part84_rlca_audits.csv, unit_mothball_periods.csv, leak_methodology_changes.csv, automatic_leak_detections.csv, ald_calibration_audits.csv, ald_leak_alerts.csv, epa_notifications.csv, technician_certifications.csv — plus manifest.txt. Every CSV is emitted (header-only when zero rows). The manifest records the filter, the export timestamp, the per-CSV row counts, and the chain-of-custody status summary for charge_events.csv.
TC-COMP-071 — Filter scope reflected in export
Goal: Verify export honors the on-screen filter.
Steps:
- Open Charge Events, scope to one technician via the Technician dropdown, narrow date range to one month.
- Note the on-screen row count.
- Download CSV records, open
charge_events.csv.
Expected: Row count in the CSV matches the on-screen count. Manifest's filter section records the technician ID and date range.
Part 84 remediation
TC-COMP-080 — Bucket A: classify removes from queue
Goal: Verify Bucket A clears after classification.
Steps:
- Add a refrigerant with no AIM/ODS flag and no GWP. Link it to at least one device model and one in-scope unit.
- Open Part 84 Remediation. Note the new refrigerant in Bucket A with its affected unit count.
- Select Classify for the row, set the AIM flag, save.
- Reload Part 84 Remediation.
Expected: Bucket A row is gone. The summary tile for "Refrigerant catalog rows needing classification" decreases by 1. The "Units affected via unclassified refrigerant" tile decreases by the previously shown count.
TC-COMP-081 — Bucket B: assign refrigerant on unit
Goal: Verify Bucket B clears after unit-side assignment.
Steps:
- Pick an in-scope unit with no refrigerant assigned. Confirm it appears in Bucket B.
- Select Edit unit for the row, assign a classified refrigerant, save.
- Reload Part 84 Remediation.
Expected: Unit no longer appears in Bucket B. Summary tile for "Units with no refrigerant assigned" decreases by 1.
TC-COMP-082 — Permission gate on Classify action
Goal: Verify the Classify action is permission-gated.
Steps:
- Sign in as a user with
compliance.viewbut withoutcompliance.manage_refrigerant_catalog. - Open Part 84 Remediation.
Expected: Bucket A rows show "Needs catalog permission" lock icon instead of the Classify action. Bucket B unit rows still show Edit unit if the user has units.edit.