test(api): pin earliest-uploadedAt + uploadEnabled-agnostic pre-check; fix comment
评审硬化三项(checkUploadDuplicates 后续,不 amend): - e2e 补「同 bytes 多条可见命中取最早 uploadedAt」:两条同 bytes 不同 uploadedAt 的种子行,断言回显更早值。 - e2e 补「uploadEnabled=false 预检仍 200 且照常命中」:钉住只读预览刻意 不校验开关的偏差——防止未来一致性重构把检查加回来,破坏 App 关册时的 降级体验(init 入队时仍有 409 兜底)。 - 修正 uploads.service checkDuplicates 的 null-uploadedAt 注释:原「谓词 命中与否不受影响」不准确,改为「仅直改库等理论形状(bytes>0 且 uploadedAt=null)下预检可能漏报而 init 仍会 409」。 uploads e2e 30/30 ✅ · api typecheck ✅ Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -594,8 +594,8 @@ export class UploadsService {
|
|||||||
const earliest = new Map<number, Date>();
|
const earliest = new Map<number, Date>();
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
// bytes>0 的行只会经 complete 落定(同事务写 uploadedAt);null 仅理论
|
// bytes>0 的行只会经 complete 落定(同事务写 uploadedAt);null 仅理论
|
||||||
// 形状(直改库等),跳过不参与映射——谓词命中与否不受影响(uploadedAt
|
// 形状(直改库等,bytes>0 且 uploadedAt=null),跳过不参与映射——该形状
|
||||||
// 只是回显元数据)。
|
// 下预检可能漏报而 init 仍会 409(偏差仅限于此理论形状,正常写路径不可达)。
|
||||||
if (row.uploadedAt === null) {
|
if (row.uploadedAt === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1117,6 +1117,46 @@ describe('uploads duplicates check (e2e)', () => {
|
|||||||
expect(res.body.duplicates).toEqual([]);
|
expect(res.body.duplicates).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('同 bytes 多条可见命中取最早 uploadedAt(force 允许的重复行)', async () => {
|
||||||
|
const bytes = 555_555;
|
||||||
|
await seedPhoto(memberAId, bytes, {
|
||||||
|
uploadedAt: new Date('2026-09-25T08:00:00.000Z'),
|
||||||
|
});
|
||||||
|
await seedPhoto(memberAId, bytes, {
|
||||||
|
uploadedAt: new Date('2026-09-10T08:00:00.000Z'), // 更早,应回显此值
|
||||||
|
});
|
||||||
|
const res = await check(memberAToken, {
|
||||||
|
albumId,
|
||||||
|
items: [item('e.jpg', bytes)],
|
||||||
|
});
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
expect(res.body.duplicates).toEqual([
|
||||||
|
{ index: 0, bytes, uploadedAt: '2026-09-10T08:00:00.000Z' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uploadEnabled=false:预检仍 200 且照常命中(只读预览刻意不校验开关;init 入队时仍 409 兜底)', async () => {
|
||||||
|
await prisma.albums.update({
|
||||||
|
where: { id: albumId },
|
||||||
|
data: { uploadEnabled: false },
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const res = await check(memberAToken, {
|
||||||
|
albumId,
|
||||||
|
items: [item('a.jpg', BYTES_A)],
|
||||||
|
});
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
expect(res.body.duplicates).toEqual([
|
||||||
|
{ index: 0, bytes: BYTES_A, uploadedAt: UPLOADED_AT_A },
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
await prisma.albums.update({
|
||||||
|
where: { id: albumId },
|
||||||
|
data: { uploadEnabled: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('非成员 → 404 ALBUM_NOT_FOUND(防枚举,同 init)', async () => {
|
it('非成员 → 404 ALBUM_NOT_FOUND(防枚举,同 init)', async () => {
|
||||||
const res = await check(outsiderToken, {
|
const res = await check(outsiderToken, {
|
||||||
albumId,
|
albumId,
|
||||||
|
|||||||
Reference in New Issue
Block a user