import { KNOWLEDGE_CONTENT_TYPES, KNOWLEDGE_DOCUMENT_STATUSES, KNOWLEDGE_FILE_TYPES, } from "../knowledgeDocuments.constants"; import { createKnowledgeDocument, deleteKnowledgeDocument, getKnowledgeDocumentById, getKnowledgeDocumentsCollection, listKnowledgeDocumentsByClientId, listReadyKnowledgeDocumentIdsByClientId, listReadyKnowledgeDocumentsByClientId, updateKnowledgeDocument, } from "../knowledgeDocuments.getters"; import { createKnowledgeDocumentFixture } from "../../../test-utils/factories/websitalk/knowledgeDocuments"; describe("db.websitalk.knowledgeDocuments", () => { const clientId = "test-client"; const nonExistentId = "507f1f77bcf86cd799439011"; describe("createKnowledgeDocument", () => { it("should create a knowledge document", async () => { const id = await createKnowledgeDocument({ clientId, filename: "faq.pdf", contentType: KNOWLEDGE_CONTENT_TYPES.PDF, fileType: KNOWLEDGE_FILE_TYPES.PDF, storagePath: "websitalk/knowledge_files/test/faq.pdf", byteSize: 2048, }); const saved = await getKnowledgeDocumentById(id.toString()); expect(saved).toMatchObject({ clientId, filename: "faq.pdf", status: KNOWLEDGE_DOCUMENT_STATUSES.UPLOADING, progressPercent: 0, fileType: KNOWLEDGE_FILE_TYPES.PDF, }); expect(saved?.createdAt).toBeInstanceOf(Date); expect(saved?.updatedAt).toBeInstanceOf(Date); }); }); describe("getKnowledgeDocumentById", () => { it("should throw for an invalid id", async () => { await expect(getKnowledgeDocumentById("invalid-id")).rejects.toThrow(); }); }); describe("listKnowledgeDocumentsByClientId", () => { it("should return documents for a client only", async () => { await getKnowledgeDocumentsCollection().insertMany([ createKnowledgeDocumentFixture({ clientId, filename: "a.pdf" }), createKnowledgeDocumentFixture({ clientId, filename: "b.pdf" }), createKnowledgeDocumentFixture({ clientId: "other-client", filename: "c.pdf", }), ]); const results = await listKnowledgeDocumentsByClientId(clientId); expect(results).toHaveLength(2); expect(results.map((doc) => doc.filename).sort()).toEqual([ "a.pdf", "b.pdf", ]); }); it("should return documents sorted by createdAt descending", async () => { await getKnowledgeDocumentsCollection().insertMany([ createKnowledgeDocumentFixture({ clientId, filename: "older.pdf", createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), }), createKnowledgeDocumentFixture({ clientId, filename: "newer.pdf", createdAt: new Date("2024-06-01"), updatedAt: new Date("2024-06-01"), }), ]); const results = await listKnowledgeDocumentsByClientId(clientId); expect(results.map((doc) => doc.filename)).toEqual([ "newer.pdf", "older.pdf", ]); }); }); describe("listReadyKnowledgeDocumentIdsByClientId", () => { it("should return only READY document ids sorted by createdAt descending", async () => { await getKnowledgeDocumentsCollection().insertMany([ createKnowledgeDocumentFixture({ clientId, status: KNOWLEDGE_DOCUMENT_STATUSES.READY, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), }), createKnowledgeDocumentFixture({ clientId, status: KNOWLEDGE_DOCUMENT_STATUSES.READY, createdAt: new Date("2024-06-01"), updatedAt: new Date("2024-06-01"), }), createKnowledgeDocumentFixture({ clientId, status: KNOWLEDGE_DOCUMENT_STATUSES.PROCESSING, }), createKnowledgeDocumentFixture({ clientId: "other", status: KNOWLEDGE_DOCUMENT_STATUSES.READY, }), ]); const readyDocs = await getKnowledgeDocumentsCollection() .find({ clientId, status: KNOWLEDGE_DOCUMENT_STATUSES.READY }) .sort({ createdAt: -1 }) .toArray(); const expectedIds = readyDocs.map((doc) => doc._id.toString()); const ids = await listReadyKnowledgeDocumentIdsByClientId(clientId); expect(ids).toHaveLength(2); expect(ids).toEqual(expectedIds); }); }); describe("listReadyKnowledgeDocumentsByClientId", () => { it("should return only READY documents sorted by createdAt descending", async () => { await getKnowledgeDocumentsCollection().insertMany([ createKnowledgeDocumentFixture({ clientId, filename: "older.pdf", status: KNOWLEDGE_DOCUMENT_STATUSES.READY, markdownStoragePath: "path/older.md", charCount: 100, totalTokens: 20, createdAt: new Date("2024-01-01"), updatedAt: new Date("2024-01-01"), }), createKnowledgeDocumentFixture({ clientId, filename: "newer.pdf", status: KNOWLEDGE_DOCUMENT_STATUSES.READY, markdownStoragePath: "path/newer.md", charCount: 200, totalTokens: 40, createdAt: new Date("2024-06-01"), updatedAt: new Date("2024-06-01"), }), createKnowledgeDocumentFixture({ clientId, filename: "processing.pdf", status: KNOWLEDGE_DOCUMENT_STATUSES.PROCESSING, }), createKnowledgeDocumentFixture({ clientId: "other", filename: "other.pdf", status: KNOWLEDGE_DOCUMENT_STATUSES.READY, }), ]); const docs = await listReadyKnowledgeDocumentsByClientId(clientId); expect(docs.map((doc) => doc.filename)).toEqual([ "newer.pdf", "older.pdf", ]); expect(docs[0]).toMatchObject({ clientId, filename: "newer.pdf", markdownStoragePath: "path/newer.md", charCount: 200, totalTokens: 40, status: KNOWLEDGE_DOCUMENT_STATUSES.READY, }); }); }); describe("updateKnowledgeDocument", () => { it("should update status and progress", async () => { const id = await createKnowledgeDocument({ clientId, filename: "doc.pdf", contentType: KNOWLEDGE_CONTENT_TYPES.PDF, fileType: KNOWLEDGE_FILE_TYPES.PDF, storagePath: "websitalk/knowledge_files/test/doc.pdf", byteSize: 100, }); const before = await getKnowledgeDocumentById(id.toString()); const updated = await updateKnowledgeDocument(id.toString(), { status: KNOWLEDGE_DOCUMENT_STATUSES.READY, progressPercent: 100, pageCount: 12, }); expect(updated?.status).toBe(KNOWLEDGE_DOCUMENT_STATUSES.READY); expect(updated?.progressPercent).toBe(100); expect(updated?.pageCount).toBe(12); expect(updated?.updatedAt.getTime()).toBeGreaterThanOrEqual( before!.updatedAt.getTime(), ); }); it("should return null when the document does not exist", async () => { expect( await updateKnowledgeDocument(nonExistentId, { status: KNOWLEDGE_DOCUMENT_STATUSES.READY, }), ).toBeNull(); }); }); describe("deleteKnowledgeDocument", () => { it("should delete and return true", async () => { const id = await createKnowledgeDocument({ clientId: "delete-client", filename: "gone.pdf", contentType: KNOWLEDGE_CONTENT_TYPES.PDF, fileType: KNOWLEDGE_FILE_TYPES.PDF, storagePath: "websitalk/knowledge_files/delete/gone.pdf", byteSize: 10, }); expect(await deleteKnowledgeDocument(id.toString())).toBe(true); expect(await getKnowledgeDocumentById(id.toString())).toBeNull(); }); it("should return false when the document does not exist", async () => { expect(await deleteKnowledgeDocument(nonExistentId)).toBe(false); }); }); });