Files
trade-message-center/apps/chrome-extension/test/onetalk-media-content-decoder.test.js
T

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" },
);
});