工作成果里摆着别单的资料:「这一单产出的」认本单碰过的行

放行存档(archiveAssetFiles)与在办列单(assetFilesOf)取行都用 taskView(where:{}) ——
那是「正式版全表 + 本单草稿」的合并视图,而资料常落在主数据上,整表里躺着别单放行过的行。
抽 assetRowsOf 两处同用,touched 为必填入参;e2e 补份数断言。
顺手清两笔:wake.test.ts 的 as unknown as(lint 红)、wake 契约的 api-report 快照(api:check 红)。
This commit is contained in:
xcs
2026-08-25 15:38:10 +08:00
parent 094386f02c
commit d788140548
9 changed files with 260 additions and 57 deletions
@@ -8,7 +8,7 @@
*/
import { describe, expect, it } from 'vitest';
import type { TaskId } from '@contracts';
import { archiveAssetFiles, keyText } from '../asset-archive.ts';
import { archiveAssetFiles, assetRowsOf, keyText } from '../asset-archive.ts';
const TASK = 't-1' as TaskId;
@@ -54,6 +54,11 @@ describe('keyText —— 主键拼法必须稳定', () => {
});
});
/** 本单碰过的行 —— `ledger.taskDrafts()` 那一份的形状 */
function touch(...ids: string[]) {
return ids.map((content_id) => ({ domain: 'content', object: 'asset', key: { content_id } }));
}
describe('archiveAssetFiles —— 存哪些、不存哪些', () => {
it('一行一份,存下来的是真字节(存几份就写几笔)', async () => {
const db = fakeDb();
@@ -62,6 +67,7 @@ describe('archiveAssetFiles —— 存哪些、不存哪些', () => {
taskId: TASK,
ids,
declarations: [ASSET],
touched: touch('CT-1', 'CT-2'),
rowsOfAddress: async () => [
{ content_id: 'CT-1', title: '白皮书', body_md: '# 白皮书\n\n正文', file_format: 'docx' },
{ content_id: 'CT-2', title: '对照表', body_md: '| a | b |\n| - | - |\n| 1 | 2 |', file_format: 'xlsx' },
@@ -72,6 +78,56 @@ describe('archiveAssetFiles —— 存哪些、不存哪些', () => {
expect(db.writes.join()).toContain('白皮书');
});
/*
* 2026-08-25 柴浩实测:「工作成果怎么有 4 个资料,这单只会产出一个」——
* 资料落在**主数据**上,整表里躺着历次跑出来的资产,前三份都是别单的成果。
*/
it('**别单放行过的行不算这一单产出的** —— 整表四行,本单只碰过一行,就只存一份', async () => {
const db = fakeDb();
const saved = await archiveAssetFiles({
db,
taskId: TASK,
ids,
declarations: [ASSET],
touched: touch('CT-2026-0004'),
rowsOfAddress: async () => [
{ content_id: 'CT-2026-0001', title: '白皮书', body_md: '别单的正文', file_format: 'docx' },
{ content_id: 'CT-2026-0002', title: '白皮书 2026', body_md: '别单的正文', file_format: 'docx' },
{ content_id: 'CT-2026-0003', title: '产品介绍书', body_md: '别单的正文', file_format: 'docx' },
{ content_id: 'CT-2026-0004', title: '白皮书 E04 定制版', body_md: '这一单写的正文', file_format: 'docx' },
],
});
expect(saved).toBe(1);
expect(db.writes.join()).toContain('CT-2026-0004');
expect(db.writes.join()).not.toContain('CT-2026-0001');
});
it('这一单一行也没碰过:表里再多行也存 0 份', async () => {
const db = fakeDb();
const saved = await archiveAssetFiles({
db,
taskId: TASK,
ids,
declarations: [ASSET],
touched: [],
rowsOfAddress: async () => [{ content_id: 'CT-1', title: 'T', body_md: '正文', file_format: 'md' }],
});
expect(saved).toBe(0);
});
it('别的表碰过不算这张表碰过 —— 地址得对上', async () => {
const db = fakeDb();
const saved = await archiveAssetFiles({
db,
taskId: TASK,
ids,
declarations: [ASSET],
touched: [{ domain: 'content_brand', object: 'requirement', key: { content_id: 'CT-1' } }],
rowsOfAddress: async () => [{ content_id: 'CT-1', title: 'T', body_md: '正文', file_format: 'md' }],
});
expect(saved).toBe(0);
});
it('**没正文的行不存** —— 一份空壳存进去,人点开只会看见一张白纸', async () => {
const db = fakeDb();
const saved = await archiveAssetFiles({
@@ -79,6 +135,7 @@ describe('archiveAssetFiles —— 存哪些、不存哪些', () => {
taskId: TASK,
ids,
declarations: [ASSET],
touched: touch('CT-1'),
rowsOfAddress: async () => [{ content_id: 'CT-1', title: '还没写', body_md: ' ', file_format: 'md' }],
});
expect(saved).toBe(0);
@@ -92,6 +149,7 @@ describe('archiveAssetFiles —— 存哪些、不存哪些', () => {
taskId: TASK,
ids,
declarations: [ASSET],
touched: touch(''),
rowsOfAddress: async () => [{ content_id: '', title: 'T', body_md: '正文', file_format: 'md' }],
});
expect(saved).toBe(0);
@@ -104,6 +162,7 @@ describe('archiveAssetFiles —— 存哪些、不存哪些', () => {
taskId: TASK,
ids,
declarations: [{ ...ASSET, file: undefined }],
touched: touch('CT-1'),
rowsOfAddress: async () => [{ content_id: 'CT-1', title: 'T', body_md: '正文', file_format: 'md' }],
});
expect(saved).toBe(0);
@@ -116,6 +175,7 @@ describe('archiveAssetFiles —— 存哪些、不存哪些', () => {
taskId: TASK,
ids,
declarations: [ASSET],
touched: touch('CT-1'),
rowsOfAddress: async () => [{ content_id: 'CT-1', title: 'T', body_md: '正文', file_format: 'pdf' }],
});
expect(saved).toBe(1);
@@ -125,7 +185,32 @@ describe('archiveAssetFiles —— 存哪些、不存哪些', () => {
it('大多数活动不产资料 —— 一张带 file 段的表都没有时返回 0,不报错', async () => {
const db = fakeDb();
expect(
await archiveAssetFiles({ db, taskId: TASK, ids, declarations: [], rowsOfAddress: async () => [] }),
await archiveAssetFiles({ db, taskId: TASK, ids, declarations: [], touched: [], rowsOfAddress: async () => [] }),
).toBe(0);
});
});
describe('assetRowsOf —— 存档与工作成果同一段', () => {
it('值取合并行那一份:这轮只改了标题,正文仍按落库后的样子造', () => {
const picked = assetRowsOf(
ASSET,
[{ content_id: 'CT-1', title: '改过的标题', body_md: '早先写下的正文', file_format: 'docx' }],
touch('CT-1'),
);
expect(picked).toEqual([
{
address: 'content.asset',
key: { content_id: 'CT-1' },
title: '改过的标题',
format: 'docx',
markdown: '早先写下的正文',
},
]);
});
it('标题空着:退主键,不摆一份没名字的资料', () => {
const picked = assetRowsOf(ASSET, [{ content_id: 'CT-1', title: ' ', body_md: '正文' }], touch('CT-1'));
expect(picked[0]?.title).toBe('CT-1');
expect(picked[0]?.format).toBe('md');
});
});