/** * DMP-11 — Response presence piggyback. * * `ChunkResponsePayload` includes `presence` (the responder's full * inventory bitset) and `generation`; receivers update their peer * index from this payload without requiring a separate `cd:advertise`. */ import { describe, expect, it } from "@suaveplan/testing/runner"; import { chunkIdsToRawBitset, decodeFramedBitset, decodeResponsePayload, encodeFramedBitset, encodeResponsePayload, GenerationTracker, rawBitsetToChunkIds, } from "../src/index.js"; import type { ChunkResponsePayload } from "../src/types/protocol.types.js"; const FILE_ID = "c".repeat(64); const PEER = "responder"; describe("DMP-11 — presence piggyback", () => { it("cd:response payload updates peer-index without cd:advertise", { timeout: 5000, }, () => { const inventory = [0, 5, 17, 99]; const presence = encodeFramedBitset( chunkIdsToRawBitset(inventory, 100), 100 ); const payload: ChunkResponsePayload = { fileId: FILE_ID, chunks: [ { chunkId: 0, bytes: new Uint8Array([0xa1]) }, { chunkId: 5, bytes: new Uint8Array([0xa2]) }, ], presence, generation: 11, }; const wire = encodeResponsePayload(payload); const decoded = decodeResponsePayload(wire); expect(decoded.generation).toBe(11); // Receiver decodes the piggybacked bitset and updates its // peer-index for (responder, FILE_ID) — no cd:advertise needed. const decodedPresence = decodeFramedBitset(decoded.presence); const recoveredInventory = rawBitsetToChunkIds( decodedPresence.raw, decodedPresence.bitLength ); expect(recoveredInventory).toEqual(inventory); const tracker = new GenerationTracker(); const observation = tracker.observe(PEER, FILE_ID, decoded.generation); expect(observation.accept).toBe(true); expect(tracker.peek(PEER, FILE_ID)).toBe(11); }); it("piggyback applies even on partial responses (chunks.length < requested)", { timeout: 5000, }, () => { const presence = encodeFramedBitset( chunkIdsToRawBitset([3, 7], 16), 16 ); const partial: ChunkResponsePayload = { fileId: FILE_ID, chunks: [{ chunkId: 3, bytes: new Uint8Array([0xc0]) }], presence, generation: 4, }; const wire = encodeResponsePayload(partial); const round = decodeResponsePayload(wire); expect(round.chunks.length).toBe(1); expect(round.presence).toEqual(presence); }); });