mirror of
https://github.com/sinanyuntu/trade-message-center.git
synced 2026-09-17 13:22:11 +08:00
303 lines
11 KiB
JavaScript
303 lines
11 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { createSendObservationCorrelator } from "../src/onetalk/main-page/message-observer/send-observation.ts";
|
|
|
|
const completeSent = (overrides = {}) => ({
|
|
messageType: "new",
|
|
upstreamType: 1,
|
|
messageId: "message-1",
|
|
conversationId: "conversation-1",
|
|
senderId: "seller@icbu",
|
|
direction: "sent",
|
|
sentAtMs: Date.now(),
|
|
content: { version: 1, kind: "text", text: "hello" },
|
|
participantIds: ["buyer@icbu", "seller@icbu"],
|
|
readStatus: 0,
|
|
messageStatus: 1,
|
|
unreadCount: 0,
|
|
...overrides,
|
|
});
|
|
|
|
test("confirms only a complete sent message from the parsed observer batch", async () => {
|
|
const correlator = createSendObservationCorrelator(100);
|
|
const calls = [];
|
|
const resultPromise = correlator.execute(
|
|
"conversation-1",
|
|
"hello",
|
|
{ source: "test" },
|
|
(input) => {
|
|
calls.push(input);
|
|
return { opId: "accepted-locally" };
|
|
},
|
|
);
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
const observed = completeSent();
|
|
correlator.observe([observed]);
|
|
|
|
const { messageType: _messageType, upstreamType: _upstreamType, ...confirmed } = observed;
|
|
assert.deepEqual(await resultPromise, { status: "confirmed_sent", message: confirmed });
|
|
assert.deepEqual(calls, [
|
|
{
|
|
cid: "conversation-1",
|
|
conversationCode: "conversation-1",
|
|
content: "hello",
|
|
ext: { source: "test" },
|
|
},
|
|
]);
|
|
});
|
|
|
|
test("maps SDK exceptions and observer timeout to delivery_unknown without retry", async () => {
|
|
const failed = createSendObservationCorrelator(100);
|
|
assert.deepEqual(
|
|
await failed.execute("conversation-1", "hello", undefined, () =>
|
|
Promise.reject(new Error("offline")),
|
|
),
|
|
{ status: "delivery_unknown", reason: "send_connection_lost" },
|
|
);
|
|
|
|
const timedOut = createSendObservationCorrelator(1);
|
|
let sendCount = 0;
|
|
assert.deepEqual(
|
|
await timedOut.execute("conversation-1", "hello", undefined, () => {
|
|
sendCount += 1;
|
|
return undefined;
|
|
}),
|
|
{ status: "delivery_unknown", reason: "send_state_lost" },
|
|
);
|
|
assert.equal(sendCount, 1);
|
|
});
|
|
|
|
test("uses a returned message ID before applying the timestamp window", async () => {
|
|
const correlator = createSendObservationCorrelator(30);
|
|
const resultPromise = correlator.execute("conversation-1", "different", undefined, () => ({
|
|
messageId: "message-1",
|
|
}));
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
const observed = completeSent({
|
|
content: { version: 1, kind: "text", text: "different" },
|
|
sentAtMs: Date.now() + 60_000,
|
|
});
|
|
correlator.observe([observed]);
|
|
const { messageType: _messageType, upstreamType: _upstreamType, ...confirmed } = observed;
|
|
assert.deepEqual(await resultPromise, { status: "confirmed_sent", message: confirmed });
|
|
|
|
const mismatch = createSendObservationCorrelator(5);
|
|
const mismatchPromise = mismatch.execute("conversation-1", "hello", undefined, () => ({
|
|
messageId: "not-message-1",
|
|
}));
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
mismatch.observe([completeSent()]);
|
|
assert.deepEqual(await mismatchPromise, {
|
|
status: "delivery_unknown",
|
|
reason: "send_state_lost",
|
|
});
|
|
});
|
|
|
|
test("confirms a normalized text message after a candidate ID match", async () => {
|
|
const correlator = createSendObservationCorrelator(100);
|
|
const resultPromise = correlator.execute("conversation-1", "hello", undefined, () => ({
|
|
id: "message-1",
|
|
}));
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
const observed = completeSent({ sentAtMs: Date.now() - 60_000 });
|
|
correlator.observe([observed]);
|
|
const result = await resultPromise;
|
|
assert.equal(result.status, "confirmed_sent");
|
|
assert.equal(result.message.sentAtMs, observed.sentAtMs);
|
|
});
|
|
|
|
test("matches a .PNM SDK send result to its canonical observed message ID", async () => {
|
|
const correlator = createSendObservationCorrelator(100);
|
|
const canonicalId = "4012345670429";
|
|
const resultPromise = correlator.execute("conversation-1", "hello", undefined, () => ({
|
|
messageId: `${canonicalId}.PNM`,
|
|
}));
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
const observed = completeSent({ messageId: canonicalId });
|
|
|
|
correlator.observe([observed]);
|
|
|
|
assert.equal((await resultPromise).status, "confirmed_sent");
|
|
});
|
|
|
|
test("uses a five-second window and never confirms a pre-send observation", async () => {
|
|
const beforeSend = createSendObservationCorrelator(20);
|
|
const beforeSendResult = beforeSend.execute(
|
|
"conversation-1",
|
|
"hello",
|
|
undefined,
|
|
() => undefined,
|
|
);
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
beforeSend.observe([completeSent({ sentAtMs: Date.now() - 1_000 })]);
|
|
assert.deepEqual(await beforeSendResult, {
|
|
status: "delivery_unknown",
|
|
reason: "send_state_lost",
|
|
});
|
|
|
|
const outsideWindow = createSendObservationCorrelator(20);
|
|
const outsideWindowResult = outsideWindow.execute(
|
|
"conversation-1",
|
|
"hello",
|
|
undefined,
|
|
() => undefined,
|
|
);
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
outsideWindow.observe([completeSent({ sentAtMs: Date.now() + 6_000 })]);
|
|
assert.deepEqual(await outsideWindowResult, {
|
|
status: "delivery_unknown",
|
|
reason: "send_state_lost",
|
|
});
|
|
});
|
|
|
|
test("does not confirm received, wrong-conversation, incomplete, or ambiguous observations", async () => {
|
|
const correlator = createSendObservationCorrelator(10);
|
|
const first = correlator.execute("conversation-1", "hello", undefined, () => undefined);
|
|
const second = correlator.execute("conversation-1", "hello", undefined, () => undefined);
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
correlator.observe([
|
|
completeSent({ direction: "received" }),
|
|
completeSent({ conversationId: "conversation-2" }),
|
|
completeSent({ messageStatus: undefined }),
|
|
completeSent(),
|
|
]);
|
|
assert.deepEqual(await first, { status: "delivery_unknown", reason: "send_ambiguous" });
|
|
assert.deepEqual(await second, { status: "delivery_unknown", reason: "send_ambiguous" });
|
|
});
|
|
|
|
test("confirms no-dimension live images from relation metadata and fails closed on same-image ambiguity", async () => {
|
|
const expected = { sizeBytes: 123, md5: "hash-1" };
|
|
const image = () =>
|
|
completeSent({
|
|
sentAtMs: Date.now() + 1,
|
|
content: {
|
|
version: 1,
|
|
kind: "image",
|
|
fileId: "file-1",
|
|
extension: "jpg",
|
|
sizeBytes: 123,
|
|
isOriginal: true,
|
|
md5: "hash-1",
|
|
previewUrl: null,
|
|
urlScope: "onetalk_session",
|
|
},
|
|
});
|
|
const candidate = createSendObservationCorrelator(100);
|
|
const byCandidate = candidate.executeImage(
|
|
"conversation-1",
|
|
expected,
|
|
() => ({ id: "message-1" }),
|
|
{ timeoutMs: 100 },
|
|
);
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
candidate.observe([image()]);
|
|
assert.equal((await byCandidate).status, "confirmed_sent");
|
|
|
|
const wrongDigest = createSendObservationCorrelator(10);
|
|
const byWrongDigest = wrongDigest.executeImage("conversation-1", expected, () => undefined, {
|
|
timeoutMs: 10,
|
|
});
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
const mismatchedImage = image();
|
|
wrongDigest.observe([
|
|
{
|
|
...mismatchedImage,
|
|
content: { ...mismatchedImage.content, md5: "other-hash" },
|
|
},
|
|
]);
|
|
assert.deepEqual(await byWrongDigest, { status: "delivery_unknown", reason: "send_timeout" });
|
|
|
|
const ambiguous = createSendObservationCorrelator(100);
|
|
const first = ambiguous.executeImage("conversation-1", expected, () => undefined, {
|
|
timeoutMs: 100,
|
|
});
|
|
const second = ambiguous.executeImage("conversation-1", expected, () => undefined, {
|
|
timeoutMs: 100,
|
|
});
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
ambiguous.observe([image()]);
|
|
assert.deepEqual(await first, { status: "delivery_unknown", reason: "send_ambiguous" });
|
|
assert.deepEqual(await second, { status: "delivery_unknown", reason: "send_ambiguous" });
|
|
});
|
|
|
|
test("confirms files only by a unique relation fingerprint or candidate message ID", async () => {
|
|
const expected = {
|
|
fileName: "quotation.zip",
|
|
extension: "zip",
|
|
sizeBytes: 456,
|
|
md5: "hash-1",
|
|
fileId: "file-1",
|
|
parentId: "parent-1",
|
|
};
|
|
const file = (overrides = {}) =>
|
|
completeSent({
|
|
sentAtMs: Date.now() + 1,
|
|
content: {
|
|
version: 1,
|
|
kind: "file",
|
|
fileId: "file-1",
|
|
parentId: "parent-1",
|
|
fileName: "quotation.zip",
|
|
extension: "zip",
|
|
sizeBytes: 456,
|
|
md5: "hash-1",
|
|
previewUrl: null,
|
|
thumbnailUrl: null,
|
|
downloadUrl: null,
|
|
downloadState: "not_provided",
|
|
urlScope: "onetalk_session",
|
|
},
|
|
...overrides,
|
|
});
|
|
const candidate = createSendObservationCorrelator(100);
|
|
const candidateResult = candidate.executeFile(
|
|
"conversation-1",
|
|
expected,
|
|
() => ({ id: "message-1" }),
|
|
{ timeoutMs: 100 },
|
|
);
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
candidate.observe([
|
|
file({
|
|
messageId: "message-1",
|
|
content: { ...file().content, fileName: "different-name.zip", sizeBytes: 999 },
|
|
}),
|
|
]);
|
|
assert.equal((await candidateResult).status, "confirmed_sent");
|
|
|
|
const fingerprint = createSendObservationCorrelator(100);
|
|
const fingerprintResult = fingerprint.executeFile("conversation-1", expected, () => undefined, {
|
|
timeoutMs: 100,
|
|
});
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
fingerprint.observe([file()]);
|
|
assert.equal((await fingerprintResult).status, "confirmed_sent");
|
|
|
|
const wrongConversation = createSendObservationCorrelator(10);
|
|
const wrongConversationResult = wrongConversation.executeFile(
|
|
"conversation-1",
|
|
expected,
|
|
() => undefined,
|
|
{ timeoutMs: 10 },
|
|
);
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
wrongConversation.observe([{ ...file(), conversationId: "conversation-2" }]);
|
|
assert.deepEqual(await wrongConversationResult, {
|
|
status: "delivery_unknown",
|
|
reason: "send_timeout",
|
|
});
|
|
|
|
const ambiguous = createSendObservationCorrelator(100);
|
|
const first = ambiguous.executeFile("conversation-1", expected, () => undefined, {
|
|
timeoutMs: 100,
|
|
});
|
|
const second = ambiguous.executeFile("conversation-1", expected, () => undefined, {
|
|
timeoutMs: 100,
|
|
});
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
ambiguous.observe([file()]);
|
|
assert.deepEqual(await first, { status: "delivery_unknown", reason: "send_ambiguous" });
|
|
assert.deepEqual(await second, { status: "delivery_unknown", reason: "send_ambiguous" });
|
|
});
|