Inspector page: static SvelteKit site with /inspect (plan step 5)
A prerendered static site (adapter-static) with a landing page and
/inspect, which runs spec §63 steps 1 to 8 on a .dkc chosen with the file
picker, dropped anywhere on the page, or taken from the official fixtures
bundled at build time. It shows every step, the decoded header, the unlock
date in UTC and local time, and each extension's id, version, criticality,
length and hex, with a text view and an informative CBOR diagnostic view,
all escaped and labelled as unauthenticated before step 15. Copiar JSON
copies the exact "datekeys inspect -json" view.
No network: a hash-mode Content-Security-Policy with connect-src 'self'
is the first element of every page, and scripts/check-build.mjs verifies
it, the fixtures and the absence of external URLs after every build.
Large files are read only up to what steps 1 to 8 need.
Reviewed for design and accessibility (WCAG AA contrast, keyboard,
focus, live status, 360 px), security and correctness; 262 tests pass,
svelte-check has no warnings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
|
import { defaultRegistry, type ExtensionRegistry, ExtensionSet, inspectView, inspectWith } from '../dkc/index.ts';
|
|
|
|
|
import { frame, replaceText, split } from '../dkc/testing/capsule.ts';
|
|
|
|
|
import { arr, b, dkRound, ext, map, t, u, bn } from '../dkc/testing/cborhex.ts';
|
|
|
|
|
import { h, listTestdata, readBytes, readJSON } from '../dkc/testing/testdata.ts';
|
|
|
|
|
import { cliJSON } from './format.ts';
|
|
|
|
|
import { buildReport, type Report } from './report.ts';
|
|
|
|
|
|
|
|
|
|
const registry = await defaultRegistry();
|
|
|
|
|
const timeOnly = readBytes('fixtures/time_only_extensions.dkc');
|
|
|
|
|
const parts = split(timeOnly);
|
|
|
|
|
|
|
|
|
|
function report(dkc: Uint8Array, extensions?: ExtensionRegistry, fileName = 'x.dkc'): Report {
|
|
|
|
|
const inspection = inspectWith(dkc, registry, extensions);
|
|
|
|
|
return buildReport(extensions === undefined ? { fileName, bytes: dkc, size: dkc.length, inspection } : { fileName, bytes: dkc, size: dkc.length, inspection, extensions });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A PUBLIC_HEADER like the one of time_only_extensions, with other extensions.
|
|
|
|
|
function header(o: { crit?: string; non?: string; dk?: string }): Uint8Array {
|
|
|
|
|
const entries: [number, string][] = [
|
|
|
|
|
[0, t('datekeycap')],
|
|
|
|
|
[1, u(1)],
|
|
|
|
|
[2, bn(16, 7)],
|
|
|
|
|
[3, o.dk ?? t(dkRound(2000))],
|
|
|
|
|
[4, u(0)],
|
|
|
|
|
];
|
|
|
|
|
if (o.crit) entries.push([5, o.crit]);
|
|
|
|
|
if (o.non) entries.push([6, o.non]);
|
|
|
|
|
return h(map(...entries));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface FixtureRecord {
|
Read capsule format 2 of spec v0.9
Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the
reader to the DateKeys Protocol Specification v0.9. Both capsule formats
are read; a format 1 capsule keeps the verdict v0.8.2 gave it.
- framing: the VERSION of the prelude is the capsule format, 1 or 2
(Prelude.format, FORMAT_1, FORMAT_2, isFormat).
- control: decodeControl and encodeControl take the format; schema
version 2 adds payload_length (8 bytes, at most L_MAX) and padding
(1 or 2).
- padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up
to L_MAX with BigInt bit lengths and ceil roundings, and the length of
PAYLOAD_AGE.
- open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P
at step 16, and at step 17 a plaintext of exactly P bytes whose
padding is zero; only the first L bytes are delivered, never the
padding. Step 17 is recorded when it passes, and step 18 gives the
bytes of content, as the reference does. Opened reports the format, L
and, in format 2, the rule and P.
- inspect: the JSON view carries format, as datekeys inspect -json.
- The page shows the format, warns about format 1, and gives the
padding rule and P once a format 2 capsule opens.
Tests: the twelve fixtures, the 125 mutation cases through open from
memory and from a Blob, the 4380 differential cases, padding.json, the
format 2 CBOR vectors, and padding.test.ts against a BigInt statement of
§29.1. The error texts of the 125 corpus cases were compared with
capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2
fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
format: number;
|
Inspector page: static SvelteKit site with /inspect (plan step 5)
A prerendered static site (adapter-static) with a landing page and
/inspect, which runs spec §63 steps 1 to 8 on a .dkc chosen with the file
picker, dropped anywhere on the page, or taken from the official fixtures
bundled at build time. It shows every step, the decoded header, the unlock
date in UTC and local time, and each extension's id, version, criticality,
length and hex, with a text view and an informative CBOR diagnostic view,
all escaped and labelled as unauthenticated before step 15. Copiar JSON
copies the exact "datekeys inspect -json" view.
No network: a hash-mode Content-Security-Policy with connect-src 'self'
is the first element of every page, and scripts/check-build.mjs verifies
it, the fixtures and the absence of external URLs after every build.
Large files are read only up to what steps 1 to 8 need.
Reviewed for design and accessibility (WCAG AA contrast, keyboard,
focus, live status, 360 px), security and correctness; 262 tests pass,
svelte-check has no warnings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
capsule_id: string;
|
|
|
|
|
datekey: string;
|
|
|
|
|
access_policy: string;
|
|
|
|
|
unlock_at: string;
|
|
|
|
|
prelude: string;
|
|
|
|
|
outer_stanzas: { type: string; args: string[] }[];
|
|
|
|
|
payload_stanzas: { type: string; args: string[] }[];
|
|
|
|
|
header_extensions?: { critical: boolean; id: string; version: number; data?: string }[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('buildReport', () => {
|
What the official SDK says when it seals: the recovery annex, §7.6 and §71
As aefc8f6 of datekeys-go. sync-testdata.mjs also vendors annex/ of Go,
the recovery annex (§79 under a title with the version and the SHA-256 of
the specification), and testdata, wordlists and annex are at aefc8f6.
/create recommends the key to a time_only capsule more than a year ahead
(§7.6), and once the capsule is made it says what opening it later will
take: the capsule, one of its keys if it has them, and the signature of
drand for its round, which an archive or a cache service must keep if
drand no longer serves it (§62.1, rule 26); and it offers the annex for
download as <capsule>.recuperacion.txt (rule 27, annex.ts). check-build.mjs
wants the annex shipped byte for byte.
profile.ts gains ProfileStatus, PROFILE_STATUS and profileStatusOf, as
StatusOf of Go: Quicknet is active. planCapsule writes no capsule with a
profile that is not, and /inspect warns when the profile of a capsule is
compromised (buildReport takes profileStatus).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 day ago
|
|
|
it('carries the state of the profile that the caller gives, from the registry of §71', () => {
|
|
|
|
|
const dkc = readBytes('fixtures/time_only.dkc');
|
|
|
|
|
const inspection = inspectWith(dkc, registry);
|
|
|
|
|
expect(buildReport({ fileName: 'x.dkc', bytes: dkc, size: dkc.length, inspection }).profileStatus).toBeUndefined();
|
|
|
|
|
expect(buildReport({ fileName: 'x.dkc', bytes: dkc, size: dkc.length, inspection, profileStatus: 'compromised' }).profileStatus).toBe('compromised');
|
|
|
|
|
});
|
|
|
|
|
|
Inspector page: static SvelteKit site with /inspect (plan step 5)
A prerendered static site (adapter-static) with a landing page and
/inspect, which runs spec §63 steps 1 to 8 on a .dkc chosen with the file
picker, dropped anywhere on the page, or taken from the official fixtures
bundled at build time. It shows every step, the decoded header, the unlock
date in UTC and local time, and each extension's id, version, criticality,
length and hex, with a text view and an informative CBOR diagnostic view,
all escaped and labelled as unauthenticated before step 15. Copiar JSON
copies the exact "datekeys inspect -json" view.
No network: a hash-mode Content-Security-Policy with connect-src 'self'
is the first element of every page, and scripts/check-build.mjs verifies
it, the fixtures and the absence of external URLs after every build.
Large files are read only up to what steps 1 to 8 need.
Reviewed for design and accessibility (WCAG AA contrast, keyboard,
focus, live status, 360 px), security and correctness; 262 tests pass,
svelte-check has no warnings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
it('shows every official fixture as its JSON record', () => {
|
|
|
|
|
for (const f of listTestdata('fixtures', '.dkc')) {
|
|
|
|
|
const dkc = readBytes(f);
|
|
|
|
|
const want = readJSON<FixtureRecord>(f.replace(/\.dkc$/, '.json'));
|
|
|
|
|
const name = f.slice('fixtures/'.length);
|
|
|
|
|
const r = report(dkc, undefined, name);
|
|
|
|
|
expect(r.valid, f).toBe(true);
|
|
|
|
|
expect(r.failure).toBeUndefined();
|
|
|
|
|
expect(r.steps.map((s) => [s.step, s.state])).toEqual([1, 2, 3, 4, 5, 6, 7, 8].map((s) => [s, 'ok']));
|
|
|
|
|
expect(r.capsule).toMatchObject({ capsuleId: want.capsule_id, dateKey: want.datekey, accessPolicy: want.access_policy, network: 'datekeys:quicknet:v1' });
|
|
|
|
|
expect(JSON.parse(r.capsule!.dateKeyJSON)).toEqual({ version: 1, network: 'datekeys:quicknet:v1', round: r.capsule!.round });
|
|
|
|
|
expect(r.unlock?.rfc3339).toBe(want.unlock_at);
|
|
|
|
|
expect(r.unlock?.epochMs).toBe(Date.parse(want.unlock_at));
|
|
|
|
|
expect(r.prelude?.hex).toBe(want.prelude);
|
Read capsule format 2 of spec v0.9
Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the
reader to the DateKeys Protocol Specification v0.9. Both capsule formats
are read; a format 1 capsule keeps the verdict v0.8.2 gave it.
- framing: the VERSION of the prelude is the capsule format, 1 or 2
(Prelude.format, FORMAT_1, FORMAT_2, isFormat).
- control: decodeControl and encodeControl take the format; schema
version 2 adds payload_length (8 bytes, at most L_MAX) and padding
(1 or 2).
- padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up
to L_MAX with BigInt bit lengths and ceil roundings, and the length of
PAYLOAD_AGE.
- open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P
at step 16, and at step 17 a plaintext of exactly P bytes whose
padding is zero; only the first L bytes are delivered, never the
padding. Step 17 is recorded when it passes, and step 18 gives the
bytes of content, as the reference does. Opened reports the format, L
and, in format 2, the rule and P.
- inspect: the JSON view carries format, as datekeys inspect -json.
- The page shows the format, warns about format 1, and gives the
padding rule and P once a format 2 capsule opens.
Tests: the twelve fixtures, the 125 mutation cases through open from
memory and from a Blob, the 4380 differential cases, padding.json, the
format 2 CBOR vectors, and padding.test.ts against a BigInt statement of
§29.1. The error texts of the 125 corpus cases were compared with
capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2
fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
expect(r.prelude).toMatchObject({ magic: 'DKC1', format: want.format, flags: '0x00', reserved: '0x0000' });
|
Inspector page: static SvelteKit site with /inspect (plan step 5)
A prerendered static site (adapter-static) with a landing page and
/inspect, which runs spec §63 steps 1 to 8 on a .dkc chosen with the file
picker, dropped anywhere on the page, or taken from the official fixtures
bundled at build time. It shows every step, the decoded header, the unlock
date in UTC and local time, and each extension's id, version, criticality,
length and hex, with a text view and an informative CBOR diagnostic view,
all escaped and labelled as unauthenticated before step 15. Copiar JSON
copies the exact "datekeys inspect -json" view.
No network: a hash-mode Content-Security-Policy with connect-src 'self'
is the first element of every page, and scripts/check-build.mjs verifies
it, the fixtures and the absence of external URLs after every build.
Large files are read only up to what steps 1 to 8 need.
Reviewed for design and accessibility (WCAG AA contrast, keyboard,
focus, live status, 360 px), security and correctness; 262 tests pass,
svelte-check has no warnings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
expect(r.prelude!.payloadOffset + r.prelude!.payloadLength!).toBe(dkc.length);
|
|
|
|
|
expect(r.outerStanzas).toEqual(want.outer_stanzas);
|
|
|
|
|
expect(r.payloadStanzas).toEqual(want.payload_stanzas);
|
|
|
|
|
expect(r.tlock?.every((c) => c.match)).toBe(true);
|
|
|
|
|
expect(r.profile).toMatchObject({ id: 'datekeys:quicknet:v1', network: 'quicknet', provider: 'drand', period: 3, genesis: '2023-08-23T15:09:27Z' });
|
|
|
|
|
expect(r.extensions?.map((e) => ({ critical: e.critical, id: e.id, version: e.version, data: e.hex }))).toEqual(
|
|
|
|
|
(want.header_extensions ?? []).map((e) => ({ critical: e.critical, id: e.id, version: e.version, data: e.data ?? '' })),
|
|
|
|
|
);
|
|
|
|
|
expect(r.view).toEqual(inspectView(inspectWith(dkc, registry), name));
|
|
|
|
|
expect(r.json).toBe(cliJSON(r.view));
|
|
|
|
|
expect(r.view.file).toBe(name);
|
|
|
|
|
expect(r.readLength).toBe(dkc.length);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('carries the public note, or that a note is not shown', () => {
|
|
|
|
|
const note = readBytes('fixtures/format3_note.dkc');
|
|
|
|
|
const base = inspectWith(note, registry);
|
|
|
|
|
expect(report(note).note).toBeUndefined(); // inspectWith does not read the note: inspect does
|
|
|
|
|
const r = (inspection: typeof base): Report => buildReport({ fileName: 'n.dkc', bytes: note, size: note.length, inspection });
|
|
|
|
|
expect(r({ ...base, publicNote: 'Cartas del viaje', unusableNote: false }).note).toEqual({ text: 'Cartas del viaje' });
|
|
|
|
|
expect(r({ ...base, unusableNote: true }).note).toEqual({ unusable: true });
|
|
|
|
|
expect(r(base).note).toBeUndefined();
|
|
|
|
|
});
|
|
|
|
|
|
Inspector page: static SvelteKit site with /inspect (plan step 5)
A prerendered static site (adapter-static) with a landing page and
/inspect, which runs spec §63 steps 1 to 8 on a .dkc chosen with the file
picker, dropped anywhere on the page, or taken from the official fixtures
bundled at build time. It shows every step, the decoded header, the unlock
date in UTC and local time, and each extension's id, version, criticality,
length and hex, with a text view and an informative CBOR diagnostic view,
all escaped and labelled as unauthenticated before step 15. Copiar JSON
copies the exact "datekeys inspect -json" view.
No network: a hash-mode Content-Security-Policy with connect-src 'self'
is the first element of every page, and scripts/check-build.mjs verifies
it, the fixtures and the absence of external URLs after every build.
Large files are read only up to what steps 1 to 8 need.
Reviewed for design and accessibility (WCAG AA contrast, keyboard,
focus, live status, 360 px), security and correctness; 262 tests pass,
svelte-check has no warnings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
|
|
|
it('shows header extension data as text, never as CBOR when it is not', () => {
|
|
|
|
|
const r = report(timeOnly);
|
|
|
|
|
expect(r.extensions).toEqual([
|
|
|
|
|
{
|
|
|
|
|
critical: false,
|
|
|
|
|
id: 'org.example.label',
|
|
|
|
|
idEscaped: false,
|
|
|
|
|
version: 1,
|
|
|
|
|
known: false,
|
|
|
|
|
length: 12,
|
|
|
|
|
hex: '7075626c6963206c6162656c',
|
|
|
|
|
text: 'public label',
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('decodes CBOR data informatively and escapes identifiers', () => {
|
|
|
|
|
const hb = header({ non: arr(ext('a\u202eb', 3, b('a2000701667365616c6564')), ext('z', 1)) });
|
|
|
|
|
const r = report(frame({ ...parts, header: hb }));
|
|
|
|
|
expect(r.valid).toBe(true);
|
|
|
|
|
expect(r.extensions).toEqual([
|
|
|
|
|
{
|
|
|
|
|
critical: false,
|
|
|
|
|
id: '"a\\u202eb"',
|
|
|
|
|
idEscaped: true,
|
|
|
|
|
version: 3,
|
|
|
|
|
known: false,
|
|
|
|
|
length: 11,
|
|
|
|
|
hex: 'a2000701667365616c6564',
|
|
|
|
|
cbor: { text: '{\n 0: 7,\n 1: "sealed"\n}', truncated: false },
|
|
|
|
|
},
|
|
|
|
|
{ critical: false, id: 'z', idEscaped: false, version: 1, known: false, length: 0, hex: '' },
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('reports an unknown critical extension at step 4 and still lists it', () => {
|
|
|
|
|
const hb = header({ crit: arr(ext('org.example.must-understand', 1, b('01'))) });
|
|
|
|
|
const r = report(frame({ ...parts, header: hb }));
|
|
|
|
|
expect(r.valid).toBe(false);
|
|
|
|
|
expect(r.failure).toEqual({
|
|
|
|
|
step: 4,
|
|
|
|
|
code: 'ERR_EXTENSION_CRITICAL_UNKNOWN',
|
|
|
|
|
message: 'capsule: PUBLIC_HEADER: extension org.example.must-understand v1: ERR_EXTENSION_CRITICAL_UNKNOWN',
|
|
|
|
|
});
|
|
|
|
|
expect(r.steps.map((s) => s.state)).toEqual(['ok', 'ok', 'ok', 'failed', 'not-run', 'not-run', 'not-run', 'not-run']);
|
|
|
|
|
expect(r.steps[3]).toMatchObject({ code: 'ERR_EXTENSION_CRITICAL_UNKNOWN', name: 'header validation' });
|
|
|
|
|
expect(r.steps[4]!.detail).toBeUndefined();
|
|
|
|
|
expect(r.extensions).toEqual([
|
|
|
|
|
{ critical: true, id: 'org.example.must-understand', idEscaped: false, version: 1, known: false, length: 1, hex: '01', cbor: { text: '1', truncated: false } },
|
|
|
|
|
]);
|
|
|
|
|
expect(r.capsule?.capsuleId).toBe('07'.repeat(16));
|
|
|
|
|
expect(r.unlock).toBeUndefined();
|
|
|
|
|
expect(r.outerStanzas).toBeUndefined();
|
|
|
|
|
expect(r.tlock).toBeUndefined();
|
|
|
|
|
expect(JSON.parse(r.json)).toMatchObject({ valid: false, error: 'ERR_EXTENSION_CRITICAL_UNKNOWN' });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('marks known extensions and the unusable ones', () => {
|
|
|
|
|
const reg: ExtensionRegistry = {
|
|
|
|
|
known: (id) => id === 'a' || id === 'b',
|
|
|
|
|
validateData: (e) => (e.id === 'b' ? new Error('bad') : undefined),
|
|
|
|
|
};
|
|
|
|
|
const r = report(frame({ ...parts, header: header({ crit: arr(ext('a', 1, b('01'))), non: arr(ext('b', 1, b('02')), ext('c', 1)) }) }), reg);
|
|
|
|
|
expect(r.valid).toBe(true);
|
|
|
|
|
expect(r.extensions?.map((e) => [e.id, e.critical, e.known, e.unusable])).toEqual([
|
|
|
|
|
['a', true, true, undefined],
|
|
|
|
|
['b', false, true, 'extension b v1: data: bad: ERR_EXTENSION_DATA_INVALID'],
|
|
|
|
|
['c', false, false, undefined],
|
|
|
|
|
]);
|
|
|
|
|
const known = report(frame({ ...parts, header: header({ crit: arr(ext('a', 1)) }) }), new ExtensionSet([['a', [1]]]));
|
|
|
|
|
expect(known.extensions?.[0]?.known).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('compares the tlock stanza with the pinned profile', () => {
|
|
|
|
|
const round = report(replaceText(timeOnly, '-> tlock 2000 ', '-> tlock 2001 '));
|
|
|
|
|
expect(round.failure?.code).toBe('ERR_ROUND_MISMATCH');
|
|
|
|
|
expect(round.unlock?.rfc3339).toBe('2023-08-23T16:49:24Z');
|
|
|
|
|
expect(round.tlock).toEqual([
|
|
|
|
|
{ label: 'Argumentos', found: '2', expected: '2', match: true },
|
|
|
|
|
{ label: 'Ronda', found: '2001', expected: '2000', match: false },
|
|
|
|
|
{
|
|
|
|
|
label: 'Cadena (chain hash)',
|
|
|
|
|
found: '52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971',
|
|
|
|
|
expected: '52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971',
|
|
|
|
|
match: true,
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
const noChain = report(frame({ ...parts, sealed: replaceText(parts.sealed, ' 52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971', '') }));
|
|
|
|
|
expect(noChain.tlock?.map((c) => [c.found, c.match])).toEqual([
|
|
|
|
|
['1', false],
|
|
|
|
|
['2000', true],
|
|
|
|
|
[undefined, false],
|
|
|
|
|
]);
|
|
|
|
|
const noTlock = report(frame({ ...parts, sealed: replaceText(parts.sealed, '-> tlock', '-> TLOCK') }));
|
|
|
|
|
expect(noTlock.failure?.step).toBe(5);
|
|
|
|
|
expect(noTlock.outerStanzas?.map((s) => s.type)).toEqual(['TLOCK']);
|
|
|
|
|
expect(noTlock.tlock).toBeUndefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('keeps what the framing steps found', () => {
|
|
|
|
|
const r = report(timeOnly.subarray(0, 100));
|
|
|
|
|
expect(r.failure).toMatchObject({ step: 3, code: 'ERR_INTEGRITY' });
|
|
|
|
|
expect(r.prelude).toMatchObject({ publicHeaderLen: 159, sealedControlLen: 482, payloadOffset: 657 });
|
|
|
|
|
expect(r.prelude?.payloadLength).toBeUndefined();
|
|
|
|
|
expect(r.capsule).toBeUndefined();
|
|
|
|
|
expect(r.extensions).toBeUndefined();
|
|
|
|
|
const empty = report(new Uint8Array(0));
|
|
|
|
|
expect(empty.failure).toMatchObject({ step: 1, code: 'ERR_INVALID_MAGIC' });
|
|
|
|
|
expect(empty.prelude).toBeUndefined();
|
|
|
|
|
expect(empty.steps.filter((s) => s.state === 'not-run')).toHaveLength(7);
|
|
|
|
|
// An unknown profile: the header decoded, no profile, no comparison.
|
|
|
|
|
const other = report(frame({ ...parts, header: header({ dk: t(dkRound(2000, 'datekeys:evmnet:v1')) }) }));
|
|
|
|
|
expect(other.failure?.code).toBe('ERR_UNKNOWN_PROFILE');
|
|
|
|
|
expect(other.capsule?.network).toBe('datekeys:evmnet:v1');
|
|
|
|
|
expect(other.profile).toBeUndefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('records the prefix that was read', () => {
|
|
|
|
|
const inspection = inspectWith(timeOnly, registry);
|
|
|
|
|
const r = buildReport({ fileName: 'big.dkc', bytes: timeOnly, size: timeOnly.length + 5_000_000, inspection });
|
|
|
|
|
expect(r.readLength).toBe(timeOnly.length);
|
|
|
|
|
expect(r.size).toBe(timeOnly.length + 5_000_000);
|
|
|
|
|
expect(r.prelude!.payloadLength).toBe(timeOnly.length + 5_000_000 - 657);
|
|
|
|
|
});
|
|
|
|
|
});
|