Skip to content

Commit c02bb8f

Browse files
committed
Add integration tests for CouchDbNoteRepository and update Jest configuration
1 parent 54c27e4 commit c02bb8f

5 files changed

Lines changed: 1807 additions & 38 deletions

File tree

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import {GenericContainer} from 'testcontainers';
2+
import {CouchDbNoteRepository} from '../../src/db/couchdb-note-repository.js';
3+
import {Note} from '../../src/models/note.js';
4+
5+
describe('CouchDbNoteRepository Integration Tests', () => {
6+
let container;
7+
let repository;
8+
const DB_NAME = 'notes_test';
9+
10+
// Helper function to wait for document availability
11+
async function waitForDocument(id, maxAttempts = 5, delay = 1000) {
12+
console.log(`Waiting for document ${id} to be available...`);
13+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
14+
console.log(`Attempt ${attempt + 1}/${maxAttempts} to find document ${id}`);
15+
try {
16+
const doc = await repository.findById(id);
17+
if (doc) {
18+
console.log(`Document ${id} found on attempt ${attempt + 1}:`, doc);
19+
return doc;
20+
}
21+
console.log(`Document ${id} not found on attempt ${attempt + 1}`);
22+
} catch (error) {
23+
console.error(`Error finding document ${id} on attempt ${attempt + 1}:`, error);
24+
}
25+
await new Promise(resolve => setTimeout(resolve, delay));
26+
}
27+
throw new Error(`Document ${id} not found after ${maxAttempts} attempts`);
28+
}
29+
30+
// Helper function to wait for document deletion
31+
async function waitForDocumentDeletion(id, maxAttempts = 5, delay = 3000) {
32+
console.log(`Waiting for document ${id} to be deleted...`);
33+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
34+
console.log(`Attempt ${attempt + 1}/${maxAttempts} to check document ${id} deletion`);
35+
try {
36+
const doc = await repository.findById(id);
37+
if (!doc) {
38+
console.log(`Document ${id} confirmed deleted on attempt ${attempt + 1}`);
39+
return true;
40+
}
41+
console.log(`Document ${id} still exists on attempt ${attempt + 1}:`, doc);
42+
} catch (error) {
43+
if (error.statusCode === 404) {
44+
console.log(`Document ${id} confirmed deleted on attempt ${attempt + 1}`);
45+
return true;
46+
}
47+
console.error(`Error checking document ${id} deletion on attempt ${attempt + 1}:`, error);
48+
}
49+
console.log(`Waiting ${delay}ms before next attempt...`);
50+
await new Promise(resolve => setTimeout(resolve, delay));
51+
}
52+
throw new Error(`Document ${id} still exists after ${maxAttempts} attempts`);
53+
}
54+
55+
beforeAll(async () => {
56+
console.log('Starting CouchDB container...');
57+
// Start CouchDB container
58+
container = await new GenericContainer('couchdb:3.4.3')
59+
.withExposedPorts(5984)
60+
.withEnvironment({
61+
COUCHDB_USER: 'admin',
62+
COUCHDB_PASSWORD: 'password'
63+
})
64+
.start();
65+
66+
// Create repository instance
67+
const port = container.getMappedPort(5984);
68+
console.log(`CouchDB container started on port ${port}`);
69+
repository = new CouchDbNoteRepository(
70+
`http://admin:password@localhost:${port}`,
71+
DB_NAME
72+
);
73+
74+
// Initialize the database
75+
console.log('Initializing database...');
76+
await repository.init();
77+
console.log('Database initialized successfully');
78+
}, 60000); // Increase timeout for container startup
79+
80+
afterAll(async () => {
81+
console.log('Stopping CouchDB container...');
82+
if (container) {
83+
await container.stop();
84+
console.log('CouchDB container stopped');
85+
}
86+
});
87+
88+
beforeEach(async () => {
89+
console.log('\n--- Starting new test ---');
90+
// await repository.init();
91+
92+
try {
93+
const notes = await repository.findAll();
94+
console.log('Cleaning up notes:', notes);
95+
for (const note of notes) {
96+
console.log(`Deleting note ${note.id}...`);
97+
await repository.delete(note.id);
98+
await waitForDocumentDeletion(note.id);
99+
}
100+
console.log('Cleanup completed');
101+
} catch (error) {
102+
console.error('Error during cleanup:', error);
103+
}
104+
});
105+
106+
describe('CRUD Operations', () => {
107+
it('should create and retrieve a note', async () => {
108+
console.log('\nTest: should create and retrieve a note');
109+
const noteData = {
110+
title: 'Test Note',
111+
content: 'Test Content'
112+
};
113+
114+
console.log('Creating note:', noteData);
115+
const createdNote = await repository.create(noteData);
116+
console.log('Created note:', createdNote);
117+
expect(createdNote).toBeInstanceOf(Note);
118+
expect(createdNote.title).toBe(noteData.title);
119+
expect(createdNote.content).toBe(noteData.content);
120+
expect(createdNote.id).toBeDefined();
121+
122+
console.log('Retrieving created note...');
123+
const retrievedNote = await waitForDocument(createdNote.id);
124+
console.log('Retrieved note:', retrievedNote);
125+
expect(retrievedNote).toBeInstanceOf(Note);
126+
expect(retrievedNote.id).toBe(createdNote.id);
127+
expect(retrievedNote.title).toBe(noteData.title);
128+
expect(retrievedNote.content).toBe(noteData.content);
129+
});
130+
131+
it('should update a note', async () => {
132+
console.log('\nTest: should update a note');
133+
// Create initial note
134+
const note = await repository.create({
135+
title: 'Original Title',
136+
content: 'Original Content'
137+
});
138+
console.log('Created initial note:', note);
139+
140+
console.log('Verifying initial note...');
141+
const initialNote = await waitForDocument(note.id);
142+
console.log('Initial note verified:', initialNote);
143+
expect(initialNote).not.toBeNull();
144+
145+
const updatedData = {
146+
title: 'Updated Title',
147+
content: 'Updated Content'
148+
};
149+
150+
console.log('Updating note with:', updatedData);
151+
const updatedNote = await repository.update(note.id, updatedData);
152+
console.log('Updated note:', updatedNote);
153+
expect(updatedNote).toBeInstanceOf(Note);
154+
expect(updatedNote.id).toBe(note.id);
155+
expect(updatedNote.title).toBe(updatedData.title);
156+
expect(updatedNote.content).toBe(updatedData.content);
157+
158+
console.log('Verifying updated note...');
159+
const retrievedNote = await waitForDocument(note.id);
160+
console.log('Retrieved updated note:', retrievedNote);
161+
expect(retrievedNote).not.toBeNull();
162+
expect(retrievedNote.title).toBe(updatedData.title);
163+
expect(retrievedNote.content).toBe(updatedData.content);
164+
});
165+
166+
it('should delete a note', async () => {
167+
console.log('\nTest: should delete a note');
168+
const note = await repository.create({
169+
title: 'To Delete',
170+
content: 'Will be deleted'
171+
});
172+
console.log('Created note to delete:', note);
173+
174+
console.log('Verifying note exists...');
175+
const initialNote = await waitForDocument(note.id);
176+
console.log('Initial note verified:', initialNote);
177+
expect(initialNote).not.toBeNull();
178+
179+
console.log('Deleting note...');
180+
const deleteResult = await repository.delete(note.id);
181+
console.log('Delete result:', deleteResult);
182+
expect(deleteResult).toBe(true);
183+
184+
console.log('Verifying note deletion...');
185+
await waitForDocumentDeletion(note.id);
186+
});
187+
188+
it('should list all notes', async () => {
189+
console.log('\nTest: should list all notes');
190+
const notes = [
191+
{title: 'Note 1', content: 'Content 1'},
192+
{title: 'Note 2', content: 'Content 2'},
193+
{title: 'Note 3', content: 'Content 3'}
194+
];
195+
196+
// Create notes sequentially
197+
for (const noteData of notes) {
198+
console.log('Creating note:', noteData);
199+
const created = await repository.create(noteData);
200+
console.log('Created note:', created);
201+
await waitForDocument(created.id);
202+
}
203+
204+
// Wait for all notes to be available
205+
console.log('Retrieving all notes...');
206+
const allNotes = await repository.findAll();
207+
console.log('All notes found:', allNotes);
208+
209+
expect(allNotes).toHaveLength(3);
210+
expect(allNotes.every(note => note instanceof Note)).toBe(true);
211+
expect(allNotes.map(note => note.title)).toEqual(
212+
expect.arrayContaining(notes.map(note => note.title))
213+
);
214+
});
215+
});
216+
});

jest.config.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export default {
22
transform: {},
3+
testTimeout: 60000,
34
moduleNameMapper: {
45
'^(\\.{1,2}/.*)\\.js$': '$1',
56
},
@@ -13,10 +14,10 @@ export default {
1314
coverageDirectory: "coverage",
1415
coverageThreshold: {
1516
global: {
16-
branches: 33,
17-
functions: 23,
18-
lines: 22,
19-
statements: 22
17+
branches: 38,
18+
functions: 45,
19+
lines: 40,
20+
statements: 40
2021
}
2122
},
2223
verbose: true

0 commit comments

Comments
 (0)