import { describe, it, expect, beforeEach, vi } from "vitest"; import { Test, TestingModule } from "@nestjs/testing"; import { ConflictException, NotFoundException } from "@nestjs/common"; import { ConversationArchiveService } from "./conversation-archive.service"; import { PrismaService } from "../prisma/prisma.service"; import { EmbeddingService } from "../knowledge/services/embedding.service"; const mockPrisma = { conversationArchive: { findUnique: vi.fn(), create: vi.fn(), count: vi.fn(), findMany: vi.fn(), findFirst: vi.fn(), }, $queryRaw: vi.fn(), $executeRaw: vi.fn(), }; const mockEmbedding = { isConfigured: vi.fn(), generateEmbedding: vi.fn(), }; describe("ConversationArchiveService", () => { let service: ConversationArchiveService; beforeEach(async () => { vi.clearAllMocks(); const module: TestingModule = await Test.createTestingModule({ providers: [ ConversationArchiveService, { provide: PrismaService, useValue: mockPrisma }, { provide: EmbeddingService, useValue: mockEmbedding }, ], }).compile(); service = module.get(ConversationArchiveService); }); describe("ingest", () => { const workspaceId = "ws-1"; const dto = { sessionId: "sess-abc", agentId: "agent-xyz", messages: [ { role: "user", content: "Hello" }, { role: "assistant", content: "Hi there!" }, ], summary: "A greeting conversation", startedAt: "2026-02-28T10:00:00Z", }; it("creates a conversation archive and returns id", async () => { mockPrisma.conversationArchive.findUnique.mockResolvedValue(null); mockPrisma.conversationArchive.create.mockResolvedValue({ id: "conv-1" }); mockEmbedding.isConfigured.mockReturnValue(false); const result = await service.ingest(workspaceId, dto); expect(result).toEqual({ id: "conv-1" }); expect(mockPrisma.conversationArchive.create).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ workspaceId, sessionId: dto.sessionId, agentId: dto.agentId, messageCount: 2, }), }) ); }); it("throws ConflictException when session already exists", async () => { mockPrisma.conversationArchive.findUnique.mockResolvedValue({ id: "existing" }); await expect(service.ingest(workspaceId, dto)).rejects.toThrow(ConflictException); }); }); describe("findAll", () => { const workspaceId = "ws-1"; it("returns paginated list", async () => { mockPrisma.conversationArchive.count.mockResolvedValue(5); mockPrisma.conversationArchive.findMany.mockResolvedValue([ { id: "conv-1", sessionId: "sess-1" }, ]); const result = await service.findAll(workspaceId, { page: 1, limit: 10 }); expect(result.pagination.total).toBe(5); expect(result.data).toHaveLength(1); }); it("uses default pagination when not provided", async () => { mockPrisma.conversationArchive.count.mockResolvedValue(0); mockPrisma.conversationArchive.findMany.mockResolvedValue([]); const result = await service.findAll(workspaceId, {}); expect(result.pagination.page).toBe(1); expect(result.pagination.limit).toBe(20); }); }); describe("findOne", () => { const workspaceId = "ws-1"; it("returns record when found", async () => { const record = { id: "conv-1", workspaceId, sessionId: "sess-1" }; mockPrisma.conversationArchive.findFirst.mockResolvedValue(record); const result = await service.findOne(workspaceId, "conv-1"); expect(result).toEqual(record); }); it("throws NotFoundException when record does not exist", async () => { mockPrisma.conversationArchive.findFirst.mockResolvedValue(null); await expect(service.findOne(workspaceId, "missing")).rejects.toThrow(NotFoundException); }); }); describe("search", () => { it("throws ConflictException when embedding is not configured", async () => { mockEmbedding.isConfigured.mockReturnValue(false); await expect(service.search("ws-1", { query: "test query" })).rejects.toThrow( ConflictException ); }); it("performs vector search when configured", async () => { mockEmbedding.isConfigured.mockReturnValue(true); mockEmbedding.generateEmbedding.mockResolvedValue(new Array(1536).fill(0.1)); mockPrisma.$queryRaw .mockResolvedValueOnce([{ id: "conv-1", similarity: 0.9 }]) .mockResolvedValueOnce([{ count: BigInt(1) }]); const result = await service.search("ws-1", { query: "greetings" }); expect(result.data).toHaveLength(1); expect(result.pagination.total).toBe(1); }); }); });