feat: establish standalone Device Core repository
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
createChannelEnvelope,
|
||||
createChannelEnvelopeDecoder,
|
||||
encodeChannelEnvelope,
|
||||
nextReconnectDelay,
|
||||
normalizeCertificateIdentities,
|
||||
normalizeChannelEnvelope,
|
||||
} from "../src/index.mjs";
|
||||
|
||||
const now = "2026-08-11T12:00:00.000Z";
|
||||
|
||||
function envelope(overrides = {}) {
|
||||
return createChannelEnvelope({
|
||||
edgeRegistrationId: "edge:pilot-1",
|
||||
channelGeneration: "generation:pilot-1",
|
||||
trackerSessionId: "channel:control",
|
||||
adapterProfileRef: "channel.control.v1",
|
||||
sequence: 1,
|
||||
eventAt: now,
|
||||
receivedAt: now,
|
||||
messageKind: "channel.hello",
|
||||
correlationId: "correlation:hello-1",
|
||||
payload: { status: "ready" },
|
||||
...overrides,
|
||||
}, { direction: "edge-to-core" });
|
||||
}
|
||||
|
||||
test("round-trips a bounded versioned Edge envelope", () => {
|
||||
const decoder = createChannelEnvelopeDecoder({ direction: "edge-to-core" });
|
||||
const encoded = encodeChannelEnvelope(envelope(), {
|
||||
direction: "edge-to-core",
|
||||
});
|
||||
const split = Math.floor(encoded.length / 2);
|
||||
|
||||
assert.deepEqual(decoder.push(encoded.subarray(0, split)), []);
|
||||
assert.deepEqual(decoder.push(encoded.subarray(split)), [envelope()]);
|
||||
assert.equal(decoder.bufferedBytes(), 0);
|
||||
decoder.finish();
|
||||
});
|
||||
|
||||
test("fails closed on unknown kinds, payload mismatches and oversized frames", () => {
|
||||
const valid = envelope();
|
||||
assert.throws(() => normalizeChannelEnvelope({
|
||||
...valid,
|
||||
messageKind: "tcp.forward",
|
||||
}, { direction: "edge-to-core" }), /message_kind_invalid/);
|
||||
assert.throws(() => normalizeChannelEnvelope({
|
||||
...valid,
|
||||
payloadBytes: valid.payloadBytes + 1,
|
||||
}, { direction: "edge-to-core" }), /payload_length_mismatch/);
|
||||
|
||||
const decoder = createChannelEnvelopeDecoder({
|
||||
direction: "edge-to-core",
|
||||
maxEnvelopeBytes: 256,
|
||||
});
|
||||
assert.throws(() => decoder.push(Buffer.alloc(257, 0x61)), /envelope_too_large/);
|
||||
});
|
||||
|
||||
test("uses bounded jittered exponential reconnect delays", () => {
|
||||
assert.equal(nextReconnectDelay(0, {
|
||||
minimumMs: 1000,
|
||||
maximumMs: 30_000,
|
||||
random: () => 1,
|
||||
}), 1000);
|
||||
assert.equal(nextReconnectDelay(5, {
|
||||
minimumMs: 1000,
|
||||
maximumMs: 30_000,
|
||||
random: () => 1,
|
||||
}), 30_000);
|
||||
assert.equal(nextReconnectDelay(5, {
|
||||
minimumMs: 1000,
|
||||
maximumMs: 30_000,
|
||||
random: () => 0,
|
||||
}), 15_000);
|
||||
});
|
||||
|
||||
test("allows exactly one active and at most one staged certificate generation", () => {
|
||||
const activeFingerprint = "AA:".repeat(31) + "AA";
|
||||
const stagedFingerprint = "BB:".repeat(31) + "BB";
|
||||
const identities = normalizeCertificateIdentities([
|
||||
{
|
||||
generationRef: "trust-generation:1",
|
||||
fingerprint: activeFingerprint,
|
||||
status: "active",
|
||||
},
|
||||
{
|
||||
generationRef: "trust-generation:2",
|
||||
fingerprint: stagedFingerprint,
|
||||
status: "staged",
|
||||
},
|
||||
]);
|
||||
assert.equal(identities.length, 2);
|
||||
assert.equal(identities[0].status, "active");
|
||||
assert.equal(identities[1].status, "staged");
|
||||
assert.throws(() => normalizeCertificateIdentities([
|
||||
{ ...identities[0], status: "staged" },
|
||||
identities[1],
|
||||
]), /active_certificate_identity_invalid/);
|
||||
assert.throws(() => normalizeCertificateIdentities([
|
||||
identities[0],
|
||||
{ ...identities[1], status: "active" },
|
||||
]), /active_certificate_identity_invalid/);
|
||||
});
|
||||
Reference in New Issue
Block a user