mirror of
https://github.com/sinanyuntu/trade-message-center.git
synced 2026-09-17 13:22:11 +08:00
404 lines
14 KiB
JavaScript
404 lines
14 KiB
JavaScript
// 验证 MAIN-world OneTalk 媒体归一化边界
|
|
|
|
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
decodeOneTalkMessageContent,
|
|
decodeOneTalkRawContent,
|
|
} from "../src/onetalk/main-page/message-observer/content-decoder.ts";
|
|
import { parseOneTalkMessages } from "../src/onetalk/main-page/message-observer/index.ts";
|
|
import { observedMessage } from "../src/onetalk/main-page/message-observer/model.ts";
|
|
|
|
const base64Json = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64");
|
|
const redirect = (action, id) =>
|
|
`https://clouddisk.alibaba.com/file/redirectFileUrl.htm?appkey=onetalk&fileAction=${action}&id=${id}&parentId=parent-1&scene=im&secOperateAliId=operation-1`;
|
|
const thumbnail = (id) =>
|
|
`https://clouddisk.alibaba.com/file/videoThumb.htm?appkey=onetalk&id=${id}&parentId=parent-1&scene=im&secOperateAliId=operation-1`;
|
|
|
|
// Raw-only input fixture: upstream dimensions are intentionally present; normalized output must omit them.
|
|
const imagePayload = {
|
|
fileId: "image-jpeg-1",
|
|
suffix: "JPG",
|
|
size: 263_333,
|
|
width: 1_280,
|
|
height: 720,
|
|
isOriginal: 1,
|
|
md5: "f28f1f8f4b760d5e2a89c3f0f83f3f68",
|
|
url: redirect("imagePreview", "image-jpeg-1"),
|
|
};
|
|
|
|
const filePayload = (extensionType, name, action, id) => ({
|
|
cardType: 12,
|
|
params: {
|
|
extensionType,
|
|
id,
|
|
parentId: "parent-1",
|
|
md5: "a614ee55b22a6545c2bc7342898c6a6f",
|
|
name,
|
|
size: "8192",
|
|
url: redirect(action, id),
|
|
thumbnailUrl: thumbnail(id),
|
|
downloadUrl: "",
|
|
},
|
|
});
|
|
|
|
const rawImage = (payload = imagePayload) => ({
|
|
contentType: 101,
|
|
custom: { type: 7, data: base64Json(payload) },
|
|
});
|
|
const rawFile = (payload) => ({
|
|
contentType: 101,
|
|
custom: { type: 10010, data: base64Json(payload) },
|
|
});
|
|
|
|
test("normalizes real-shape JPEG, ZIP, PDF, and generic cardType=12 files", () => {
|
|
assert.deepEqual(decodeOneTalkRawContent(rawImage()), {
|
|
status: "decoded",
|
|
content: {
|
|
version: 1,
|
|
kind: "image",
|
|
fileId: "image-jpeg-1",
|
|
extension: "jpg",
|
|
sizeBytes: 263_333,
|
|
isOriginal: true,
|
|
md5: "f28f1f8f4b760d5e2a89c3f0f83f3f68",
|
|
previewUrl: redirect("imagePreview", "image-jpeg-1"),
|
|
urlScope: "onetalk_session",
|
|
},
|
|
});
|
|
|
|
const zip = decodeOneTalkRawContent(
|
|
rawFile(filePayload("ZIP", "archive.zip", "download", "zip-1")),
|
|
);
|
|
assert.equal(zip.status, "decoded");
|
|
assert.deepEqual(zip.content, {
|
|
version: 1,
|
|
kind: "file",
|
|
fileId: "zip-1",
|
|
parentId: "parent-1",
|
|
fileName: "archive.zip",
|
|
extension: "zip",
|
|
sizeBytes: 8_192,
|
|
md5: "a614ee55b22a6545c2bc7342898c6a6f",
|
|
previewUrl: null,
|
|
thumbnailUrl: thumbnail("zip-1"),
|
|
downloadUrl: redirect("download", "zip-1"),
|
|
downloadState: "available",
|
|
urlScope: "onetalk_session",
|
|
});
|
|
|
|
const pdf = decodeOneTalkRawContent(
|
|
rawFile(filePayload("pdf", "quotation.pdf", "officePreview", "pdf-1")),
|
|
);
|
|
assert.equal(pdf.status, "decoded");
|
|
assert.equal(pdf.content.previewUrl, redirect("officePreview", "pdf-1"));
|
|
assert.equal(pdf.content.downloadUrl, null);
|
|
assert.equal(pdf.content.downloadState, "not_provided");
|
|
|
|
const generic = decodeOneTalkRawContent(
|
|
rawFile(filePayload("docx", "offer.docx", "download", "docx-1")),
|
|
);
|
|
assert.equal(generic.status, "decoded");
|
|
assert.equal(generic.content.extension, "docx");
|
|
});
|
|
|
|
test("normalizes exact SDK flat history media through the shared media contract", () => {
|
|
assert.deepEqual(
|
|
decodeOneTalkMessageContent({
|
|
source: "sdk_flat_history",
|
|
msgType: 102,
|
|
subType: 60,
|
|
originalData: imagePayload,
|
|
}),
|
|
decodeOneTalkRawContent(rawImage()),
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkMessageContent({
|
|
source: "sdk_flat_history",
|
|
msgType: 10010,
|
|
subType: 61,
|
|
originalData: filePayload("pdf", "quotation.pdf", "officePreview", "flat-file"),
|
|
}),
|
|
decodeOneTalkRawContent(
|
|
rawFile(filePayload("pdf", "quotation.pdf", "officePreview", "flat-file")),
|
|
),
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkMessageContent({
|
|
source: "sdk_flat_history",
|
|
msgType: 102,
|
|
subType: 60,
|
|
originalData: { ...imagePayload, width: "wide" },
|
|
}),
|
|
decodeOneTalkRawContent(rawImage()),
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkMessageContent({
|
|
source: "sdk_flat_history",
|
|
msgType: 999,
|
|
subType: 999,
|
|
originalData: { unsupported: true },
|
|
}),
|
|
{ status: "ignored" },
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkMessageContent({
|
|
source: "sdk_flat_history",
|
|
msgType: 999,
|
|
subType: 999,
|
|
originalData: { text: "unknown flat content must not become business text" },
|
|
}),
|
|
{ status: "ignored" },
|
|
);
|
|
});
|
|
|
|
test("skips a legal non-file business card and aggregates every safe media anomaly", () => {
|
|
assert.deepEqual(decodeOneTalkRawContent(rawFile({ cardType: 2000, params: {} })), {
|
|
status: "unsupported_skipped",
|
|
});
|
|
const cases = [
|
|
[{ contentType: 101, custom: { type: 7, data: "not-base64" } }, "media_invalid_base64"],
|
|
[
|
|
{
|
|
contentType: 101,
|
|
custom: { type: 7, data: Buffer.from([0xc3, 0x28]).toString("base64") },
|
|
},
|
|
"media_invalid_utf8",
|
|
],
|
|
[
|
|
{
|
|
contentType: 101,
|
|
custom: { type: 7, data: Buffer.from("{", "utf8").toString("base64") },
|
|
},
|
|
"media_invalid_json",
|
|
],
|
|
[
|
|
{ contentType: 101, custom: { type: 7, data: "AAAA".repeat(131_073) } },
|
|
"media_payload_too_large",
|
|
],
|
|
[rawImage({ ...imagePayload, url: "https://evil.example/preview" }), "media_invalid_url"],
|
|
];
|
|
for (const [raw, code] of cases) {
|
|
assert.deepEqual(decodeOneTalkRawContent(raw), {
|
|
status: "anomaly",
|
|
code,
|
|
mediaKind: "image",
|
|
});
|
|
}
|
|
});
|
|
|
|
test("ignores all raw image dimensions at both live and flat-history decoder boundaries", () => {
|
|
const expected = decodeOneTalkRawContent(rawImage());
|
|
const variants = [
|
|
(() => {
|
|
const { width: _width, height: _height, ...withoutDimensions } = imagePayload;
|
|
return withoutDimensions;
|
|
})(),
|
|
{ ...imagePayload, height: undefined },
|
|
{ ...imagePayload, width: undefined },
|
|
{ ...imagePayload, width: "wide", height: { raw: true } },
|
|
{ ...imagePayload, width: -1, height: -2 },
|
|
{ ...imagePayload, width: 65_536, height: Number.MAX_SAFE_INTEGER },
|
|
imagePayload,
|
|
];
|
|
for (const payload of variants) {
|
|
assert.deepEqual(decodeOneTalkRawContent(rawImage(payload)), expected);
|
|
assert.deepEqual(
|
|
decodeOneTalkMessageContent({
|
|
source: "sdk_flat_history",
|
|
msgType: 102,
|
|
subType: 60,
|
|
originalData: payload,
|
|
}),
|
|
expected,
|
|
);
|
|
}
|
|
});
|
|
|
|
test("keeps malformed image sizes as schema anomalies while dimensions remain ignored", () => {
|
|
assert.deepEqual(
|
|
decodeOneTalkRawContent(rawImage({ ...imagePayload, size: -1, width: "ignored" })),
|
|
{ status: "anomaly", code: "media_invalid_schema", mediaKind: "image" },
|
|
);
|
|
});
|
|
|
|
test("enforces file suffix and URL action rules while preserving missing URL states", () => {
|
|
const noUrls = decodeOneTalkRawContent(
|
|
rawFile({
|
|
...filePayload("pdf", "quotation.pdf", "officePreview", "pdf-no-url"),
|
|
params: {
|
|
...filePayload("pdf", "quotation.pdf", "officePreview", "pdf-no-url").params,
|
|
url: "",
|
|
thumbnailUrl: "",
|
|
downloadUrl: "",
|
|
},
|
|
}),
|
|
);
|
|
assert.equal(noUrls.status, "decoded");
|
|
assert.equal(noUrls.content.previewUrl, null);
|
|
assert.equal(noUrls.content.thumbnailUrl, null);
|
|
assert.equal(noUrls.content.downloadUrl, null);
|
|
assert.equal(noUrls.content.downloadState, "not_provided");
|
|
|
|
assert.deepEqual(
|
|
decodeOneTalkRawContent(
|
|
rawFile(filePayload("pdf", "quotation.zip", "download", "mismatched-extension")),
|
|
),
|
|
{ status: "anomaly", code: "media_invalid_schema", mediaKind: "file" },
|
|
);
|
|
assert.deepEqual(
|
|
decodeOneTalkRawContent(
|
|
rawImage({ ...imagePayload, url: redirect("download", "image-wrong-action") }),
|
|
),
|
|
{ status: "anomaly", code: "media_invalid_url", mediaKind: "image" },
|
|
);
|
|
});
|
|
|
|
const pageWindow = {
|
|
currentUserAccountId: "seller-account",
|
|
__conversationListFullData__: [{ owner: { accountId: "seller-account", aliId: "seller" } }],
|
|
};
|
|
const rawMessage = (id, content) => ({
|
|
type: 1,
|
|
messageId: id,
|
|
cid: "buyer-seller#tenant@icbu",
|
|
createAt: 100,
|
|
content,
|
|
sender: { uid: "buyer@icbu" },
|
|
unreadCount: 0,
|
|
});
|
|
const liveBatchFrame = (messages) =>
|
|
JSON.stringify({
|
|
code: 200,
|
|
body: messages.map((message) => ({
|
|
singleChatUserConversation: {
|
|
lastMessage: { message, readStatus: 0, msgStatus: 1 },
|
|
singleChatConversation: { pairFirst: "buyer@icbu", pairSecond: "seller@icbu" },
|
|
},
|
|
})),
|
|
});
|
|
const liveFrame = (message) => liveBatchFrame([message]);
|
|
|
|
test("continues a same-batch valid message after an invalid media payload", () => {
|
|
const valid = rawMessage("valid", {
|
|
contentType: 1,
|
|
text: { content: "kept", extension: { chatToken: "secret" } },
|
|
});
|
|
const invalid = rawMessage("invalid", {
|
|
contentType: 101,
|
|
custom: { type: 7, data: "not-base64-secret" },
|
|
});
|
|
const result = parseOneTalkMessages(pageWindow, liveFrame(valid));
|
|
const mixed = parseOneTalkMessages(
|
|
pageWindow,
|
|
JSON.stringify({
|
|
code: 200,
|
|
body: [
|
|
{
|
|
singleChatUserConversation: {
|
|
lastMessage: { message: valid, readStatus: 0, msgStatus: 1 },
|
|
singleChatConversation: {
|
|
pairFirst: "buyer@icbu",
|
|
pairSecond: "seller@icbu",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
singleChatUserConversation: {
|
|
lastMessage: { message: invalid, readStatus: 0, msgStatus: 1 },
|
|
singleChatConversation: {
|
|
pairFirst: "buyer@icbu",
|
|
pairSecond: "seller@icbu",
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
);
|
|
|
|
assert.equal(result.messages.length, 1);
|
|
assert.deepEqual(mixed.messages, result.messages);
|
|
assert.deepEqual(mixed.diagnostics, {
|
|
unsupportedSkippedCount: 0,
|
|
invalidObservationCount: 0,
|
|
anomalies: [{ code: "media_invalid_base64", mediaKind: "image", count: 1 }],
|
|
});
|
|
assert.equal(JSON.stringify(mixed).includes("not-base64-secret"), false);
|
|
});
|
|
|
|
test("continues type-one file cards through the shared message-class gate", () => {
|
|
const text = rawMessage("business-text", {
|
|
contentType: 1,
|
|
text: { content: "buyer inquiry" },
|
|
});
|
|
const file = rawMessage(
|
|
"business-file",
|
|
rawFile(filePayload("pdf", "quotation.pdf", "officePreview", "business-file")),
|
|
);
|
|
|
|
const parsed = parseOneTalkMessages(pageWindow, liveBatchFrame([text, file]));
|
|
|
|
assert.deepEqual(
|
|
parsed.messages.map((message) => [
|
|
message.messageId,
|
|
message.upstreamType,
|
|
message.content.kind,
|
|
]),
|
|
[
|
|
["business-text", 1, "text"],
|
|
["business-file", 1, "file"],
|
|
],
|
|
);
|
|
assert.deepEqual(parsed.diagnostics, {
|
|
unsupportedSkippedCount: 0,
|
|
invalidObservationCount: 0,
|
|
anomalies: [],
|
|
});
|
|
});
|
|
|
|
test("prioritizes an invalid identity over malformed media and keeps a same-batch valid message", () => {
|
|
const valid = rawMessage("valid", { contentType: 1, text: { content: "kept" } });
|
|
const invalid = {
|
|
...rawMessage("invalid-identity-secret", {
|
|
contentType: 101,
|
|
custom: { type: 7, data: "malformed-base64" },
|
|
}),
|
|
sender: { uid: "outside@icbu" },
|
|
};
|
|
const parsed = parseOneTalkMessages(pageWindow, liveBatchFrame([valid, invalid]));
|
|
|
|
assert.deepEqual(
|
|
parsed.messages.map((message) => message.messageId),
|
|
["valid"],
|
|
);
|
|
assert.deepEqual(parsed.diagnostics, {
|
|
unsupportedSkippedCount: 0,
|
|
invalidObservationCount: 1,
|
|
anomalies: [],
|
|
});
|
|
assert.equal(JSON.stringify(parsed).includes("invalid-identity-secret"), false);
|
|
assert.equal(JSON.stringify(parsed).includes("malformed-base64"), false);
|
|
});
|
|
|
|
test("rejects every malformed direct participant set before decoding media", () => {
|
|
const message = rawMessage("invalid-direct-identity", {
|
|
contentType: 101,
|
|
custom: { type: 7, data: "malformed-base64" },
|
|
});
|
|
for (const participantIds of [
|
|
[],
|
|
["buyer@icbu"],
|
|
["buyer@icbu", "buyer@icbu"],
|
|
["seller@icbu", "other@icbu"],
|
|
]) {
|
|
assert.deepEqual(observedMessage(message, participantIds, 0, 1, "new", "seller@icbu"), {
|
|
status: "invalid_observation",
|
|
});
|
|
}
|
|
assert.deepEqual(
|
|
observedMessage(message, ["buyer@icbu", "seller@icbu"], 0, 1, "new", "seller@icbu"),
|
|
{ status: "anomaly", code: "media_invalid_base64", mediaKind: "image" },
|
|
);
|
|
});
|