import type { Session } from "$lib/types/Session"; import type { User } from "$lib/types/User"; import type { Conversation } from "$lib/types/Conversation"; import { ObjectId } from "mongodb"; import { deleteConversations } from "./09-delete-empty-conversations"; import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest"; import { collections } from "$lib/server/database"; type Message = Conversation["messages"][number]; const userData = { _id: new ObjectId(), createdAt: new Date(), updatedAt: new Date(), username: "new-username", name: "name", avatarUrl: "https://example.com/avatar.png", hfUserId: "9999999999", } satisfies User; Object.freeze(userData); const sessionForUser = { _id: new ObjectId(), createdAt: new Date(), updatedAt: new Date(), userId: userData._id, sessionId: "session-id-9999999999", expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24), } satisfies Session; Object.freeze(sessionForUser); const userMessage = { from: "user", id: "user-message-id", content: "Hello, how are you?", } satisfies Message; const assistantMessage = { from: "assistant", id: "assistant-message-id", content: "I'm fine, thank you!", } satisfies Message; const systemMessage = { from: "system", id: "system-message-id", content: "This is a system message", } satisfies Message; const conversationBase = { _id: new ObjectId(), createdAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), updatedAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), model: "model-id", embeddingModel: "embedding-model-id", title: "title", messages: [], } satisfies Conversation; describe.sequential("Deleting discarded conversations", async () => { test("a conversation with no messages should get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, sessionId: sessionForUser.sessionId, }); const result = await deleteConversations(collections); expect(result).toBe(1); }); test("a conversation with no messages that is less than 1 hour old should not get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, sessionId: sessionForUser.sessionId, createdAt: new Date(Date.now() - 30 * 60 * 1000), }); const result = await deleteConversations(collections); expect(result).toBe(0); }); test("a conversation with only system messages should get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, sessionId: sessionForUser.sessionId, messages: [systemMessage], }); const result = await deleteConversations(collections); expect(result).toBe(1); }); test("a conversation with a user message should not get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, sessionId: sessionForUser.sessionId, messages: [userMessage], }); const result = await deleteConversations(collections); expect(result).toBe(0); }); test("a conversation with an assistant message should not get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, sessionId: sessionForUser.sessionId, messages: [assistantMessage], }); const result = await deleteConversations(collections); expect(result).toBe(0); }); test("a conversation with a mix of messages should not get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, sessionId: sessionForUser.sessionId, messages: [systemMessage, userMessage, assistantMessage, userMessage, assistantMessage], }); const result = await deleteConversations(collections); expect(result).toBe(0); }); test("a conversation with a userId and no sessionId should not get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, messages: [userMessage, assistantMessage], userId: userData._id, }); const result = await deleteConversations(collections); expect(result).toBe(0); }); test("a conversation with no userId or sessionId should get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, messages: [userMessage, assistantMessage], }); const result = await deleteConversations(collections); expect(result).toBe(1); }); test("a conversation with a sessionId that exists should not get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, messages: [userMessage, assistantMessage], sessionId: sessionForUser.sessionId, }); const result = await deleteConversations(collections); expect(result).toBe(0); }); test("a conversation with a userId and a sessionId that doesn't exist should NOT get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, userId: userData._id, messages: [userMessage, assistantMessage], sessionId: new ObjectId().toString(), }); const result = await deleteConversations(collections); expect(result).toBe(0); }); test("a conversation with only a sessionId that doesn't exist, should get deleted", async () => { await collections.conversations.insertOne({ ...conversationBase, messages: [userMessage, assistantMessage], sessionId: new ObjectId().toString(), }); const result = await deleteConversations(collections); expect(result).toBe(1); }); test("many conversations should get deleted", async () => { const conversations = Array.from({ length: 10010 }, () => ({ ...conversationBase, _id: new ObjectId(), })); await collections.conversations.insertMany(conversations); const result = await deleteConversations(collections); expect(result).toBe(10010); }); }); beforeAll(async () => { await collections.users.insertOne(userData); await collections.sessions.insertOne(sessionForUser); }); afterAll(async () => { await collections.users.deleteOne({ _id: userData._id, }); await collections.sessions.deleteOne({ _id: sessionForUser._id, }); await collections.conversations.deleteMany({}); }); afterEach(async () => { await collections.conversations.deleteMany({ _id: { $in: [conversationBase._id] }, }); });