Third step of the state-map seam, and the proof that it holds. stateFlowFromJson rebuilds a StateFlow from its toJson form (computed members - totals, coverage differences, Riverpod gaps - are recomputed), and `pipe check --model <file>` runs every rule on such a file instead of analysing Dart. A front end for another language now only has to write that JSON to get every rule, --baseline ratcheting and --fail-on gating. StateUnion.toJson now carries uiReadsBaseFields (only when true), which the round trip showed it had been dropping. Checked byte for byte, through a file on disk: `pipe state --format json` then `pipe check --model` reports exactly what `pipe check` does on the fixture and on resolved mirrors of nuwayze (102 findings), tick, dex and word_alive. state_map_roundtrip_test.dart asserts the same in process (model JSON is idempotent; findings identical; every rule fires, so the comparison is not vacuous), and model_cli_test.dart covers the CLI path. --model refuses --write-baseline: a model does not record whether its front end read every file, so the baseline could silently be partial - the hazard the source path already refuses. A malformed model is a PipeException, not a crash. 134 tests pass.
44 lines
1.4 KiB
Dart
44 lines
1.4 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:pipe/pipe.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
/// The state-map seam: rules read only the model, so a model that went through
|
|
/// JSON - as one written by a separate front end would - must produce exactly
|
|
/// the same report as the in-process model. If a rule quietly depended on
|
|
/// something the JSON does not carry, this is where it shows.
|
|
void main() {
|
|
late ProjectAnalysis analysis;
|
|
|
|
setUpAll(() async {
|
|
analysis = await analyzeProject(
|
|
await loadProject('test/fixtures/stuck_loading'),
|
|
concurrency: 1,
|
|
);
|
|
});
|
|
|
|
tearDownAll(() async {
|
|
await analysis.dispose();
|
|
});
|
|
|
|
StateFlow throughJson(StateFlow flow) => stateFlowFromJson(
|
|
jsonDecode(jsonEncode(flow.toJson())) as Map<String, Object?>,
|
|
);
|
|
|
|
test('the model survives JSON unchanged', () {
|
|
final once = jsonEncode(analysis.flow.toJson());
|
|
final twice = jsonEncode(throughJson(analysis.flow).toJson());
|
|
expect(twice, once);
|
|
});
|
|
|
|
test('rules on the decoded model report exactly the same findings', () {
|
|
final direct = jsonEncode(runChecks(flow: analysis.flow).toJson());
|
|
final decoded = jsonEncode(
|
|
runChecks(flow: throughJson(analysis.flow)).toJson(),
|
|
);
|
|
expect(decoded, direct);
|
|
// Not vacuous: every rule fires somewhere on the fixture.
|
|
final rules = runChecks(flow: analysis.flow).findings.map((f) => f.rule);
|
|
expect(rules.toSet(), containsAll(allRules));
|
|
});
|
|
}
|