mirror of
https://github.com/sinanyuntu/trade-message-center.git
synced 2026-09-17 13:22:11 +08:00
1948 lines
67 KiB
TypeScript
1948 lines
67 KiB
TypeScript
// 验证 OneTalk 协议和 Mock 授权契约
|
|
|
|
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { runInNewContext } from "node:vm";
|
|
|
|
import {
|
|
ONETALK_CONTACT_PROFILE_MAX_FRAME_BYTES,
|
|
ONETALK_CONVERSATIONS_DISCOVERED_MAX_ENTRIES_PER_FRAME,
|
|
ONETALK_CONVERSATIONS_DISCOVERED_MAX_FRAGMENTS,
|
|
ONETALK_MESSAGE_BATCH_MAX_ITEMS,
|
|
ONETALK_MESSAGE_BATCH_MAX_FRAME_BYTES,
|
|
ONETALK_CONTENT_VERSION,
|
|
ONETALK_ERROR_CODES,
|
|
ONETALK_MAX_MEDIA_SIZE_BYTES,
|
|
ONETALK_MESSAGES_ROUTE,
|
|
ONETALK_HISTORY_ROUTE,
|
|
ONETALK_PROTOCOL_VERSION,
|
|
cloneOneTalkBuyerFact,
|
|
createMockAuthorizationReader,
|
|
createOneTalkAnchorSnapshotFrame,
|
|
createOneTalkConversationAckFrame,
|
|
createOneTalkConversationDiscoveredFrame,
|
|
createOneTalkConversationUpdatedFrame,
|
|
createOneTalkContactProfileObservedFrame,
|
|
createOneTalkBuyerFactsObservedFrame,
|
|
createOneTalkConversationsAckFrame,
|
|
createOneTalkConversationsDiscoveredFrame,
|
|
createOneTalkMessageAckFrame,
|
|
createOneTalkMessagesAckFrame,
|
|
createOneTalkMessagesObservedFrame,
|
|
createOneTalkPluginStatusFrame,
|
|
createOneTalkSyncStatusFrame,
|
|
decodeMindAuthorizationResponse,
|
|
decodeOneTalkFrame,
|
|
decodeOneTalkMessageContent,
|
|
isOneTalkAvatarUrl,
|
|
isOneTalkBuyerFact,
|
|
isOneTalkBuyerFactCompanyWebsite,
|
|
isOneTalkBuyerFactContactDetailsSources,
|
|
isCenterConversation,
|
|
isOneTalkContactProfile,
|
|
isOneTalkOutboundContent,
|
|
isPlainRecord,
|
|
isOneTalkCenterMessage,
|
|
isNextOneTalkConversationsDiscoveredFragment,
|
|
isOneTalkMessage,
|
|
isOneTalkMessageContent,
|
|
type OneTalkMindScope,
|
|
type MockAuthorizationRecord,
|
|
type OneTalkPluginScope,
|
|
type OneTalkFrame,
|
|
type OneTalkBuyerFact,
|
|
oneTalkFrameWireByteLength,
|
|
} from "../src/index.ts";
|
|
|
|
const pluginScope: OneTalkPluginScope = {
|
|
channelAccountId: "onetalk-account-1",
|
|
deviceId: "device-1",
|
|
};
|
|
|
|
const mindScope: OneTalkMindScope = {
|
|
mindUserId: "mind-user-1",
|
|
workspaceId: "workspace-1",
|
|
channelAccountId: pluginScope.channelAccountId,
|
|
};
|
|
|
|
test("accepts only current-realm plain records at canonical boundaries", () => {
|
|
class CustomRecord {
|
|
public readonly value = 1;
|
|
}
|
|
const nullPrototype = Object.assign(Object.create(null), { value: 1 });
|
|
const crossRealm = runInNewContext("({ value: 1 })");
|
|
|
|
assert.equal(isPlainRecord({ value: 1 }), true);
|
|
assert.equal(isPlainRecord(nullPrototype), true);
|
|
assert.equal(isPlainRecord([]), false);
|
|
assert.equal(isPlainRecord(null), false);
|
|
assert.equal(isPlainRecord("value"), false);
|
|
assert.equal(isPlainRecord(new CustomRecord()), false);
|
|
assert.equal(isPlainRecord(crossRealm), false);
|
|
|
|
const customFrame = Object.assign(Object.create({}), {
|
|
...frameBase,
|
|
type: "heartbeat",
|
|
payload: { sentAtMs: 1 },
|
|
});
|
|
assert.deepEqual(decodeOneTalkFrame(customFrame), {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
assert.deepEqual(
|
|
decodeMindAuthorizationResponse(
|
|
Object.assign(Object.create({}), {
|
|
binding: "binding-1",
|
|
authorizationVersion: "version-1",
|
|
permissions: ["read"],
|
|
mindScope,
|
|
}),
|
|
),
|
|
{ ok: false },
|
|
);
|
|
});
|
|
|
|
const frameBase = {
|
|
protocolVersion: ONETALK_PROTOCOL_VERSION,
|
|
connectionType: "plugin" as const,
|
|
requestId: "request-1",
|
|
scope: pluginScope,
|
|
};
|
|
|
|
const textContent = {
|
|
version: ONETALK_CONTENT_VERSION,
|
|
kind: "text" as const,
|
|
text: "hello",
|
|
};
|
|
|
|
const jpegContent = {
|
|
version: ONETALK_CONTENT_VERSION,
|
|
kind: "image" as const,
|
|
fileId: "image-jpeg-1",
|
|
extension: "jpg",
|
|
sizeBytes: 263_333,
|
|
isOriginal: true,
|
|
md5: "f28f1f8f4b760d5e2a89c3f0f83f3f68",
|
|
previewUrl:
|
|
"https://clouddisk.alibaba.com/file/redirectFileUrl.htm?appkey=onetalk&fileAction=imagePreview&id=image-jpeg-1&parentId=parent-1&scene=im&secOperateAliId=operation-1",
|
|
urlScope: "onetalk_session" as const,
|
|
};
|
|
|
|
const zipContent = {
|
|
version: ONETALK_CONTENT_VERSION,
|
|
kind: "file" as const,
|
|
fileId: "file-zip-1",
|
|
parentId: "parent-1",
|
|
fileName: "archive.zip",
|
|
extension: "zip",
|
|
sizeBytes: 8_192,
|
|
md5: "a614ee55b22a6545c2bc7342898c6a6f",
|
|
previewUrl: null,
|
|
thumbnailUrl:
|
|
"https://clouddisk.alibaba.com/file/videoThumb.htm?appkey=onetalk&id=file-zip-1&parentId=parent-1&scene=im&secOperateAliId=operation-2",
|
|
downloadUrl:
|
|
"https://clouddisk.alibaba.com/file/redirectFileUrl.htm?appkey=onetalk&fileAction=download&id=file-zip-1&parentId=parent-1&scene=im&secOperateAliId=operation-2",
|
|
downloadState: "available" as const,
|
|
urlScope: "onetalk_session" as const,
|
|
};
|
|
|
|
const pdfContent = {
|
|
version: ONETALK_CONTENT_VERSION,
|
|
kind: "file" as const,
|
|
fileId: "file-pdf-1",
|
|
parentId: "parent-1",
|
|
fileName: "quotation.pdf",
|
|
extension: "pdf",
|
|
sizeBytes: 64_512,
|
|
md5: "c56f2d90b0e701d6ffda5505b661b5d1",
|
|
previewUrl:
|
|
"https://clouddisk.alibaba.com/file/redirectFileUrl.htm?appkey=onetalk&fileAction=officePreview&id=file-pdf-1&parentId=parent-1&scene=im&secOperateAliId=operation-3",
|
|
thumbnailUrl:
|
|
"https://clouddisk.alibaba.com/file/videoThumb.htm?appkey=onetalk&id=file-pdf-1&parentId=parent-1&scene=im&secOperateAliId=operation-3",
|
|
downloadUrl: null,
|
|
downloadState: "not_provided" as const,
|
|
urlScope: "onetalk_session" as const,
|
|
};
|
|
|
|
const businessCardContent = {
|
|
version: ONETALK_CONTENT_VERSION,
|
|
kind: "business_card" as const,
|
|
};
|
|
|
|
const businessCardViewContent = {
|
|
...businessCardContent,
|
|
contactName: "Buyer Name",
|
|
companyName: "Buyer Company",
|
|
countryCode: "CN",
|
|
avatarUrl: "https://cdn.example.test/avatar/buyer.jpg",
|
|
};
|
|
|
|
const inquiryContent = {
|
|
version: ONETALK_CONTENT_VERSION,
|
|
kind: "inquiry" as const,
|
|
};
|
|
|
|
const orderContent = {
|
|
version: ONETALK_CONTENT_VERSION,
|
|
kind: "order" as const,
|
|
orderId: "order-1",
|
|
bizCode: -42,
|
|
contractId: "contract-1",
|
|
id: "id-1",
|
|
tenant: "tenant-1",
|
|
orderAmount: 12.5,
|
|
orderAmountCurrency: "USD",
|
|
paymentAmount: 10,
|
|
paymentAmountCurrency: "USD",
|
|
statusMessageKey: "order.pending_payment",
|
|
actions: [{ name: "pay", messageKey: "order.pay", payStep: "deposit" }],
|
|
};
|
|
|
|
const observedMessage = {
|
|
messageId: "message-1",
|
|
conversationId: "conversation-1",
|
|
senderId: "sender-1",
|
|
direction: "received" as const,
|
|
sentAtMs: 1_700_000_000_000,
|
|
content: textContent,
|
|
participantIds: ["sender-1", "login-user-1"],
|
|
readStatus: 0,
|
|
messageStatus: 1,
|
|
unreadCount: 0,
|
|
};
|
|
|
|
const completeMessage = observedMessage;
|
|
|
|
const centerMessage = {
|
|
messageId: observedMessage.messageId,
|
|
conversationId: observedMessage.conversationId,
|
|
senderId: observedMessage.senderId,
|
|
participantIds: observedMessage.participantIds,
|
|
direction: observedMessage.direction,
|
|
sentAtMs: observedMessage.sentAtMs,
|
|
content: textContent,
|
|
};
|
|
|
|
const centerConversation = {
|
|
conversationId: "conversation-1",
|
|
conversationType: "direct" as const,
|
|
name: "Customer One",
|
|
avatarUrl: null,
|
|
lastContactTimeLong: 1_700_000_000_000,
|
|
messagePreview: "hello",
|
|
};
|
|
|
|
const authorizationRecord: MockAuthorizationRecord = {
|
|
scope: pluginScope,
|
|
mindScope,
|
|
binding: "binding-1",
|
|
permissions: ["read", "send"],
|
|
authorizationVersion: "version-1",
|
|
active: true,
|
|
};
|
|
|
|
const decode = (frame: unknown): OneTalkFrame => {
|
|
const result = decodeOneTalkFrame(frame);
|
|
assert.equal(result.ok, true);
|
|
return result.frame;
|
|
};
|
|
|
|
test("decodes a plugin handshake and preserves the exact scope", () => {
|
|
const frame = decode({
|
|
...frameBase,
|
|
type: "ws.hello",
|
|
payload: {
|
|
binding: "binding-1",
|
|
requestedPermissions: ["read", "send"],
|
|
},
|
|
});
|
|
|
|
assert.equal(frame.type, "ws.hello");
|
|
assert.deepEqual(frame.scope, pluginScope);
|
|
});
|
|
|
|
test("rejects an unknown protocol version with the upgrade error", () => {
|
|
const result = decodeOneTalkFrame({
|
|
...frameBase,
|
|
protocolVersion: 99,
|
|
type: "ws.hello",
|
|
payload: { requestedPermissions: ["read"] },
|
|
});
|
|
|
|
assert.deepEqual(result, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.protocolUpgradeRequired,
|
|
});
|
|
});
|
|
|
|
test("hard-rejects v5 and all other old protocol versions with the upgrade error", () => {
|
|
for (const protocolVersion of [0, 1, 2, 3, 4, 5, 99]) {
|
|
const result = decodeOneTalkFrame({
|
|
...frameBase,
|
|
protocolVersion,
|
|
type: "ws.hello",
|
|
payload: { requestedPermissions: ["read"] },
|
|
});
|
|
|
|
assert.deepEqual(result, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.protocolUpgradeRequired,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("rejects malformed scope and required send correlation", () => {
|
|
const missingDevice = decodeOneTalkFrame({
|
|
...frameBase,
|
|
scope: { ...pluginScope, deviceId: "" },
|
|
type: "ws.hello",
|
|
payload: { requestedPermissions: ["read"] },
|
|
});
|
|
assert.deepEqual(missingDevice, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
|
|
const missingSendRequestId = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "send.request",
|
|
payload: { conversationId: "conversation-1", content: { kind: "text", text: "hello" } },
|
|
});
|
|
assert.deepEqual(missingSendRequestId, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
});
|
|
|
|
test("rejects unsupported frame types with the stable invalid-message code", () => {
|
|
const result = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "legacy.message",
|
|
payload: {},
|
|
});
|
|
|
|
assert.deepEqual(result, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
});
|
|
|
|
test("decodes only exact direct discovery with its commit ACK and the profile future error", () => {
|
|
const discovered = createOneTalkConversationDiscoveredFrame(frameBase, {
|
|
conversationId: "conversation-1",
|
|
conversationType: "direct",
|
|
lastContactTimeLong: null,
|
|
messagePreview: null,
|
|
});
|
|
const direct = decode(discovered);
|
|
assert.equal(direct.type, "conversation.discovered");
|
|
assert.equal(direct.payload.conversationType, "direct");
|
|
|
|
const acknowledgement = createOneTalkConversationAckFrame(discovered);
|
|
assert.deepEqual(decodeOneTalkFrame(acknowledgement), { ok: true, frame: acknowledgement });
|
|
assert.equal(acknowledgement.requestId, discovered.requestId);
|
|
assert.deepEqual(acknowledgement.scope, discovered.scope);
|
|
|
|
for (const payload of [
|
|
{ conversationId: "conversation-1" },
|
|
{
|
|
conversationId: "conversation-1",
|
|
conversationType: "group",
|
|
lastContactTimeLong: null,
|
|
messagePreview: null,
|
|
},
|
|
{
|
|
conversationId: "conversation-1",
|
|
conversationType: "direct",
|
|
lastContactTimeLong: -1,
|
|
messagePreview: null,
|
|
},
|
|
{
|
|
conversationId: "conversation-1",
|
|
conversationType: "direct",
|
|
lastContactTimeLong: 1.5,
|
|
messagePreview: null,
|
|
},
|
|
]) {
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({ ...frameBase, type: "conversation.discovered", payload }),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
}
|
|
|
|
const futureError = decode({
|
|
...frameBase,
|
|
type: "ws.error",
|
|
payload: { code: ONETALK_ERROR_CODES.profileObservedAtFuture },
|
|
});
|
|
assert.equal(futureError.type, "ws.error");
|
|
assert.equal(futureError.payload.code, ONETALK_ERROR_CODES.profileObservedAtFuture);
|
|
});
|
|
|
|
test("strictly decodes logical conversation discovery batches, fragments, and commit ACKs", () => {
|
|
const entries = [
|
|
{ conversationId: "conversation-1", lastContactTimeLong: null, messagePreview: null },
|
|
{
|
|
conversationId: "conversation-2",
|
|
lastContactTimeLong: 1_700_000_000_000,
|
|
messagePreview: "latest request",
|
|
},
|
|
];
|
|
const discovered = createOneTalkConversationsDiscoveredFrame(frameBase, {
|
|
batchId: "batch-1",
|
|
entries,
|
|
});
|
|
assert.deepEqual(decodeOneTalkFrame(discovered), { ok: true, frame: discovered });
|
|
|
|
const emptyDiscovered = createOneTalkConversationsDiscoveredFrame(frameBase, {
|
|
batchId: "batch-empty",
|
|
entries: [],
|
|
});
|
|
assert.deepEqual(decodeOneTalkFrame(emptyDiscovered), { ok: true, frame: emptyDiscovered });
|
|
const emptyAcknowledgement = createOneTalkConversationsAckFrame(emptyDiscovered);
|
|
assert.deepEqual(decodeOneTalkFrame(emptyAcknowledgement), {
|
|
ok: true,
|
|
frame: emptyAcknowledgement,
|
|
});
|
|
|
|
const acknowledgement = createOneTalkConversationsAckFrame(discovered);
|
|
assert.deepEqual(decodeOneTalkFrame(acknowledgement), { ok: true, frame: acknowledgement });
|
|
assert.equal(acknowledgement.requestId, discovered.requestId);
|
|
assert.deepEqual(acknowledgement.scope, discovered.scope);
|
|
assert.equal(acknowledgement.payload.batchId, discovered.payload.batchId);
|
|
|
|
const firstFragment = createOneTalkConversationsDiscoveredFrame(frameBase, {
|
|
batchId: "batch-fragmented",
|
|
entries: [entries[0]],
|
|
fragment: { sequence: 0, isFinal: false },
|
|
});
|
|
const finalFragment = createOneTalkConversationsDiscoveredFrame(frameBase, {
|
|
batchId: "batch-fragmented",
|
|
entries: [entries[1]],
|
|
fragment: { sequence: 1, isFinal: true },
|
|
});
|
|
assert.equal(decodeOneTalkFrame(firstFragment).ok, true);
|
|
assert.equal(decodeOneTalkFrame(finalFragment).ok, true);
|
|
assert.equal(isNextOneTalkConversationsDiscoveredFragment(firstFragment, finalFragment), true);
|
|
|
|
const gap = createOneTalkConversationsDiscoveredFrame(frameBase, {
|
|
batchId: "batch-fragmented",
|
|
entries: [entries[1]],
|
|
fragment: { sequence: 2, isFinal: true },
|
|
});
|
|
const afterTerminal = createOneTalkConversationsDiscoveredFrame(frameBase, {
|
|
batchId: "batch-fragmented",
|
|
entries: [entries[1]],
|
|
fragment: { sequence: 2, isFinal: false },
|
|
});
|
|
assert.equal(isNextOneTalkConversationsDiscoveredFragment(firstFragment, gap), false);
|
|
assert.equal(isNextOneTalkConversationsDiscoveredFragment(finalFragment, afterTerminal), false);
|
|
});
|
|
|
|
test("fails closed on malformed, duplicate, over-count, over-budget, and wrong-direction discovery frames", () => {
|
|
const entry = {
|
|
conversationId: "conversation-1",
|
|
lastContactTimeLong: null,
|
|
messagePreview: null,
|
|
};
|
|
const discovered = createOneTalkConversationsDiscoveredFrame(frameBase, {
|
|
batchId: "batch-1",
|
|
entries: [entry],
|
|
});
|
|
|
|
const invalidPayloads = [
|
|
{ batchId: "batch-1", entries: [entry, entry] },
|
|
{
|
|
batchId: "batch-1",
|
|
entries: [{ ...entry, conversationType: "group" }],
|
|
},
|
|
{
|
|
batchId: "batch-1",
|
|
entries: [{ ...entry, latestMessageAtMs: null }],
|
|
},
|
|
{
|
|
batchId: "batch-1",
|
|
entries: [{ ...entry, messagePreview: " " }],
|
|
},
|
|
{
|
|
batchId: "batch-1",
|
|
entries: [
|
|
{
|
|
conversationId: "conversation-1",
|
|
lastContactTimeLong: Number.MAX_SAFE_INTEGER + 1,
|
|
messagePreview: null,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
batchId: "batch-1",
|
|
entries: [],
|
|
fragment: { sequence: 0, isFinal: false },
|
|
},
|
|
{
|
|
batchId: "batch-1",
|
|
entries: [entry],
|
|
fragment: { sequence: 0, isFinal: true },
|
|
},
|
|
{
|
|
batchId: "batch-1",
|
|
entries: [entry],
|
|
fragment: { sequence: ONETALK_CONVERSATIONS_DISCOVERED_MAX_FRAGMENTS, isFinal: false },
|
|
},
|
|
{
|
|
batchId: "batch-1",
|
|
entries: [entry],
|
|
fragment: { sequence: 0, isFinal: false, terminal: false },
|
|
},
|
|
];
|
|
for (const payload of invalidPayloads) {
|
|
assert.deepEqual(decodeOneTalkFrame({ ...discovered, payload }), {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
}
|
|
|
|
const overCount = {
|
|
...discovered,
|
|
payload: {
|
|
batchId: "batch-over-count",
|
|
entries: Array.from(
|
|
{ length: ONETALK_CONVERSATIONS_DISCOVERED_MAX_ENTRIES_PER_FRAME + 1 },
|
|
(_, index) => ({
|
|
conversationId: `conversation-${index}`,
|
|
lastContactTimeLong: null,
|
|
messagePreview: null,
|
|
}),
|
|
),
|
|
},
|
|
};
|
|
assert.deepEqual(decodeOneTalkFrame(overCount), {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
|
|
const atWireLimit = {
|
|
...discovered,
|
|
payload: { batchId: "x", entries: [entry] },
|
|
};
|
|
const batchIdLength =
|
|
ONETALK_MESSAGE_BATCH_MAX_FRAME_BYTES - oneTalkFrameWireByteLength(atWireLimit) + 1;
|
|
const withinWireLimit = {
|
|
...atWireLimit,
|
|
payload: { ...atWireLimit.payload, batchId: "x".repeat(batchIdLength) },
|
|
};
|
|
assert.equal(
|
|
oneTalkFrameWireByteLength(withinWireLimit),
|
|
ONETALK_MESSAGE_BATCH_MAX_FRAME_BYTES,
|
|
);
|
|
assert.equal(decodeOneTalkFrame(withinWireLimit).ok, true);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...withinWireLimit,
|
|
payload: { ...withinWireLimit.payload, batchId: `${withinWireLimit.payload.batchId}x` },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...discovered,
|
|
connectionType: "mind_page",
|
|
scope: mindScope,
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
assert.deepEqual(decodeOneTalkFrame({ ...discovered, unexpected: true }), {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
});
|
|
|
|
test("validates observed messages for history, incremental, live, and send confirmation sources", () => {
|
|
for (const observationSource of [
|
|
"history",
|
|
"incremental",
|
|
"live",
|
|
"send_confirmation",
|
|
] as const) {
|
|
const frame = decode({
|
|
...frameBase,
|
|
type: "message.observed",
|
|
payload: { observationSource, message: observedMessage },
|
|
});
|
|
assert.equal(frame.type, "message.observed");
|
|
}
|
|
});
|
|
|
|
test("accepts bounded history batches and rejects invalid batch sources, counts, and ACK results", () => {
|
|
const observed = createOneTalkMessagesObservedFrame(
|
|
{ ...frameBase, connectionType: "plugin", scope: pluginScope },
|
|
"history",
|
|
[observedMessage],
|
|
);
|
|
assert.equal(decodeOneTalkFrame(observed).ok, true);
|
|
|
|
for (const payload of [
|
|
{ observationSource: "live", messages: [observedMessage] },
|
|
{ observationSource: "send_confirmation", messages: [observedMessage] },
|
|
{ observationSource: "history", messages: [] },
|
|
{
|
|
observationSource: "incremental",
|
|
messages: Array.from(
|
|
{ length: ONETALK_MESSAGE_BATCH_MAX_ITEMS + 1 },
|
|
() => observedMessage,
|
|
),
|
|
},
|
|
]) {
|
|
assert.deepEqual(decodeOneTalkFrame({ ...observed, payload }), {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
}
|
|
|
|
const acknowledgement = createOneTalkMessagesAckFrame(
|
|
{ ...frameBase, connectionType: "plugin", scope: pluginScope },
|
|
[{ status: "accepted", conversationId: "conversation-1", messageId: "message-1" }],
|
|
);
|
|
assert.equal(decodeOneTalkFrame(acknowledgement).ok, true);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({ ...acknowledgement, payload: { results: [{ status: "unknown" }] } }),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
});
|
|
|
|
const contactProfile = {
|
|
conversationId: "conversation-1",
|
|
aliId: "2208314000798",
|
|
accountId: "243340382",
|
|
loginId: "hzhago",
|
|
name: "Heena Liu",
|
|
companyName: "Hago",
|
|
countryCode: "CN",
|
|
currentTimeZone: -9,
|
|
serviceType: "cgs",
|
|
avatarUrl: "https://cdn.example.test/avatar/customer-1.jpg",
|
|
observedAtMs: 1_700_000_000_000,
|
|
profileFingerprint: "v1-12345678",
|
|
observationStatus: "confirmed" as const,
|
|
};
|
|
|
|
const buyerFact = {
|
|
conversationId: "conversation-1",
|
|
buyerTags: ["will_loss"],
|
|
buyerFeatures: [],
|
|
tags: {
|
|
state: "confirmed" as const,
|
|
errorCode: null,
|
|
attemptedAtMs: 1_700_000_000_000,
|
|
confirmedAtMs: 1_700_000_000_000,
|
|
},
|
|
features: {
|
|
state: "confirmed" as const,
|
|
errorCode: null,
|
|
attemptedAtMs: 1_700_000_000_000,
|
|
confirmedAtMs: 1_700_000_000_000,
|
|
},
|
|
observedAtMs: 1_700_000_000_000,
|
|
factFingerprint: 'tags=["will_loss"]|features=[]',
|
|
} satisfies OneTalkBuyerFact;
|
|
|
|
test("rejects custom-prototype nested canonical content while accepting null prototypes", () => {
|
|
const customContent = Object.assign(Object.create({}), textContent);
|
|
const nullPrototypeContent = Object.assign(Object.create(null), textContent);
|
|
const customMessage = Object.assign(Object.create({}), observedMessage);
|
|
const customProfile = Object.assign(Object.create({}), contactProfile);
|
|
const customBuyerFact = Object.assign(Object.create({}), buyerFact);
|
|
const customTags = Object.assign(Object.create({}), buyerFact.tags);
|
|
const outboundContent = {
|
|
kind: "image" as const,
|
|
source: {
|
|
downloadUrl: "https://mind.example.test/image.jpg",
|
|
fileName: "image.jpg",
|
|
mimeType: "image/jpeg",
|
|
},
|
|
};
|
|
const customOutboundContent = Object.assign(Object.create({}), outboundContent);
|
|
const customOutboundSource = Object.assign(Object.create({}), outboundContent.source);
|
|
|
|
assert.deepEqual(decodeOneTalkMessageContent(customContent), { ok: false });
|
|
assert.equal(isOneTalkMessageContent(nullPrototypeContent), true);
|
|
assert.equal(isOneTalkMessage(customMessage), false);
|
|
assert.equal(isOneTalkMessage({ ...observedMessage, content: nullPrototypeContent }), true);
|
|
assert.equal(isOneTalkMessage({ ...observedMessage, content: customContent }), false);
|
|
assert.equal(isOneTalkContactProfile(customProfile), false);
|
|
assert.equal(isOneTalkBuyerFact(customBuyerFact), false);
|
|
assert.equal(isOneTalkBuyerFact({ ...buyerFact, tags: customTags }), false);
|
|
assert.equal(isOneTalkOutboundContent(customOutboundContent), false);
|
|
assert.equal(
|
|
isOneTalkOutboundContent({ ...outboundContent, source: customOutboundSource }),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.observed",
|
|
payload: {
|
|
observationSource: "history",
|
|
message: { ...observedMessage, content: customContent },
|
|
},
|
|
}).ok,
|
|
false,
|
|
);
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [customProfile] },
|
|
}).ok,
|
|
false,
|
|
);
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "buyer.facts.observed",
|
|
payload: { facts: [customBuyerFact] },
|
|
}).ok,
|
|
false,
|
|
);
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "send.command",
|
|
sendRequestId: "send-custom-content",
|
|
payload: { conversationId: "conversation-1", content: customOutboundContent },
|
|
}).ok,
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("strictly decodes buyer facts and preserves a confirmed collection after a failed retry", () => {
|
|
assert.equal(isOneTalkBuyerFactCompanyWebsite("https://example.test"), true);
|
|
assert.equal(isOneTalkBuyerFactCompanyWebsite("http://example.test:80/"), true);
|
|
assert.equal(isOneTalkBuyerFactCompanyWebsite("example.test"), false);
|
|
assert.equal(isOneTalkBuyerFactCompanyWebsite(" https://example.test/"), false);
|
|
|
|
const observed = decode({
|
|
...frameBase,
|
|
type: "buyer.facts.observed",
|
|
payload: { facts: [buyerFact] },
|
|
});
|
|
assert.equal(observed.type, "buyer.facts.observed");
|
|
const constructed = createOneTalkBuyerFactsObservedFrame(frameBase, [buyerFact]);
|
|
assert.deepEqual(constructed.payload.facts[0].buyerTags, ["will_loss"]);
|
|
|
|
const extended = {
|
|
...buyerFact,
|
|
email: "buyer@example.test",
|
|
registrationDate: "2025-03-12",
|
|
companyWebsite: "https://example.test/",
|
|
contactDetails: {
|
|
state: "confirmed" as const,
|
|
errorCode: null,
|
|
attemptedAtMs: buyerFact.observedAtMs,
|
|
confirmedAtMs: buyerFact.observedAtMs,
|
|
},
|
|
factFingerprint:
|
|
'tags=["will_loss"]|features=[]|email="buyer@example.test"|registrationDate="2025-03-12"|companyWebsite="https://example.test/"',
|
|
} satisfies OneTalkBuyerFact;
|
|
const extendedFrame = createOneTalkBuyerFactsObservedFrame(frameBase, [extended]);
|
|
const cloned = cloneOneTalkBuyerFact(extended);
|
|
if (!("contactDetails" in cloned)) assert.fail("extended fact clone lost contact details");
|
|
if (isOneTalkBuyerFactContactDetailsSources(cloned.contactDetails)) {
|
|
assert.fail("legacy extended fact changed contact source shape");
|
|
}
|
|
cloned.contactDetails.state = "failed";
|
|
assert.equal(extended.contactDetails.state, "confirmed");
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "buyer.facts.observed",
|
|
payload: extendedFrame.payload,
|
|
}).ok,
|
|
true,
|
|
);
|
|
|
|
const fieldLevel = {
|
|
...extended,
|
|
contactDetails: {
|
|
email: { ...extended.contactDetails },
|
|
registrationDate: {
|
|
state: "pending" as const,
|
|
errorCode: null,
|
|
attemptedAtMs: buyerFact.observedAtMs,
|
|
confirmedAtMs: null,
|
|
},
|
|
companyWebsite: { ...extended.contactDetails },
|
|
},
|
|
registrationDate: null,
|
|
factFingerprint:
|
|
'tags=["will_loss"]|features=[]|email="buyer@example.test"|registrationDate=null|companyWebsite="https://example.test/"',
|
|
} satisfies OneTalkBuyerFact;
|
|
const fieldLevelClone = cloneOneTalkBuyerFact(fieldLevel);
|
|
if (!("contactDetails" in fieldLevelClone)) assert.fail("field-level contact sources lost");
|
|
assert.equal(isOneTalkBuyerFactContactDetailsSources(fieldLevelClone.contactDetails), true);
|
|
if (isOneTalkBuyerFactContactDetailsSources(fieldLevelClone.contactDetails)) {
|
|
fieldLevelClone.contactDetails.email.state = "failed";
|
|
assert.equal(fieldLevel.contactDetails.email.state, "confirmed");
|
|
}
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "buyer.facts.observed",
|
|
payload: { facts: [fieldLevel] },
|
|
}).ok,
|
|
true,
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "buyer.facts.observed",
|
|
payload: {
|
|
facts: [{ ...extended, email: "buyer@example.test", contactDetails: undefined }],
|
|
},
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "buyer.facts.observed",
|
|
payload: { facts: [{ ...extended, registrationDate: "2025-02-30" }] },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
|
|
const failedAfterConfirmed: OneTalkBuyerFact = {
|
|
...buyerFact,
|
|
tags: {
|
|
state: "failed",
|
|
errorCode: "crm_contract_unverified",
|
|
attemptedAtMs: 1_700_000_000_001,
|
|
confirmedAtMs: buyerFact.tags.confirmedAtMs,
|
|
},
|
|
observedAtMs: 1_700_000_000_001,
|
|
};
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "buyer.facts.observed",
|
|
payload: { facts: [failedAfterConfirmed] },
|
|
}).ok,
|
|
true,
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "buyer.facts.observed",
|
|
payload: { facts: [{ ...buyerFact, buyerTags: [" raw "] }] },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "buyer.facts.observed",
|
|
payload: { facts: [{ ...buyerFact, buyerFeatures: ["feature", "feature"] }] },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
});
|
|
|
|
test("strictly decodes profile frames and rejects sensitive or incomplete payloads", () => {
|
|
const observed = decode({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [contactProfile] },
|
|
});
|
|
assert.equal(observed.type, "contact.profile.observed");
|
|
|
|
const ack = decode({
|
|
...frameBase,
|
|
type: "contact.profile.ack",
|
|
payload: { status: "delivered", profileCount: 1 },
|
|
});
|
|
assert.equal(ack.type, "contact.profile.ack");
|
|
|
|
const explicitNull = decode({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [{ ...contactProfile, avatarUrl: null }] },
|
|
});
|
|
assert.equal(explicitNull.type, "contact.profile.observed");
|
|
assert.equal(explicitNull.payload.profiles[0].avatarUrl, null);
|
|
|
|
const constructed = createOneTalkContactProfileObservedFrame(frameBase, [contactProfile]);
|
|
assert.equal(constructed.payload.profiles[0].avatarUrl, contactProfile.avatarUrl);
|
|
|
|
const sensitiveFields = [
|
|
["chatToken", "secret-chat-token"],
|
|
["aliIdEncrypt", "secret-ali-id"],
|
|
["accountIdEncrypt", "secret-account-id"],
|
|
["loginIdEncrypt", "secret-login-id"],
|
|
["kHTAccessToken", "secret-kht-token"],
|
|
["rawRow", { chatToken: "secret-raw-row" }],
|
|
] as const;
|
|
for (const [field, value] of sensitiveFields) {
|
|
const result = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [{ ...contactProfile, [field]: value }] },
|
|
});
|
|
assert.deepEqual(result, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
assert.equal(JSON.stringify(result).includes("secret"), false);
|
|
|
|
const topLevelResult = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [contactProfile] },
|
|
[field]: value,
|
|
});
|
|
assert.deepEqual(topLevelResult, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
assert.equal(JSON.stringify(topLevelResult).includes("secret"), false);
|
|
}
|
|
|
|
const extraField = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [{ ...contactProfile, unexpected: "not-allowed" }] },
|
|
});
|
|
assert.deepEqual(extraField, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
|
|
for (const avatarUrl of [
|
|
"",
|
|
" ",
|
|
"a",
|
|
"/avatar/customer-1.jpg",
|
|
"//cdn.example.test/avatar/customer-1.jpg",
|
|
"javascript:alert(1)",
|
|
"data:image/png;base64,not-an-avatar-url",
|
|
"blob:https://cdn.example.test/avatar/customer-1",
|
|
"ftp://cdn.example.test/avatar/customer-1.jpg",
|
|
"https://cdn.example.test/avatar/customer 1.jpg",
|
|
42,
|
|
{},
|
|
] as const) {
|
|
assert.equal(isOneTalkAvatarUrl(avatarUrl), false);
|
|
const invalidAvatar = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [{ ...contactProfile, avatarUrl }] },
|
|
});
|
|
assert.deepEqual(invalidAvatar, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
}
|
|
|
|
for (const avatarUrl of [
|
|
"http://cdn.example.test/avatar/customer-1.jpg",
|
|
"https://cdn.example.test/avatar/customer-1.jpg?size=small",
|
|
]) {
|
|
assert.equal(isOneTalkAvatarUrl(avatarUrl), true);
|
|
const validAvatar = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [{ ...contactProfile, avatarUrl }] },
|
|
});
|
|
assert.equal(validAvatar.ok, true);
|
|
}
|
|
|
|
const missingAvatar = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: {
|
|
profiles: [
|
|
Object.fromEntries(
|
|
Object.entries(contactProfile).filter(([key]) => key !== "avatarUrl"),
|
|
),
|
|
],
|
|
},
|
|
});
|
|
assert.deepEqual(missingAvatar, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
|
|
const missingIdentity = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [{ ...contactProfile, aliId: "" }] },
|
|
});
|
|
assert.deepEqual(missingIdentity, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
|
|
const emptyBatch = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [] },
|
|
});
|
|
assert.deepEqual(emptyBatch, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
|
|
const emptyBatchWithSensitiveField = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [] },
|
|
chatToken: "secret-empty-batch",
|
|
});
|
|
assert.deepEqual(emptyBatchWithSensitiveField, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
assert.equal(JSON.stringify(emptyBatchWithSensitiveField).includes("secret"), false);
|
|
});
|
|
|
|
test("requires profile observed and ack frames on the plugin connection", () => {
|
|
const mindFrameBase = {
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
};
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...mindFrameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [contactProfile] },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...mindFrameBase,
|
|
type: "contact.profile.ack",
|
|
payload: { status: "delivered", profileCount: 1 },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
});
|
|
|
|
test("rejects a profile wire frame whose full serialized size exceeds the limit", () => {
|
|
const frameForName = (name: string) => ({
|
|
...frameBase,
|
|
type: "contact.profile.observed",
|
|
payload: { profiles: [{ ...contactProfile, name }] },
|
|
});
|
|
const emptyNamePayload = frameForName("").payload;
|
|
const nameLength =
|
|
ONETALK_CONTACT_PROFILE_MAX_FRAME_BYTES -
|
|
new TextEncoder().encode(JSON.stringify(emptyNamePayload)).byteLength;
|
|
const oversizedFrame = frameForName("x".repeat(nameLength));
|
|
|
|
assert.equal(
|
|
new TextEncoder().encode(JSON.stringify(oversizedFrame.payload)).byteLength,
|
|
ONETALK_CONTACT_PROFILE_MAX_FRAME_BYTES,
|
|
);
|
|
assert.ok(
|
|
new TextEncoder().encode(JSON.stringify(oversizedFrame)).byteLength >
|
|
ONETALK_CONTACT_PROFILE_MAX_FRAME_BYTES,
|
|
);
|
|
assert.deepEqual(decodeOneTalkFrame(oversizedFrame), {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
});
|
|
|
|
test("requires exact normalized message observations and excludes raw message fields", () => {
|
|
const observed = decode({
|
|
...frameBase,
|
|
type: "message.observed",
|
|
payload: { observationSource: "history", message: observedMessage },
|
|
});
|
|
assert.equal(observed.type, "message.observed");
|
|
assert.deepEqual(observed.payload.message, observedMessage);
|
|
|
|
for (const message of [
|
|
{ ...observedMessage, text: "legacy text" },
|
|
{ ...observedMessage, contentType: 1 },
|
|
{ ...observedMessage, content: { ...textContent, custom: { data: "raw-content" } } },
|
|
{ ...observedMessage, content: { ...textContent, version: 2 } },
|
|
{
|
|
...observedMessage,
|
|
content: { version: ONETALK_CONTENT_VERSION, kind: "text", text: "" },
|
|
},
|
|
{ ...observedMessage, sentAtMs: -1 },
|
|
]) {
|
|
const result = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.observed",
|
|
payload: { observationSource: "incremental", message },
|
|
});
|
|
assert.deepEqual(result, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
assert.equal(JSON.stringify(result).includes("raw-content"), false);
|
|
}
|
|
|
|
const payloadWithRawField = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.observed",
|
|
payload: {
|
|
observationSource: "history",
|
|
message: observedMessage,
|
|
custom: { data: "raw-content" },
|
|
},
|
|
});
|
|
assert.deepEqual(payloadWithRawField, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
});
|
|
|
|
test("validates normalized text, JPEG, ZIP, and PDF content with exact metadata", () => {
|
|
for (const content of [textContent, jpegContent, zipContent, pdfContent]) {
|
|
assert.equal(isOneTalkMessageContent(content), true);
|
|
assert.deepEqual(decodeOneTalkMessageContent(content), { ok: true, content });
|
|
}
|
|
|
|
for (const content of [
|
|
{ ...jpegContent, extension: "JPG" },
|
|
{ ...jpegContent, sizeBytes: -1 },
|
|
{ ...jpegContent, sizeBytes: Number.MAX_SAFE_INTEGER + 1 },
|
|
{
|
|
...jpegContent,
|
|
previewUrl: "https://example.test/file/redirectFileUrl.htm?fileAction=imagePreview",
|
|
},
|
|
{ ...jpegContent, previewUrl: "" },
|
|
{ ...zipContent, cardType: 12 },
|
|
{ ...zipContent, cardType: 2000 },
|
|
{ ...zipContent, fileName: "archive.pdf" },
|
|
{ ...zipContent, downloadUrl: null },
|
|
{ ...pdfContent, downloadState: "available" },
|
|
{ kind: "text", text: "missing-version" },
|
|
{ ...textContent, version: 2 },
|
|
{
|
|
...pdfContent,
|
|
previewUrl:
|
|
"https://clouddisk.alibaba.com/file/redirectFileUrl.htm?fileAction=download&id=file-pdf-1",
|
|
},
|
|
]) {
|
|
assert.equal(isOneTalkMessageContent(content), false);
|
|
assert.deepEqual(decodeOneTalkMessageContent(content), { ok: false });
|
|
}
|
|
|
|
const imageSendCommand = decode({
|
|
...frameBase,
|
|
type: "send.command",
|
|
sendRequestId: "send-generic-content",
|
|
payload: {
|
|
conversationId: "conversation-1",
|
|
content: {
|
|
kind: "image",
|
|
source: {
|
|
downloadUrl: "https://mind.example.test/bridge/image-1",
|
|
fileName: "image.jpg",
|
|
mimeType: "image/jpeg",
|
|
},
|
|
},
|
|
},
|
|
});
|
|
assert.equal(imageSendCommand.type, "send.command");
|
|
});
|
|
|
|
test("stores exact business-card markers and only exposes customer fields in public messages", () => {
|
|
for (const content of [businessCardContent, inquiryContent, orderContent]) {
|
|
assert.equal(isOneTalkMessageContent(content), true);
|
|
assert.deepEqual(decodeOneTalkMessageContent(content), { ok: true, content });
|
|
assert.equal(isOneTalkMessage({ ...observedMessage, content }), true);
|
|
}
|
|
|
|
const publicBusinessCardMessage = { ...centerMessage, content: businessCardViewContent };
|
|
assert.equal(isOneTalkMessageContent(businessCardViewContent), false);
|
|
assert.equal(isOneTalkCenterMessage(publicBusinessCardMessage), true);
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
connectionType: "mind_page",
|
|
scope: mindScope,
|
|
type: "message.created",
|
|
payload: { message: publicBusinessCardMessage },
|
|
}).ok,
|
|
true,
|
|
);
|
|
|
|
for (const content of [
|
|
{ ...businessCardContent, contactName: "" },
|
|
{ ...businessCardContent, avatarUrl: " https://cdn.example.test/avatar/buyer.jpg" },
|
|
{ ...businessCardContent, contact: { accountIdEncrypt: "secret" } },
|
|
{ ...inquiryContent, params: { encryFeedbackId: "secret" } },
|
|
{ ...orderContent, orderAmount: -1 },
|
|
{ ...orderContent, orderAmountCurrency: "" },
|
|
{ ...orderContent, actions: [{ ...orderContent.actions[0], sign: "secret" }] },
|
|
{ ...orderContent, actions: [{ name: "pay", messageKey: "order.pay", payStep: 1 }] },
|
|
{ ...orderContent, params: { sign: "secret" } },
|
|
]) {
|
|
assert.equal(isOneTalkMessageContent(content), false);
|
|
assert.deepEqual(decodeOneTalkMessageContent(content), { ok: false });
|
|
}
|
|
});
|
|
|
|
test("rejects legacy image dimensions as exact-shape extras", () => {
|
|
for (const [key, value] of [
|
|
["width", 1_280],
|
|
["height", 720],
|
|
["width", null],
|
|
["height", "720"],
|
|
["width", { raw: true }],
|
|
["height", [720]],
|
|
["width", Number.MAX_SAFE_INTEGER + 1],
|
|
["height", -1],
|
|
] as const) {
|
|
const content = { ...jpegContent, [key]: value };
|
|
assert.equal(isOneTalkMessageContent(content), false);
|
|
assert.deepEqual(decodeOneTalkMessageContent(content), { ok: false });
|
|
}
|
|
});
|
|
|
|
test("accepts multiline text while rejecting non-text control characters", () => {
|
|
for (const text of ["line one\nline two", "line one\r\nline two", "column one\tcolumn two"]) {
|
|
const content = { ...textContent, text };
|
|
assert.equal(isOneTalkMessageContent(content), true);
|
|
assert.deepEqual(decodeOneTalkMessageContent(content), { ok: true, content });
|
|
}
|
|
|
|
for (const text of ["line one\0line two", "line one\u000bline two", "line one\u007fline two"]) {
|
|
const content = { ...textContent, text };
|
|
assert.equal(isOneTalkMessageContent(content), false);
|
|
assert.deepEqual(decodeOneTalkMessageContent(content), { ok: false });
|
|
}
|
|
});
|
|
|
|
test("accepts only exact outbound media sources without media policy duplication", () => {
|
|
const base = {
|
|
...frameBase,
|
|
type: "send.command",
|
|
sendRequestId: "image-command",
|
|
payload: {
|
|
conversationId: "conversation-1",
|
|
content: {
|
|
kind: "image",
|
|
source: {
|
|
downloadUrl: "https://mind.example.test/bridge/image-1",
|
|
fileName: "image.jpg",
|
|
mimeType: "application/x-mind-approved-image",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
assert.equal(decodeOneTalkFrame(base).ok, true);
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...base,
|
|
sendRequestId: "file-command",
|
|
payload: {
|
|
...base.payload,
|
|
content: { ...base.payload.content, kind: "file" },
|
|
},
|
|
}).ok,
|
|
true,
|
|
);
|
|
for (const content of [
|
|
{ ...base.payload.content, source: { ...base.payload.content.source, extra: true } },
|
|
{
|
|
...base.payload.content,
|
|
source: {
|
|
...base.payload.content.source,
|
|
downloadUrl: "http://mind.example.test/image",
|
|
},
|
|
},
|
|
{
|
|
...base.payload.content,
|
|
source: {
|
|
...base.payload.content.source,
|
|
downloadUrl: "https://user:pass@mind.example.test/image",
|
|
},
|
|
},
|
|
{
|
|
kind: "image",
|
|
source: { downloadUrl: "https://mind.example.test/image", fileName: "image.jpg" },
|
|
},
|
|
{ kind: "text", text: "hello", source: {} },
|
|
"hello",
|
|
]) {
|
|
assert.deepEqual(decodeOneTalkFrame({ ...base, payload: { ...base.payload, content } }), {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("enforces media metadata bounds and legal nullable media URL states", () => {
|
|
for (const content of [
|
|
{ ...jpegContent, sizeBytes: 0, previewUrl: null },
|
|
{
|
|
...jpegContent,
|
|
sizeBytes: ONETALK_MAX_MEDIA_SIZE_BYTES,
|
|
},
|
|
{ ...zipContent, sizeBytes: 0, thumbnailUrl: null },
|
|
{ ...zipContent, sizeBytes: ONETALK_MAX_MEDIA_SIZE_BYTES, thumbnailUrl: null },
|
|
{
|
|
...pdfContent,
|
|
previewUrl: null,
|
|
thumbnailUrl: null,
|
|
downloadUrl: null,
|
|
downloadState: "not_provided" as const,
|
|
},
|
|
]) {
|
|
assert.equal(isOneTalkMessageContent(content), true);
|
|
}
|
|
|
|
for (const content of [
|
|
{ ...jpegContent, sizeBytes: ONETALK_MAX_MEDIA_SIZE_BYTES + 1 },
|
|
{ ...zipContent, sizeBytes: ONETALK_MAX_MEDIA_SIZE_BYTES + 1 },
|
|
{ ...jpegContent, sizeBytes: Number.MAX_SAFE_INTEGER + 1 },
|
|
{ ...zipContent, sizeBytes: Number.MAX_SAFE_INTEGER + 1 },
|
|
]) {
|
|
assert.equal(isOneTalkMessageContent(content), false);
|
|
assert.deepEqual(decodeOneTalkMessageContent(content), { ok: false });
|
|
}
|
|
});
|
|
|
|
test("rejects unknown content kinds and invalid URL scopes", () => {
|
|
for (const content of [
|
|
{ version: ONETALK_CONTENT_VERSION, kind: "video", urlScope: "onetalk_session" },
|
|
{ ...jpegContent, urlScope: "public" },
|
|
{ ...zipContent, urlScope: "other_session" },
|
|
]) {
|
|
assert.equal(isOneTalkMessageContent(content), false);
|
|
assert.deepEqual(decodeOneTalkMessageContent(content), { ok: false });
|
|
}
|
|
});
|
|
|
|
test("requires exactly two unique direct participants including the sender", () => {
|
|
for (const participantIds of [[], ["sender-1"], ["sender-1", "sender-1"]]) {
|
|
const message = { ...completeMessage, participantIds };
|
|
assert.equal(isOneTalkMessage(message), false);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.observed",
|
|
payload: { observationSource: "history", message },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
}
|
|
|
|
const senderOutsideSet = {
|
|
...completeMessage,
|
|
participantIds: ["other-1", "other-2"],
|
|
};
|
|
assert.equal(isOneTalkMessage(senderOutsideSet), false);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.observed",
|
|
payload: { observationSource: "history", message: senderOutsideSet },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
|
|
for (const participantIds of [
|
|
["sender-1", "login-user-1"],
|
|
["login-user-1", "sender-1"],
|
|
]) {
|
|
const message = { ...completeMessage, participantIds };
|
|
assert.equal(isOneTalkMessage(message), true);
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.observed",
|
|
payload: { observationSource: "history", message },
|
|
}).ok,
|
|
true,
|
|
);
|
|
|
|
const center = { ...centerMessage, participantIds };
|
|
assert.equal(isOneTalkCenterMessage(center), true);
|
|
assert.equal(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
type: "message.created",
|
|
payload: { message: center },
|
|
}).ok,
|
|
true,
|
|
);
|
|
}
|
|
|
|
for (const participantIds of [[], ["sender-1"], ["sender-1", "sender-1"]]) {
|
|
assert.equal(isOneTalkCenterMessage({ ...centerMessage, participantIds }), false);
|
|
}
|
|
assert.equal(
|
|
isOneTalkCenterMessage({
|
|
...centerMessage,
|
|
participantIds: ["other-1", "other-2"],
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("uses the shared public Center message for created frames and internal facts for confirmation", () => {
|
|
const created = decode({
|
|
...frameBase,
|
|
type: "message.created",
|
|
connectionType: "mind_page",
|
|
scope: mindScope,
|
|
payload: { message: centerMessage },
|
|
});
|
|
assert.equal(created.type, "message.created");
|
|
assert.equal(isOneTalkCenterMessage(centerMessage), true);
|
|
|
|
for (const forbiddenField of ["readStatus", "unreadCount", "messageStatus"] as const) {
|
|
const publicMessageWithRawField = {
|
|
...centerMessage,
|
|
[forbiddenField]: forbiddenField === "readStatus" ? "unread" : 0,
|
|
};
|
|
assert.equal(isOneTalkCenterMessage(publicMessageWithRawField), false);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.created",
|
|
connectionType: "mind_page",
|
|
scope: mindScope,
|
|
payload: { message: publicMessageWithRawField },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
}
|
|
|
|
const internalMessageAsCreated = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.created",
|
|
connectionType: "mind_page",
|
|
scope: mindScope,
|
|
payload: { message: completeMessage },
|
|
});
|
|
assert.deepEqual(internalMessageAsCreated, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
|
|
const rawCenterMessage = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.created",
|
|
connectionType: "mind_page",
|
|
scope: mindScope,
|
|
payload: { message: { ...centerMessage, contentType: 1 } },
|
|
});
|
|
assert.deepEqual(rawCenterMessage, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
|
|
const legacyTextCenterMessage = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "message.created",
|
|
connectionType: "mind_page",
|
|
scope: mindScope,
|
|
payload: { message: { ...centerMessage, text: "legacy text" } },
|
|
});
|
|
assert.deepEqual(legacyTextCenterMessage, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
|
|
const confirmed = decode({
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
type: "send.result",
|
|
sendRequestId: "send-confirmed",
|
|
payload: { status: "confirmed_sent", message: { ...centerMessage, direction: "sent" } },
|
|
});
|
|
assert.equal(confirmed.type, "send.result");
|
|
|
|
const rawSendResult = decodeOneTalkFrame({
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
type: "send.result",
|
|
sendRequestId: "send-raw-fields",
|
|
payload: {
|
|
status: "confirmed_sent",
|
|
message: { ...completeMessage, direction: "sent" },
|
|
},
|
|
});
|
|
assert.deepEqual(rawSendResult, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
|
|
const publicSendResultWithUnknownField = decodeOneTalkFrame({
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
type: "send.result",
|
|
sendRequestId: "send-public-extra-field",
|
|
payload: {
|
|
status: "confirmed_sent",
|
|
message: { ...centerMessage, direction: "sent", contentType: 1 },
|
|
},
|
|
});
|
|
assert.deepEqual(publicSendResultWithUnknownField, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
|
|
const incompleteConfirmed = decodeOneTalkFrame({
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
type: "send.result",
|
|
sendRequestId: "send-incomplete",
|
|
payload: { status: "confirmed_sent", message: observedMessage },
|
|
});
|
|
assert.deepEqual(incompleteConfirmed, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
});
|
|
|
|
test("freezes exact public conversation and private history routes", () => {
|
|
assert.equal(
|
|
ONETALK_MESSAGES_ROUTE,
|
|
"/api/bright/onetalk/accounts/:channelAccountId/conversations/:conversationId/messages",
|
|
);
|
|
assert.equal(
|
|
ONETALK_HISTORY_ROUTE,
|
|
"/api/bright/onetalk/accounts/:channelAccountId/conversations/:conversationId/history",
|
|
);
|
|
assert.equal(isCenterConversation(centerConversation), true);
|
|
|
|
const mindFrameBase = {
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
};
|
|
const updated = createOneTalkConversationUpdatedFrame(mindFrameBase, centerConversation, true);
|
|
assert.deepEqual(decodeOneTalkFrame(updated), { ok: true, frame: updated });
|
|
|
|
for (const forbiddenField of [
|
|
"readStatus",
|
|
"unreadCount",
|
|
"plugin",
|
|
"latestMessageId",
|
|
"latestMessageAtMs",
|
|
"channelAccountId",
|
|
]) {
|
|
const conversationWithInternalField = { ...centerConversation, [forbiddenField]: null };
|
|
assert.equal(isCenterConversation(conversationWithInternalField), false);
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...mindFrameBase,
|
|
type: "conversation.updated",
|
|
payload: { conversation: conversationWithInternalField, moveToTop: true },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
}
|
|
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "conversation.updated",
|
|
payload: { conversation: centerConversation, moveToTop: true },
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
});
|
|
|
|
test("covers anchors, sync completion, acknowledgements, and all send results", () => {
|
|
const anchor = decode({
|
|
...frameBase,
|
|
type: "anchor.snapshot",
|
|
payload: {
|
|
anchors: [{ conversationId: "conversation-1", latestMessageId: "message-1" }],
|
|
},
|
|
});
|
|
assert.equal(anchor.type, "anchor.snapshot");
|
|
const anchorSnapshot = createOneTalkAnchorSnapshotFrame(frameBase, [
|
|
{ conversationId: "conversation-1", latestMessageId: "message-1" },
|
|
]);
|
|
assert.equal(anchorSnapshot.requestId, frameBase.requestId);
|
|
assert.deepEqual(anchorSnapshot.payload.anchors, [
|
|
{ conversationId: "conversation-1", latestMessageId: "message-1" },
|
|
]);
|
|
|
|
const completion = decode({
|
|
...frameBase,
|
|
type: "sync.complete",
|
|
payload: {
|
|
conversationId: "conversation-1",
|
|
mode: "incremental",
|
|
historyComplete: true,
|
|
result: "succeeded_with_anomalies",
|
|
latestMessageId: "message-2",
|
|
latestMessageAtMs: 1_700_000_000_123,
|
|
anomalyCode: "incremental_anchor_not_found",
|
|
},
|
|
});
|
|
assert.equal(completion.type, "sync.complete");
|
|
assert.equal(completion.payload.latestMessageAtMs, 1_700_000_000_123);
|
|
|
|
for (const latestMessageAtMs of [-1, 1.5, Number.NaN, "1700000000123"]) {
|
|
assert.deepEqual(
|
|
decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "sync.complete",
|
|
payload: {
|
|
conversationId: "conversation-1",
|
|
mode: "full",
|
|
historyComplete: true,
|
|
result: "succeeded",
|
|
latestMessageAtMs,
|
|
},
|
|
}),
|
|
{ ok: false, code: ONETALK_ERROR_CODES.invalidMessage },
|
|
);
|
|
}
|
|
|
|
const acknowledgement = decode({
|
|
...frameBase,
|
|
type: "message.ack",
|
|
payload: {
|
|
status: "anomaly",
|
|
conversationId: "conversation-1",
|
|
messageId: "message-3",
|
|
anomalyCode: "missing_sender_id",
|
|
},
|
|
});
|
|
assert.equal(acknowledgement.type, "message.ack");
|
|
|
|
const acknowledgementFromObservation = createOneTalkMessageAckFrame(frameBase, {
|
|
status: "accepted",
|
|
conversationId: "conversation-1",
|
|
messageId: "message-1",
|
|
});
|
|
assert.equal(acknowledgementFromObservation.requestId, frameBase.requestId);
|
|
assert.deepEqual(acknowledgementFromObservation.scope, frameBase.scope);
|
|
|
|
const unknownSyncAnomaly = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "sync.complete",
|
|
payload: {
|
|
conversationId: "conversation-1",
|
|
mode: "incremental",
|
|
historyComplete: true,
|
|
result: "failed",
|
|
anomalyCode: "arbitrary_internal_code",
|
|
},
|
|
});
|
|
assert.deepEqual(unknownSyncAnomaly, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
|
|
for (const status of ["rejected_before_send", "delivery_unknown"] as const) {
|
|
const result = decode({
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
type: "send.result",
|
|
sendRequestId: `send-${status}`,
|
|
payload: {
|
|
status,
|
|
reason: status === "rejected_before_send" ? "page_not_found" : "send_timeout",
|
|
},
|
|
});
|
|
assert.equal(result.type, "send.result");
|
|
}
|
|
|
|
const confirmed = decode({
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
type: "send.result",
|
|
sendRequestId: "send-confirmed",
|
|
payload: {
|
|
status: "confirmed_sent",
|
|
message: { ...centerMessage, direction: "sent" },
|
|
},
|
|
});
|
|
assert.equal(confirmed.type, "send.result");
|
|
|
|
const command = decode({
|
|
...frameBase,
|
|
type: "send.command",
|
|
sendRequestId: "send-command",
|
|
payload: { conversationId: "conversation-1", content: { kind: "text", text: "hello" } },
|
|
});
|
|
assert.equal(command.type, "send.command");
|
|
|
|
const confirmation = decode({
|
|
...frameBase,
|
|
type: "send.confirmation",
|
|
sendRequestId: "send-command",
|
|
payload: {
|
|
status: "confirmed_sent",
|
|
message: { ...completeMessage, direction: "sent" },
|
|
},
|
|
});
|
|
assert.equal(confirmation.type, "send.confirmation");
|
|
|
|
for (const type of ["send.command", "send.confirmation"] as const) {
|
|
const missingId = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type,
|
|
...(type === "send.command"
|
|
? {
|
|
payload: {
|
|
conversationId: "conversation-1",
|
|
content: { kind: "text", text: "hello" },
|
|
},
|
|
}
|
|
: { payload: { status: "delivery_unknown", reason: "send_timeout" } }),
|
|
});
|
|
assert.deepEqual(missingId, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
}
|
|
|
|
for (const type of ["send.confirmation", "send.result"] as const) {
|
|
const nonConfirmationWithMessage = decodeOneTalkFrame({
|
|
...frameBase,
|
|
connectionType:
|
|
type === "send.confirmation" ? ("plugin" as const) : ("mind_page" as const),
|
|
scope: type === "send.confirmation" ? pluginScope : mindScope,
|
|
type,
|
|
sendRequestId: `send-invalid-${type}`,
|
|
payload: {
|
|
status: "delivery_unknown",
|
|
reason: "send_timeout",
|
|
message: { ...completeMessage, direction: "sent" },
|
|
},
|
|
});
|
|
assert.deepEqual(nonConfirmationWithMessage, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
|
|
const nonConfirmationWithNullMessage = decodeOneTalkFrame({
|
|
...frameBase,
|
|
connectionType:
|
|
type === "send.confirmation" ? ("plugin" as const) : ("mind_page" as const),
|
|
scope: type === "send.confirmation" ? pluginScope : mindScope,
|
|
type,
|
|
sendRequestId: `send-invalid-null-${type}`,
|
|
payload: {
|
|
status: "rejected_before_send",
|
|
reason: "page_not_found",
|
|
message: null,
|
|
},
|
|
});
|
|
assert.deepEqual(nonConfirmationWithNullMessage, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
}
|
|
|
|
const confirmedWithReason = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "send.result",
|
|
sendRequestId: "send-invalid-reason",
|
|
payload: {
|
|
status: "confirmed_sent",
|
|
reason: "send_timeout",
|
|
message: { ...completeMessage, direction: "sent" },
|
|
},
|
|
});
|
|
assert.deepEqual(confirmedWithReason, {
|
|
ok: false,
|
|
code: ONETALK_ERROR_CODES.invalidMessage,
|
|
});
|
|
});
|
|
|
|
test("decodes Mind page plugin and sync status frames", () => {
|
|
const mindFrameBase = {
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
};
|
|
const pluginStatus = createOneTalkPluginStatusFrame(mindFrameBase, "online");
|
|
assert.deepEqual(decodeOneTalkFrame(pluginStatus), { ok: true, frame: pluginStatus });
|
|
|
|
const syncStatus = createOneTalkSyncStatusFrame(mindFrameBase, {
|
|
conversationId: "conversation-1",
|
|
mode: "incremental",
|
|
syncPhase: "incremental",
|
|
syncResult: "succeeded",
|
|
latestMessageId: "message-1",
|
|
historyComplete: true,
|
|
messageCount: 1,
|
|
anchorAdvanced: true,
|
|
});
|
|
assert.deepEqual(decodeOneTalkFrame(syncStatus), { ok: true, frame: syncStatus });
|
|
});
|
|
|
|
test("rejects server frames sent toward the wrong connection type", () => {
|
|
const pluginStatus = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "plugin.status",
|
|
payload: { status: "online" },
|
|
});
|
|
assert.deepEqual(pluginStatus, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
|
|
const syncStatus = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "sync.status",
|
|
payload: {
|
|
conversationId: "conversation-1",
|
|
mode: "full",
|
|
syncPhase: "initial",
|
|
syncResult: "incomplete",
|
|
latestMessageId: null,
|
|
historyComplete: false,
|
|
messageCount: 0,
|
|
anchorAdvanced: false,
|
|
},
|
|
});
|
|
assert.deepEqual(syncStatus, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
});
|
|
|
|
test("allows a Mind page scope without a plugin device identifier", () => {
|
|
const frame = decode({
|
|
...frameBase,
|
|
connectionType: "mind_page",
|
|
scope: mindScope,
|
|
type: "ws.hello",
|
|
payload: { requestedPermissions: ["read"] },
|
|
});
|
|
|
|
assert.equal(frame.connectionType, "mind_page");
|
|
});
|
|
|
|
test("does not echo credentials in decoder failures", () => {
|
|
const result = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "ws.hello",
|
|
payload: { credential: "secret-session", requestedPermissions: "read" },
|
|
});
|
|
|
|
assert.equal(result.ok, false);
|
|
assert.equal(JSON.stringify(result).includes("secret-session"), false);
|
|
|
|
const scopeCredential = decodeOneTalkFrame({
|
|
...frameBase,
|
|
scope: { ...pluginScope, sessionToken: "secret-scope-token" },
|
|
type: "ws.hello",
|
|
payload: { binding: "binding-1", requestedPermissions: ["read"] },
|
|
});
|
|
|
|
assert.deepEqual(scopeCredential, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
assert.equal(JSON.stringify(scopeCredential).includes("secret-scope-token"), false);
|
|
|
|
const mindCredential = decodeOneTalkFrame({
|
|
...frameBase,
|
|
connectionType: "mind_page",
|
|
scope: mindScope,
|
|
type: "ws.hello",
|
|
payload: { credential: "mind-session", requestedPermissions: ["read"] },
|
|
});
|
|
assert.equal(mindCredential.ok, false);
|
|
|
|
const bothBindingFields = decodeOneTalkFrame({
|
|
...frameBase,
|
|
type: "ws.hello",
|
|
payload: {
|
|
binding: "binding-1",
|
|
credential: "not-a-binding-alias",
|
|
requestedPermissions: ["read"],
|
|
},
|
|
});
|
|
assert.deepEqual(bothBindingFields, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
});
|
|
|
|
test("rejects cyclic JSON payloads without throwing", () => {
|
|
const content: { self?: unknown } = {};
|
|
content.self = content;
|
|
|
|
const result = decodeOneTalkFrame({
|
|
...frameBase,
|
|
connectionType: "mind_page" as const,
|
|
scope: mindScope,
|
|
type: "send.request",
|
|
sendRequestId: "send-1",
|
|
payload: { conversationId: "conversation-1", content },
|
|
});
|
|
|
|
assert.deepEqual(result, { ok: false, code: ONETALK_ERROR_CODES.invalidMessage });
|
|
});
|
|
|
|
test("mock authorization separates scope, permission, revoke, and dependency failures", async () => {
|
|
const reader = createMockAuthorizationReader([authorizationRecord]);
|
|
|
|
const connected = await reader.authorize({
|
|
connectionType: "plugin",
|
|
operation: "connect",
|
|
scope: pluginScope,
|
|
binding: "binding-1",
|
|
});
|
|
assert.deepEqual(connected, {
|
|
allowed: true,
|
|
binding: "binding-1",
|
|
authorizationVersion: "version-1",
|
|
permissions: ["read", "send"],
|
|
mindScope,
|
|
});
|
|
|
|
const readOnly = createMockAuthorizationReader([
|
|
{ ...authorizationRecord, permissions: ["read"] },
|
|
]);
|
|
const sendDenied = await readOnly.authorize({
|
|
connectionType: "mind_page",
|
|
operation: "send",
|
|
scope: mindScope,
|
|
});
|
|
assert.deepEqual(sendDenied, {
|
|
allowed: false,
|
|
code: ONETALK_ERROR_CODES.authorizationRejected,
|
|
});
|
|
|
|
const mismatchedScope = await reader.authorize({
|
|
connectionType: "plugin",
|
|
operation: "connect",
|
|
scope: { ...pluginScope, channelAccountId: "other-account" },
|
|
binding: "binding-1",
|
|
});
|
|
assert.deepEqual(mismatchedScope, {
|
|
allowed: false,
|
|
code: ONETALK_ERROR_CODES.scopeMismatch,
|
|
});
|
|
|
|
reader.revoke(pluginScope);
|
|
const revoked = await reader.authorize({
|
|
connectionType: "plugin",
|
|
operation: "heartbeat",
|
|
scope: pluginScope,
|
|
binding: "binding-1",
|
|
});
|
|
assert.deepEqual(revoked, {
|
|
allowed: false,
|
|
code: ONETALK_ERROR_CODES.bindingRevoked,
|
|
});
|
|
|
|
reader.setUnavailable(true);
|
|
const unavailable = await reader.authorize({
|
|
connectionType: "plugin",
|
|
operation: "connect",
|
|
scope: pluginScope,
|
|
binding: "binding-1",
|
|
});
|
|
assert.deepEqual(unavailable, {
|
|
allowed: false,
|
|
code: ONETALK_ERROR_CODES.authorizationUnavailable,
|
|
});
|
|
|
|
reader.setUnavailable(false);
|
|
reader.restore(pluginScope);
|
|
assert.equal(await reader.readAuthorizationVersion(pluginScope, "binding-1"), "version-1");
|
|
|
|
reader.upsert({ ...authorizationRecord, authorizationVersion: "version-2" });
|
|
assert.equal(await reader.readAuthorizationVersion(pluginScope, "binding-1"), "version-2");
|
|
});
|
|
|
|
test("does not use deviceId as an authorization decision input", async () => {
|
|
const reader = createMockAuthorizationReader([authorizationRecord]);
|
|
const decision = await reader.authorize({
|
|
connectionType: "plugin",
|
|
operation: "connect",
|
|
scope: { ...pluginScope, deviceId: "another-installation" },
|
|
binding: "binding-1",
|
|
});
|
|
assert.equal(decision.allowed, true);
|
|
});
|
|
|
|
test("strictly rejects duplicate Mind permissions in an otherwise valid response", () => {
|
|
assert.deepEqual(
|
|
decodeMindAuthorizationResponse({
|
|
binding: "binding-1",
|
|
authorizationVersion: "version-1",
|
|
permissions: ["read", "read"],
|
|
mindScope,
|
|
}),
|
|
{ ok: false },
|
|
);
|
|
});
|