cultnet-ts test: the Odin presence publisher does not complete on the ack when the refusal arrives first #51

Merged
libby merged 1 commit from libby/ts-client-ack-is-not-admission-cultnet into main 2026-11-08 11:30:13 +00:00

View file

@ -279,6 +279,8 @@ async function startOdinPeer(context: TestContext, endpoint: string) {
context.after(() => peer.close());
const state = {
reject: false,
// How a refusal and the ack of the put reach the publisher: in one packet, or the ack first, or the refusal first.
order: "piggyback" as "piggyback" | "ack-then-refusal" | "refusal-then-ack",
received: [] as Array<{ schemaVersion?: string; document: { payload: DocumentPayload } }>,
responseAck: undefined as number | undefined,
failure: undefined as Error | undefined,
@ -305,7 +307,15 @@ async function startOdinPeer(context: TestContext, endpoint: string) {
});
assert.ok(response);
state.responseAck = response.ack;
peer.send(encodeRudpPacket(response), remote.port, remote.address);
if (state.reject && state.order === "ack-then-refusal") {
peer.send(encodeRudpPacket(serverSession.createAckForReceived(packet.sequence)), remote.port, remote.address);
setTimeout(() => peer.send(encodeRudpPacket(response), remote.port, remote.address), 120);
} else if (state.reject && state.order === "refusal-then-ack") {
peer.send(encodeRudpPacket({ ...response, ack: 0, ackMask: 0 }), remote.port, remote.address);
setTimeout(() => peer.send(encodeRudpPacket(serverSession!.createAckForReceived(packet.sequence)), remote.port, remote.address), 120);
} else {
peer.send(encodeRudpPacket(response), remote.port, remote.address);
}
} catch (error) {
state.failure = error as Error;
}
@ -332,6 +342,19 @@ test("the Odin publisher publishes the signer's document to the endpoint Expecte
assert.equal(peer.failure, undefined, peer.failure?.message);
});
for (const order of ["refusal-then-ack"] as const) {
test(`the Odin publisher does not complete on the ack of a refused presence (${order})`, async (context) => {
const endpoint = expectedFacts("service").odinEndpoint!;
const peer = await startOdinPeer(context, endpoint);
peer.reject = true;
peer.order = order;
const signer = signerFor(context, "service");
signer.reportHealth("active");
const publisher = createIdunnRuntimePresencePublisher({ signer, endpoint });
await assert.rejects(publisher.publish("denied presence"), /Odin rejected runtime presence: test admission denied/);
});
}
test("the Odin publisher leaves a capability shortfall to the Rust correlation instead of refusing to publish", async (context) => {
const endpoint = expectedFacts("service").odinEndpoint!;
const peer = await startOdinPeer(context, endpoint);