-
Notifications
You must be signed in to change notification settings - Fork 0
Validation and Testing
npm testRuns node tests/run-tests.mjs. No external test framework — tests/run-tests.mjs implements describe/test/expect inline (lines 10–140) with matchers .toBe, .toEqual, .toBeCloseTo, .toBeGreaterThan, .toBeLessThan, .toHaveProperty, .toHaveLength, .toBeDefined. Output is one line per test, then a pass/fail summary.
208 tests pass in under 5 seconds. The Jest configuration in package.json is vestigial — test:jest and test:watch still work but are not the canonical runner; the CI gate is npm test.
npm test is required to pass before every commit per CLAUDE.md.
The algorithm suite. Directly imports from js/decoModel.js and js/tissueCompartments.js.
-
Constants & pressure. Checks
SURFACE_PRESSURE,WATER_VAPOR_PRESSURE,N2_FRACTION,PRESSURE_PER_METER,getAmbientPressure,getAlveolarN2Pressure. - Haldane and Schreiner equations. Verifies the closed-form output at known half-times (1, 5, 10, 20 min) and compares Schreiner against a time-stepped Haldane reference.
-
M-values, ceilings, GF interpolation. Exercises
getMValue,getAdjustedMValue,getCompartmentCeiling,getDiveCeiling,interpolateGF, and thecalculateInstantGF/calculateMaxGFpair. -
GF-low anchor and first stop. Covers
findFirstStopAtGFLowacross a range of depths and GF settings, including edge cases where the unrounded ceiling coincides with a 3 m grid depth and where gas switches occur en route. -
Deco schedule. Full
generateDecoScheduleruns for air, EAN50, and O₂ deco; asserts stop count, per-stop time bounds, total-deco bounds, and theDecoCapExceededErrorpath. -
Tissue loading end-to-end.
calculateTissueLoadingon simple profiles; verifies monotonicity on descent, exponential shape, and surface equilibration. -
Variant switching.
setZHL16Variant('A'|'B'|'C')followed by re-checking a reference dive.
Configuration and gas management.
-
Setup loading.
getDefaultSetup,extendDiveSetup, localStorage round-trip viasaveDiveSetup/loadSavedSetup. -
Profile generation.
generateSimpleProfiledescent/ascent rates, bottom-time semantics (measured from dive start, not depth arrival), safety stop insertion. -
Gas helpers.
calculateMOD,getGases,getGasAtWaypoint,getGasAtTime,getGasSwitchEvents,insertGasSwitchWaypointswith 3 m MOD rounding. -
computeGasConsumption. SAC accounting at switch depths — explicitly tests that the switch-stop window bills at the bottom SAC rate, not the deco SAC rate.
Waypoint-array validation.
- Structure (first waypoint at surface, monotonic time, non-negative depth).
-
parseProfileInputon tab-separated and comma-separated text. -
calculateRatesclassification into descent / ascent / level. -
getDiveStatsmaxima and totals.
The cross-implementation comparison script. Runs as part of npm test (included by run-tests.mjs) but also runnable standalone: node tests/decotengu-comparison.test.mjs.
Procedural style rather than describe/test — loops over every scenario in tests/decotengu-reference.json, reports pass/fail counts, and exits non-zero on regression.
Pinned regression: passing {gasSwitchTime: 0} to generateDecoSchedule must produce byte-identical schedules to the baseline captured before the gasSwitchTime feature was added. Baseline file: tests/decojs-baseline.json. Tolerance is 0 — exact match required across all 3900 scenarios.
This is the primary numerical-correctness evidence for the DecoJS algorithm.
Reference data. tests/decotengu-reference.json holds 3900 pre-generated deco scenarios produced by decotengu v0.14.1 (see References). Reference data is regenerated with python3 scripts/generate_decotengu_reference.py > tests/decotengu-reference.json.
Scenario coverage.
- Depths: 15 m, 18 m, 21 m, … 60 m (step 3 m, 16 values)
- Bottom times: NDL+3, NDL+6, … NDL+30 min (step 3 min, 10 values per depth)
-
Gas configs:
air,air+EAN50,air+O2,air+EAN50+O2 - GF presets: 100/100 (Bühlmann), 90/95, 80/85, 70/80, 50/80, 40/80, 30/80, 20/80 (Deco Planner-style)
- Algorithm: Bühlmann ZH-L16C for both sides
Tolerances. Per-scenario assertion is |total_deco_diff| ≤ max(5 min, 20% of reference_total_deco). Per-stop tolerance is ±1 min.
These tolerances cover stop-time discretization noise — both implementations round stop times to whole minutes, and the continuous ceiling crossing can land on either side of a minute boundary, giving ±1 min per stop.
Match statistics across all 3900 scenarios:
| Pass rate | 100 % (3900/3900) |
| Exact match on total deco | 56 % (2196 scenarios) |
| Within ±2 min | 95 % (P95) |
| Mean |diff| | 0.6 min |
| Median |diff| | 0 min |
| Max |diff| | 5 min |
| DecoJS gives less | 41 % |
| DecoJS gives more | 3 % |
The largest residuals are concentrated at GF 20/80 (the most aggressive setting in the matrix) where stop-time discretization between the two implementations occasionally lands a stop on different minute boundaries.
Bühlmann constants (SURFACE_PRESSURE=1.01325, N2_FRACTION=0.7902, TC1 b-coefficient variant-specific) match decotengu — which itself cross-references the HeinrichsWeikamp OSTC firmware. See References for the provenance chain.
Tests live in tests/ with filenames *.test.js (ES modules via import) or *.test.mjs. Structure is:
// tests/myThing.test.js
import { myFn } from '../js/decoModel.js';
describe('myFn', () => {
test('returns the expected value at surface', () => {
expect(myFn(0)).toBeCloseTo(1.01325, 4);
});
});For a new algorithm feature:
- Add unit tests covering the equation at a few hand-calculated points and the equation's boundary behaviour (surface, max depth, zero time).
- If the feature changes deco output, regenerate
tests/decojs-baseline.jsondeliberately and commit the diff in a separate commit so the regression test stays meaningful. - If the feature could diverge from decotengu, note the divergence in
tests/decotengu-comparison.test.mjscomment block and widen the tolerance locally for the affected scenarios rather than globally.
Bug fixes should include a regression test that fails without the fix (per CLAUDE.md convention).
What is currently not covered — honest inventory so callers know where to be careful:
-
UI components. No render or interaction tests for
DiveSetupEditor,DiveProfileChart,MValueChart, orGFChart. Chart.js output is not asserted. -
i18n. No tests for translation loading,
data-i18nsubstitution, or thelanguagechangeevent fan-out to components. -
Keyboard shortcuts.
MValueChartandGFChartexpose arrow-key / space / home / end playback; none of this is tested. -
Helium.
COMPARTMENTScarries He coefficients but the algorithm lumps He into N₂ vian2Fraction. Full trimix (separate He kinetics) is not implemented and not tested. Gas definitions accepthe > 0but no decotengu-reference scenarios exercise it. -
SAC / gas consumption edge cases.
computeGasConsumptionhas basic coverage but not realistic multi-dive or bail-out scenarios. -
Altitude / salinity.
DEFAULT_ENVIRONMENTinchartTypes.jsexists but the algorithm uses fixedSURFACE_PRESSURE = 1.01325; altitude adjustment is not tested because it is not implemented.
Cross-link: see the individual algorithm chapters (Algo-02-NDL-Calculation, Algo-03-First-Stop-Ramped-GF, Algo-04-Deco-Stop-Loop, Algo-05-Multi-Gas-Switching) for which algorithm each test file exercises.