This commit is contained in:
浅念
2026-08-27 16:11:49 +08:00
commit 2d8ef3d7bb
104 changed files with 20196 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import request from 'supertest';
import { setupApp, resetDb, teardown } from './setup.js';
import { makePayload } from './fixtures.js';
import { getPrisma } from '../src/lib/prisma.js';
let app: any;
beforeAll(async () => {
({ app } = await setupApp());
}, 90000);
afterAll(async () => {
await teardown();
});
async function login() {
const r = await request(app)
.post('/api/auth/login')
.send({ username: 'admin', password: 'admin-password-1234' });
return r.body.token;
}
async function pushInquiry(payload: any) {
await request(app)
.post('/api/ingest')
.set('Authorization', 'Bearer dev-ingest-token-1234')
.send(payload);
}
describe('inquiries list', () => {
it('lists filtered by country', async () => {
await resetDb();
await pushInquiry(makePayload({ customer: { country: 'PE' } }));
await pushInquiry(
makePayload({ inquiry: { inquiryId: 'X2' }, customer: { customerId: 'c2', country: 'US' } }),
);
const token = await login();
const res = await request(app)
.get('/api/inquiries?country=PE')
.set('Authorization', 'Bearer ' + token);
expect(res.status).toBe(200);
expect(res.body.items.length).toBe(1);
expect(res.body.items[0].customer.country).toBe('PE');
expect(res.body).toHaveProperty('total');
expect(res.body).toHaveProperty('page');
expect(res.body).toHaveProperty('size');
});
it('401 without jwt', async () => {
const res = await request(app).get('/api/inquiries');
expect(res.status).toBe(401);
});
});
describe('inquiries detail', () => {
it('gets inquiry detail with customer + message count', async () => {
await resetDb();
await pushInquiry(makePayload()); // inquiryId='13776617920', 2 messages
const token = await login();
const res = await request(app)
.get('/api/inquiries/13776617920')
.set('Authorization', 'Bearer ' + token);
expect(res.status).toBe(200);
expect(res.body.inquiryId).toBe('13776617920');
expect(res.body.customer).toBeTruthy();
expect(res.body._count.messages).toBe(2);
});
it('404 for unknown inquiry', async () => {
const token = await login();
const res = await request(app)
.get('/api/inquiries/NOPE')
.set('Authorization', 'Bearer ' + token);
expect(res.status).toBe(404);
});
});
describe('inquiry messages pagination', () => {
// 造 50 条消息(sendTime 各不同),用于测默认从最新 + before 向上翻页
async function seed50() {
await resetDb();
const prisma = getPrisma();
await pushInquiry(makePayload()); // 建 inquiry + 2 条消息
const base = Date.parse('2026-07-13T02:12:00Z');
await prisma.chatMessage.createMany({
data: Array.from({ length: 48 }, (_, i) => ({
messageId: 'p' + i,
inquiryId: '13776617920',
sender: 'x',
direction: 'incoming' as const,
sendTime: new Date(base + (i + 1) * 1000),
content: 'c' + i,
translation: '',
})),
});
}
it('默认无 cursor 返回最新一页(asc 旧→新)+ prevCursor 指向更旧', async () => {
await seed50();
const token = await login();
const r1 = await request(app)
.get('/api/inquiries/13776617920/messages?size=20')
.set('Authorization', 'Bearer ' + token);
expect(r1.status).toBe(200);
expect(r1.body.items.length).toBe(20);
expect(r1.body.prevCursor).toBeTruthy();
// 默认从最新:返回的 20 条应是全局最新的 20 条,且按 sendTime 升序(顶旧底新)
const ts = r1.body.items.map((m: any) => new Date(m.sendTime).getTime());
for (let i = 1; i < ts.length; i++) expect(ts[i]).toBeGreaterThanOrEqual(ts[i - 1]);
// 最新一条(全局最大 sendTime)应在 items 末尾
const prisma = getPrisma();
const maxRow = await prisma.chatMessage.findFirst({
where: { inquiryId: '13776617920' },
orderBy: { sendTime: 'desc' },
});
expect(ts[ts.length - 1]).toBe(maxRow!.sendTime.getTime());
});
it('before cursor 向上加载更旧,与已加载不重叠', async () => {
await seed50();
const token = await login();
const r1 = await request(app)
.get('/api/inquiries/13776617920/messages?size=20')
.set('Authorization', 'Bearer ' + token);
const c = r1.body.prevCursor;
expect(c).toBeTruthy();
const r2 = await request(app)
.get(
`/api/inquiries/13776617920/messages?size=20&beforeSendTime=${encodeURIComponent(c.beforeSendTime)}&beforeMessageId=${c.beforeMessageId}`,
)
.set('Authorization', 'Bearer ' + token);
expect(r2.status).toBe(200);
expect(r2.body.items.length).toBe(20);
// 不重叠
const ids1 = new Set(r1.body.items.map((m: any) => m.messageId));
expect(r2.body.items.every((m: any) => !ids1.has(m.messageId))).toBe(true);
// r2 全部比 r1 第一条更旧
const r1First = new Date(r1.body.items[0].sendTime).getTime();
expect(r2.body.items.every((m: any) => new Date(m.sendTime).getTime() <= r1First)).toBe(true);
});
it('400 when only one cursor field provided (after or before)', async () => {
const token = await login();
const r1 = await request(app)
.get('/api/inquiries/13776617920/messages?afterSendTime=2026-07-13T02:12:00Z')
.set('Authorization', 'Bearer ' + token);
expect(r1.status).toBe(400);
const r2 = await request(app)
.get('/api/inquiries/13776617920/messages?beforeSendTime=2026-07-13T02:12:00Z')
.set('Authorization', 'Bearer ' + token);
expect(r2.status).toBe(400);
});
});