// The opening against Go: every case of test/vectors/open_cases.json, which // tool/open_go_vectors.go writes with capsule.Open of the reference, opened // in memory and from a source read in pieces: the same result, text, checks // with their details, release requests, state of the sink, content or files, // and unusable extensions, and an output that is closed only when a capsule // of format 1 or 2 opens and aborted after any failure (spec ยง56). @TestOn('vm') library; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:datekeys/datekeys.dart'; import 'package:datekeys/src/sha256.dart' show sha256; import 'package:test/test.dart'; import 'open_support.dart'; Json readJson(String path) => jsonDecode(File(path).readAsStringSync()) as Json; final _fixtures = {}; Uint8List fixture(String file) => _fixtures.putIfAbsent( file, () => File('testdata/fixtures/$file').readAsBytesSync(), ); void main() { final vectors = readJson('test/vectors/open_cases.json'); final fixtures = (vectors['fixtures']! as List).cast(); final steps = (vectors['steps']! as List).cast(); test('open_cases.json is of this spec, from the generator of this repo', () { expect(vectors['spec'], specVersion); expect(vectors['generator'], 'tool/open_go_vectors.go'); expect(fixtures, hasLength(greaterThanOrEqualTo(48))); expect(steps, hasLength(greaterThan(100))); // Every fixture opens with each of its credentials. final names = {for (final c in fixtures) str(c, 'name').split(':').first}; final files = Directory('testdata/fixtures') .listSync() .map((f) => f.uri.pathSegments.last) .where((n) => n.endsWith('.dkc')) .map((n) => n.substring(0, n.length - 4)) .toSet(); expect(names, files); expect(fixtures.every((c) => c['result'] == 'ok'), isTrue); // The steps that the mutation corpus does not reach are here. final failed = { for (final c in steps) if (c['result'] != 'ok') ((c['checks']! as List).last as List)[0], }; expect(failed, containsAll([2, 3, 7, 8, 9, 10, 11, 12, 13, 14])); expect(failed, containsAll([15, 17])); }); test('every fixture opens to its plaintext, or to the files of its record, ' 'with each credential', () async { for (final c in fixtures) { final name = str(c, 'name').split(':').first; final r = readJson('testdata/fixtures/$name.json'); final plaintext = fixture(str(r, 'plaintext_file')); expect(toHex(sha256(plaintext)), r['plaintext_sha256'], reason: name); final o = await openCase(c, capsuleOf(c, fixture)); expect(o.opened.ok, isTrue, reason: str(c, 'name')); if (r['format'] != 3) { expect(o.output.received.toBytes(), plaintext, reason: str(c, 'name')); continue; } // Format 3: each file is the range of BODY that the record names. final at = r['content_offset']! as int; final files = (r['files'] as List? ?? const []).cast(); expect(o.sink.files, hasLength(files.length), reason: str(c, 'name')); for (var i = 0; i < files.length; i++) { final f = files[i]; expect( o.sink.files[i].toBytes(), plaintext.sublist(at + (f['start']! as int), at + (f['end']! as int)), reason: '${c['name']}: ${f['path']}', ); } expect( [o.opened.head!.comment, o.opened.head!.author, o.opened.areaLen], [r['comment'] ?? '', r['declared_author'] ?? '', r['area_len']], reason: str(c, 'name'), ); } }); test('the comparison sees every field', () async { final c = steps.firstWhere((c) => c['name'] == 'format 3: two files'); final o = await openCase(c, capsuleOf(c, fixture)); expect(differences(c, o), isEmpty); for (final (key, value) in <(String, Object?)>[ ('result', 'ERR_INTEGRITY'), ('text', 'ok.'), ('checks', (c['checks']! as List).sublist(1)), ('release_requests', 0), ('sink', 'aborted'), ('files', []), ('unusable', {'head': []}), ]) { expect(differences({...c, key: value}, o), hasLength(1), reason: key); } }); for (final (section, cases) in [('fixtures', fixtures), ('steps', steps)]) { group(section, () { for (final c in cases) { test(str(c, 'name'), () async { final dkc = capsuleOf(c, fixture); final memory = await openCase(c, dkc); expect(differences(c, memory), isEmpty, reason: 'in memory'); final source = await openCase(c, dkc, fromSource: true); expect(differences(c, source), isEmpty, reason: 'from a source'); }); } }); } }