227 lines
5.9 KiB
JavaScript
227 lines
5.9 KiB
JavaScript
import assert from "node:assert/strict";
|
||
import test from "node:test";
|
||
import {
|
||
clearFeishuAccessTokenCacheForTests,
|
||
readFeishuSource,
|
||
} from "../lib/feishu-client.ts";
|
||
|
||
const bindings = {
|
||
FEISHU_APP_ID: "cli_test",
|
||
FEISHU_APP_SECRET: "secret_test",
|
||
};
|
||
|
||
function apiResponse(data) {
|
||
return Response.json({ code: 0, msg: "success", data });
|
||
}
|
||
|
||
function fakeFeishu(options = {}) {
|
||
const calls = [];
|
||
const fetchImpl = async (input, init = {}) => {
|
||
const url = new URL(String(input));
|
||
calls.push({ url, init });
|
||
if (url.pathname.endsWith("/tenant_access_token/internal")) {
|
||
return Response.json({
|
||
code: 0,
|
||
msg: "success",
|
||
tenant_access_token: "tenant-test",
|
||
expire: 7200,
|
||
});
|
||
}
|
||
if (url.pathname.endsWith("/wiki/v2/spaces/get_node")) {
|
||
return apiResponse({
|
||
node: {
|
||
obj_type: "sheet",
|
||
obj_token: "spreadsheet-test",
|
||
},
|
||
});
|
||
}
|
||
if (url.pathname.endsWith("/sheets/query")) {
|
||
return apiResponse({
|
||
sheets:
|
||
options.sheets ??
|
||
[
|
||
{
|
||
sheet_id: "sheet-one",
|
||
title: "内容池",
|
||
resource_type: "sheet",
|
||
hidden: false,
|
||
grid_properties: { row_count: 20, column_count: 10 },
|
||
},
|
||
],
|
||
});
|
||
}
|
||
if (url.pathname.endsWith("/values_batch_get")) {
|
||
return apiResponse({
|
||
valueRanges: [
|
||
{
|
||
values: [
|
||
["作品ID", "标题", "标签", "正文", null, null],
|
||
[
|
||
7,
|
||
"一篇测试笔记",
|
||
"#测试 #KOC",
|
||
"测试正文",
|
||
{
|
||
type: "embed-image",
|
||
fileToken: "file-token-one",
|
||
width: 1080,
|
||
height: 1440,
|
||
},
|
||
[
|
||
{
|
||
type: "embed-image",
|
||
fileToken: "file-token-two",
|
||
width: 1080,
|
||
height: 1440,
|
||
},
|
||
],
|
||
],
|
||
[8, "", "", "空标题不会导入"],
|
||
],
|
||
},
|
||
],
|
||
});
|
||
}
|
||
throw new Error(`Unexpected request: ${url.pathname}`);
|
||
};
|
||
return { calls, fetchImpl };
|
||
}
|
||
|
||
test("resolves a wiki sheet and imports title, body, tags, and all images", async () => {
|
||
clearFeishuAccessTokenCacheForTests();
|
||
const { calls, fetchImpl } = fakeFeishu();
|
||
const source = await readFeishuSource(
|
||
"https://tenant.feishu.cn/wiki/wiki-test",
|
||
bindings,
|
||
fetchImpl,
|
||
);
|
||
|
||
assert.equal(source.spreadsheetToken, "spreadsheet-test");
|
||
assert.equal(source.sheetId, "sheet-one");
|
||
assert.equal(source.sheetName, "内容池");
|
||
assert.equal(source.rows.length, 1);
|
||
assert.equal(source.rows[0].sourceRow, 7);
|
||
assert.equal(source.rows[0].body, "测试正文\n\n#测试 #KOC");
|
||
assert.deepEqual(
|
||
source.rows[0].images.map((image) => image.fileToken),
|
||
["file-token-one", "file-token-two"],
|
||
);
|
||
assert.deepEqual(source.columns, [
|
||
"作品ID",
|
||
"标题",
|
||
"标签",
|
||
"正文",
|
||
"图片1",
|
||
"图片2",
|
||
]);
|
||
const valuesCall = calls.find((call) =>
|
||
call.url.pathname.endsWith("/values_batch_get"),
|
||
);
|
||
assert.equal(valuesCall.url.searchParams.get("ranges"), "sheet-one!A1:J20");
|
||
});
|
||
|
||
test("imports video attachments from a Feishu video task sheet", async () => {
|
||
clearFeishuAccessTokenCacheForTests();
|
||
const { fetchImpl } = fakeFeishu();
|
||
const videoFetch = async (input, init = {}) => {
|
||
const url = new URL(String(input));
|
||
if (url.pathname.endsWith("/values_batch_get")) {
|
||
return apiResponse({
|
||
valueRanges: [
|
||
{
|
||
values: [
|
||
["标题", "内容(标题+正文+tag)", "视频"],
|
||
[
|
||
"一条测试视频",
|
||
"一条测试视频\n视频正文 #测试",
|
||
{
|
||
type: "attachment",
|
||
fileToken: "video-token-one",
|
||
text: "demo.mp4",
|
||
mimeType: "video/mp4",
|
||
size: 1024,
|
||
},
|
||
],
|
||
],
|
||
},
|
||
],
|
||
});
|
||
}
|
||
return fetchImpl(input, init);
|
||
};
|
||
const source = await readFeishuSource(
|
||
"https://tenant.feishu.cn/wiki/wiki-test",
|
||
bindings,
|
||
videoFetch,
|
||
);
|
||
|
||
assert.equal(source.rows.length, 1);
|
||
assert.equal(source.rows[0].title, "一条测试视频");
|
||
assert.equal(source.rows[0].body, "一条测试视频\n视频正文 #测试");
|
||
assert.deepEqual(source.rows[0].videos, [
|
||
{
|
||
index: 1,
|
||
fileToken: "video-token-one",
|
||
name: "demo.mp4",
|
||
mimeType: "video/mp4",
|
||
size: 1024,
|
||
},
|
||
]);
|
||
});
|
||
|
||
test("requires an exact sheet link when a workbook has multiple visible sheets", async () => {
|
||
clearFeishuAccessTokenCacheForTests();
|
||
const { fetchImpl } = fakeFeishu({
|
||
sheets: [
|
||
{
|
||
sheet_id: "one",
|
||
title: "内容A",
|
||
resource_type: "sheet",
|
||
hidden: false,
|
||
},
|
||
{
|
||
sheet_id: "two",
|
||
title: "内容B",
|
||
resource_type: "sheet",
|
||
hidden: false,
|
||
},
|
||
],
|
||
});
|
||
|
||
await assert.rejects(
|
||
readFeishuSource(
|
||
"https://tenant.feishu.cn/wiki/wiki-test",
|
||
bindings,
|
||
fetchImpl,
|
||
),
|
||
/包含多个工作表/,
|
||
);
|
||
});
|
||
|
||
test("rejects a wiki node that is not a spreadsheet", async () => {
|
||
clearFeishuAccessTokenCacheForTests();
|
||
const fetchImpl = async (input) => {
|
||
const url = new URL(String(input));
|
||
if (url.pathname.endsWith("/tenant_access_token/internal")) {
|
||
return Response.json({
|
||
code: 0,
|
||
msg: "success",
|
||
tenant_access_token: "tenant-test",
|
||
expire: 7200,
|
||
});
|
||
}
|
||
return apiResponse({
|
||
node: { obj_type: "docx", obj_token: "document-test" },
|
||
});
|
||
};
|
||
|
||
await assert.rejects(
|
||
readFeishuSource(
|
||
"https://tenant.feishu.cn/wiki/wiki-test",
|
||
bindings,
|
||
fetchImpl,
|
||
),
|
||
/不是电子表格/,
|
||
);
|
||
});
|