const couchdbService = require("../../services/couchdbService"); describe("CouchDB Service", () => { beforeAll(async () => { // Note: These tests require CouchDB to be running // They will be skipped if CouchDB is not available try { await couchdbService.initialize(); } catch (error) { console.log("CouchDB not available, skipping tests"); } }); describe("Connection", () => { test("should initialize connection", async () => { if (!couchdbService.isReady()) { console.log("Skipping test - CouchDB not available"); return; } expect(couchdbService.isReady()).toBe(true); expect(couchdbService.getDB()).toBeDefined(); }); test("should check connection status", () => { const isReady = couchdbService.isReady(); expect(typeof isReady).toBe("boolean"); }); }); describe("Document Operations", () => { test("should create and retrieve a document", async () => { if (!couchdbService.isReady()) { console.log("Skipping test - CouchDB not available"); return; } const testDoc = { _id: "test_doc_1", type: "test", name: "Test Document", createdAt: new Date().toISOString() }; // Create document const created = await couchdbService.createDocument(testDoc); expect(created._id).toBe(testDoc._id); expect(created._rev).toBeDefined(); // Retrieve document const retrieved = await couchdbService.getDocument(testDoc._id); expect(retrieved._id).toBe(testDoc._id); expect(retrieved.name).toBe(testDoc.name); // Clean up await couchdbService.deleteDocument(testDoc._id, created._rev); }); test("should update a document", async () => { if (!couchdbService.isReady()) { console.log("Skipping test - CouchDB not available"); return; } const testDoc = { _id: "test_doc_2", type: "test", name: "Original Name", createdAt: new Date().toISOString() }; // Create document const created = await couchdbService.createDocument(testDoc); // Update document created.name = "Updated Name"; const updated = await couchdbService.updateDocument(created); expect(updated.name).toBe("Updated Name"); expect(updated._rev).not.toBe(created._rev); // Clean up await couchdbService.deleteDocument(testDoc._id, updated._rev); }); test("should find documents by selector", async () => { if (!couchdbService.isReady()) { console.log("Skipping test - CouchDB not available"); return; } const testDocs = [ { _id: "test_doc_3a", type: "test", category: "A", name: "Test A", createdAt: new Date().toISOString() }, { _id: "test_doc_3b", type: "test", category: "B", name: "Test B", createdAt: new Date().toISOString() } ]; // Create documents const created = await couchdbService.bulkDocs({ docs: testDocs }); // Find by type const foundByType = await couchdbService.findByType("test"); expect(foundByType.length).toBeGreaterThanOrEqual(2); // Find by category const foundByCategory = await couchdbService.find({ selector: { type: "test", category: "A" } }); expect(foundByCategory.length).toBe(1); expect(foundByCategory[0].category).toBe("A"); // Clean up for (let i = 0; i < testDocs.length; i++) { if (created[i].ok) { await couchdbService.deleteDocument(testDocs[i]._id, created[i].rev); } } }); }); describe("Helper Functions", () => { test("should generate and extract IDs correctly", () => { const originalId = "1234567890abcdef"; const type = "user"; const prefixedId = couchdbService.generateId(type, originalId); expect(prefixedId).toBe(`${type}_${originalId}`); const extractedId = couchdbService.extractOriginalId(prefixedId); expect(extractedId).toBe(originalId); }); test("should validate documents correctly", () => { const validDoc = { type: "user", name: "John Doe", email: "john@example.com" }; const invalidDoc = { name: "John Doe" // Missing type }; const validErrors = couchdbService.validateDocument(validDoc, ["email"]); expect(validErrors).toHaveLength(0); const invalidErrors = couchdbService.validateDocument(invalidDoc, ["email"]); expect(invalidErrors.length).toBeGreaterThan(0); expect(invalidErrors.some(e => e.includes("type"))).toBe(true); }); }); describe("User-specific Operations", () => { test("should find user by email", async () => { if (!couchdbService.isReady()) { console.log("Skipping test - CouchDB not available"); return; } const testUser = { _id: "user_test_1", type: "user", name: "Test User", email: "test@example.com", password: "hashedpassword", points: 100, adoptedStreets: [], completedTasks: [], posts: [], events: [], earnedBadges: [], stats: { streetsAdopted: 0, tasksCompleted: 0, postsCreated: 0, eventsParticipated: 0, badgesEarned: 0 }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; // Create user const created = await couchdbService.createDocument(testUser); // Find by email const found = await couchdbService.findUserByEmail("test@example.com"); expect(found).toBeTruthy(); expect(found.email).toBe("test@example.com"); expect(found.name).toBe("Test User"); // Clean up await couchdbService.deleteDocument(testUser._id, created._rev); }); }); describe("Geospatial Operations", () => { test("should find streets by location", async () => { if (!couchdbService.isReady()) { console.log("Skipping test - CouchDB not available"); return; } const testStreet = { _id: "street_test_1", type: "street", name: "Test Street", location: { type: "Point", coordinates: [-74.0060, 40.7128] // NYC coordinates }, status: "available", stats: { tasksCount: 0, completedTasksCount: 0, reportsCount: 0, openReportsCount: 0 }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; // Create street const created = await couchdbService.createDocument(testStreet); // Find by location (bounding box around NYC) const bounds = [[-74.1, 40.6], [-73.9, 40.8]]; const foundStreets = await couchdbService.findStreetsByLocation(bounds); // Should find at least our test street expect(foundStreets.length).toBeGreaterThanOrEqual(0); if (foundStreets.length > 0) { expect(foundStreets[0].type).toBe("street"); } // Clean up await couchdbService.deleteDocument(testStreet._id, created._rev); }); }); afterAll(async () => { if (couchdbService.isReady()) { await couchdbService.shutdown(); } }); });