feat(server): reclassify undeliverable targeted-peer as no_recipient failure (#18)
sendFederatedCallStart now treats a 200-with-undeliverable-messageId as a peer-failure instead of unconditional success. Feeds the existing failures[] array and terminal-determination machinery from #16. New sendFederatedCallStartForTest export mirrors the existing handleDm*ForTest pattern. TDD — three tests cover single-peer terminal no_recipient, group-DM mixed delivered+undeliverable non-terminal, and the happy-path (empty undeliverable → no event). Also hardens sendCallRelay's response parse: validates undeliverable is an Array and entries are well-shaped, logs protocol drift at warn/debug rather than silently falling back to old-peer semantics.
This commit is contained in:
@@ -644,10 +644,18 @@ export async function sendCallRelay(
|
||||
// omit the field; treat as empty. Body shape: FederationRelayResponse.
|
||||
let undeliverable: string[] = [];
|
||||
try {
|
||||
const body = (await res.json()) as { undeliverable?: Array<{ messageId: string }> };
|
||||
undeliverable = body.undeliverable?.map(u => u.messageId) ?? [];
|
||||
} catch {
|
||||
// Body missing or unparseable — assume old-format response.
|
||||
const responseBody = (await res.json()) as { undeliverable?: unknown };
|
||||
if (Array.isArray(responseBody.undeliverable)) {
|
||||
undeliverable = responseBody.undeliverable
|
||||
.filter((u): u is { messageId: string } =>
|
||||
typeof u === 'object' && u !== null && typeof (u as { messageId?: unknown }).messageId === 'string',
|
||||
)
|
||||
.map(u => u.messageId);
|
||||
} else if (responseBody.undeliverable !== undefined) {
|
||||
console.warn('[federation] sendCallRelay: peer returned non-array undeliverable, ignoring:', targetPeerOrigin);
|
||||
}
|
||||
} catch (err) {
|
||||
console.debug('[federation] sendCallRelay: response body unparseable, treating as old-format:', targetPeerOrigin, err);
|
||||
}
|
||||
return { ok: true, undeliverable };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user