172 lines
No EOL
6.2 KiB
JavaScript
172 lines
No EOL
6.2 KiB
JavaScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { NotFoundException } from '@nestjs/common';
|
|
import { SessionService } from '../modules/session/session.service';
|
|
function makeSession(overrides = {}) {
|
|
return {
|
|
id: 'session-uuid-1',
|
|
personaId: 'miku',
|
|
userId: null,
|
|
lastActivityAt: new Date('2026-04-01T00:00:00Z'),
|
|
expiresAt: new Date('2026-04-02T00:00:00Z'),
|
|
createdAt: new Date('2026-04-01T00:00:00Z'),
|
|
updatedAt: new Date('2026-04-01T00:00:00Z'),
|
|
...overrides
|
|
};
|
|
}
|
|
function makeMessage(overrides = {}) {
|
|
return {
|
|
id: 'msg-uuid-1',
|
|
sessionId: 'session-uuid-1',
|
|
role: 'user',
|
|
content: 'Hello',
|
|
emotion: null,
|
|
createdAt: new Date('2026-04-01T00:00:00Z'),
|
|
updatedAt: new Date('2026-04-01T00:00:00Z'),
|
|
...overrides
|
|
};
|
|
}
|
|
function makeSessionRepo(overrides = {}) {
|
|
return {
|
|
create: vi.fn((data)=>({
|
|
...data
|
|
})),
|
|
save: vi.fn(async (entity)=>({
|
|
id: 'session-uuid-1',
|
|
...entity
|
|
})),
|
|
findOneBy: vi.fn(async ()=>makeSession()),
|
|
update: vi.fn(async ()=>({
|
|
affected: 1
|
|
})),
|
|
delete: vi.fn(async ()=>({
|
|
affected: 1
|
|
})),
|
|
find: vi.fn(async ()=>[]),
|
|
...overrides
|
|
};
|
|
}
|
|
function makeMessageRepo(overrides = {}) {
|
|
return {
|
|
create: vi.fn((data)=>({
|
|
...data
|
|
})),
|
|
save: vi.fn(async (entity)=>({
|
|
id: 'msg-uuid-1',
|
|
...entity
|
|
})),
|
|
find: vi.fn(async ()=>[]),
|
|
...overrides
|
|
};
|
|
}
|
|
describe('SessionService', ()=>{
|
|
let service;
|
|
let sessionRepo;
|
|
let messageRepo;
|
|
beforeEach(()=>{
|
|
sessionRepo = makeSessionRepo();
|
|
messageRepo = makeMessageRepo();
|
|
service = new SessionService(sessionRepo, messageRepo);
|
|
});
|
|
describe('createSession', ()=>{
|
|
it('creates a session with default persona miku', async ()=>{
|
|
const result = await service.createSession({});
|
|
expect(sessionRepo.create).toHaveBeenCalledWith(expect.objectContaining({
|
|
personaId: 'miku',
|
|
userId: null
|
|
}));
|
|
expect(result).toBeDefined();
|
|
});
|
|
it('creates a session with specified persona and user', async ()=>{
|
|
await service.createSession({
|
|
personaId: 'quinn',
|
|
userId: 'user-123'
|
|
});
|
|
expect(sessionRepo.create).toHaveBeenCalledWith(expect.objectContaining({
|
|
personaId: 'quinn',
|
|
userId: 'user-123'
|
|
}));
|
|
});
|
|
it('sets expiresAt 24 hours in the future', async ()=>{
|
|
await service.createSession({});
|
|
const call = sessionRepo.create.mock.calls[0][0];
|
|
const diffMs = call.expiresAt.getTime() - call.lastActivityAt.getTime();
|
|
expect(diffMs).toBe(24 * 60 * 60 * 1000);
|
|
});
|
|
});
|
|
describe('getSession', ()=>{
|
|
it('returns the session when found', async ()=>{
|
|
const session = await service.getSession('session-uuid-1');
|
|
expect(session.id).toBe('session-uuid-1');
|
|
});
|
|
it('throws NotFoundException when session not found', async ()=>{
|
|
sessionRepo.findOneBy = vi.fn(async ()=>null);
|
|
await expect(service.getSession('missing')).rejects.toThrow(NotFoundException);
|
|
});
|
|
});
|
|
describe('deleteSession', ()=>{
|
|
it('deletes an existing session', async ()=>{
|
|
await expect(service.deleteSession('session-uuid-1')).resolves.toBeUndefined();
|
|
expect(sessionRepo.delete).toHaveBeenCalledWith('session-uuid-1');
|
|
});
|
|
it('throws NotFoundException when session does not exist', async ()=>{
|
|
sessionRepo.delete = vi.fn(async ()=>({
|
|
affected: 0
|
|
}));
|
|
await expect(service.deleteSession('missing')).rejects.toThrow(NotFoundException);
|
|
});
|
|
});
|
|
describe('getHistory', ()=>{
|
|
it('returns mapped message DTOs', async ()=>{
|
|
messageRepo.find = vi.fn(async ()=>[
|
|
makeMessage()
|
|
]);
|
|
const history = await service.getHistory('session-uuid-1');
|
|
expect(history).toHaveLength(1);
|
|
expect(history[0]).toMatchObject({
|
|
id: 'msg-uuid-1',
|
|
session_id: 'session-uuid-1',
|
|
role: 'user',
|
|
content: 'Hello',
|
|
emotion: null
|
|
});
|
|
expect(typeof history[0].created_at).toBe('string');
|
|
});
|
|
it('returns empty array for session with no messages', async ()=>{
|
|
const history = await service.getHistory('session-uuid-1');
|
|
expect(history).toEqual([]);
|
|
});
|
|
it('throws NotFoundException when session not found', async ()=>{
|
|
sessionRepo.findOneBy = vi.fn(async ()=>null);
|
|
await expect(service.getHistory('missing')).rejects.toThrow(NotFoundException);
|
|
});
|
|
});
|
|
describe('appendMessage', ()=>{
|
|
it('saves a user message and returns it', async ()=>{
|
|
messageRepo.save = vi.fn(async (m)=>({
|
|
id: 'msg-uuid-2',
|
|
...m
|
|
}));
|
|
const result = await service.appendMessage({
|
|
sessionId: 'session-uuid-1',
|
|
role: 'user',
|
|
content: 'Hello'
|
|
});
|
|
expect(result).toBeDefined();
|
|
expect(sessionRepo.update).toHaveBeenCalledWith('session-uuid-1', expect.objectContaining({
|
|
lastActivityAt: expect.any(Date)
|
|
}));
|
|
});
|
|
it('uses neutral as default emotion when not specified', async ()=>{
|
|
await service.appendMessage({
|
|
sessionId: 'session-uuid-1',
|
|
role: 'assistant',
|
|
content: 'Hi there'
|
|
});
|
|
expect(messageRepo.create).toHaveBeenCalledWith(expect.objectContaining({
|
|
emotion: null
|
|
}));
|
|
});
|
|
});
|
|
});
|
|
|
|
//# sourceMappingURL=session.service.spec.js.map
|