后台
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user