109 lines
4.3 KiB
TypeScript
109 lines
4.3 KiB
TypeScript
import { fixtureFetch } from './backup-fixture';
|
|
import 'dotenv/config';
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { randomUUID, randomBytes, createHash } from 'node:crypto';
|
|
import { PrismaClient } from '@prisma/client';
|
|
import { readBackupZip } from '../src/zip';
|
|
const base = process.env.TEST_API_URL || 'http://127.0.0.1:3100/api';
|
|
test('group order persists per user, validates input and survives ZIP and reject incomplete imports', async () => {
|
|
const db = new PrismaClient(),
|
|
ids: string[] = [];
|
|
const origin = process.env.WEB_ORIGIN === '*' ? 'http://localhost:5173' : process.env.WEB_ORIGIN!;
|
|
async function user() {
|
|
const u = await db.user.create({
|
|
data: { username: 'wp_groups_' + randomUUID(), passwordHash: 'unused' },
|
|
});
|
|
ids.push(u.id);
|
|
const token = randomBytes(32).toString('hex');
|
|
await db.session.create({
|
|
data: {
|
|
id: createHash('sha256').update(token).digest('hex'),
|
|
userId: u.id,
|
|
expiresAt: new Date(Date.now() + 3600000),
|
|
},
|
|
});
|
|
return { id: u.id, cookie: 'wp_session=' + token };
|
|
}
|
|
async function call(path: string, cookie: string, method = 'GET', body?: unknown) {
|
|
const res = await fixtureFetch(base + path, {
|
|
method,
|
|
headers: {
|
|
Cookie: cookie,
|
|
Origin: origin,
|
|
...(body ? { 'Content-Type': 'application/json' } : {}),
|
|
},
|
|
body: body ? JSON.stringify(body) : undefined,
|
|
});
|
|
return { status: res.status, data: await res.json() };
|
|
}
|
|
try {
|
|
const a = await user(),
|
|
b = await user(),
|
|
c = await user();
|
|
assert.deepEqual((await call('/auth/me', a.cookie)).data.accountGroupOrder, []);
|
|
const order = ['理财', '', '未分组', '日常'];
|
|
assert.equal(
|
|
(await call('/settings', a.cookie, 'PATCH', { accountGroupOrder: order })).status,
|
|
200,
|
|
);
|
|
assert.deepEqual(
|
|
(await db.user.findUniqueOrThrow({ where: { id: a.id } })).accountGroupOrder,
|
|
order,
|
|
);
|
|
assert.deepEqual((await call('/auth/me', a.cookie)).data.accountGroupOrder, order);
|
|
assert.deepEqual((await call('/settings', a.cookie)).data.accountGroupOrder, order);
|
|
assert.deepEqual((await call('/settings', b.cookie)).data.accountGroupOrder, []);
|
|
for (const invalid of [['日常', '日常'], [' 日常 ', '日常'], [42], ['x'.repeat(61)]]) {
|
|
assert.equal(
|
|
(await call('/settings', a.cookie, 'PATCH', { accountGroupOrder: invalid })).status,
|
|
400,
|
|
);
|
|
}
|
|
assert.deepEqual((await call('/settings', a.cookie)).data.accountGroupOrder, order);
|
|
assert.equal(
|
|
(
|
|
await call('/positions', a.cookie, 'POST', {
|
|
name: 'Daily',
|
|
kind: 'account',
|
|
side: 'asset',
|
|
category: 'cash',
|
|
groupName: '日常',
|
|
currency: 'CNY',
|
|
amount: '20',
|
|
date: '2026-09-01T10:00',
|
|
})
|
|
).status,
|
|
201,
|
|
);
|
|
const zip = await fetch(base + '/backup', { headers: { Cookie: a.cookie } });
|
|
assert.equal(zip.status, 200);
|
|
const backup: any = await readBackupZip(Buffer.from(await zip.arrayBuffer()));
|
|
assert.deepEqual(backup.preferences.accountGroupOrder, order);
|
|
assert.equal(
|
|
(await call('/backup/restore-fixture', b.cookie, 'POST', { confirmed: true, backup })).status,
|
|
201,
|
|
);
|
|
assert.deepEqual((await call('/auth/me', b.cookie)).data.accountGroupOrder, order);
|
|
await call('/settings', b.cookie, 'PATCH', { accountGroupOrder: ['日常'] });
|
|
assert.equal(
|
|
(await call('/backup/restore-fixture', b.cookie, 'POST', { confirmed: true, backup })).status,
|
|
409,
|
|
);
|
|
assert.deepEqual((await call('/settings', b.cookie)).data.accountGroupOrder, ['日常']);
|
|
delete backup.preferences.accountGroupOrder;
|
|
assert.equal(
|
|
(await call('/backup/restore-fixture', c.cookie, 'POST', { confirmed: true, backup })).status,
|
|
400,
|
|
);
|
|
assert.deepEqual((await call('/auth/me', c.cookie)).data.accountGroupOrder, []);
|
|
const comments: any[] = await db.$queryRawUnsafe(
|
|
"SELECT COLUMN_COMMENT AS comment FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'User' AND COLUMN_NAME = 'accountGroupOrder'",
|
|
);
|
|
assert.ok(comments[0].comment.includes('分组'));
|
|
} finally {
|
|
await db.user.deleteMany({ where: { id: { in: ids } } });
|
|
await db.$disconnect();
|
|
}
|
|
});
|