Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3e9059c
test: improve push routes coverage
jescalada Jan 18, 2026
35bd6ae
Merge branch 'main' into improve-test-coverage
jescalada Jan 18, 2026
5d0e096
test: /:id/authorise edge cases
jescalada Jan 18, 2026
52adf56
Merge branch 'improve-test-coverage' of https://github.com/jescalada/…
jescalada Jan 18, 2026
8b18566
test: src/service/routes/repo edge cases
jescalada Jan 18, 2026
1b00794
test: extend MongoDB repo tests
jescalada Jan 20, 2026
6e465c1
Merge branch 'main' into improve-test-coverage
jescalada Jan 20, 2026
1957fd6
test: add MongoDB user tests with mocked db
jescalada Jan 24, 2026
da7f718
test: add Mongo user tests with mocked db
jescalada Jan 24, 2026
75b3525
test: add MongoDB helper tests with mocked db
jescalada Jan 24, 2026
f94d8ff
test: improve coverage for auth routes and fix res.send message
jescalada Jan 24, 2026
87bcd3b
test: improve coverage for misc routes and edge cases
jescalada Jan 24, 2026
7b6386d
test: improve coverage for parsePush edge cases
jescalada Jan 24, 2026
fe42f52
test: add ldahelper tests, move activeDirectory tests
jescalada Jan 26, 2026
c195042
Merge branch 'main' into improve-test-coverage
jescalada Feb 3, 2026
f3b680d
Merge branch 'main' into improve-test-coverage
jescalada Feb 4, 2026
929e096
chore: run formatter
jescalada Feb 4, 2026
5f2f2cc
Merge branch 'main' into improve-test-coverage
jescalada Feb 5, 2026
d77ee07
Merge branch 'main' into improve-test-coverage
jescalada Feb 6, 2026
ee8e485
Merge branch 'main' into improve-test-coverage
jescalada Feb 6, 2026
081d7d1
fix: typos and unused testadmin creation
jescalada Feb 6, 2026
c985147
Merge branch 'improve-test-coverage' of https://github.com/jescalada/…
jescalada Feb 6, 2026
de58b87
Merge branch 'main' into improve-test-coverage
kriswest Feb 9, 2026
e713eb0
Merge branch 'main' into improve-test-coverage
jescalada Feb 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/db/mongo/pushes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const getPushes = async (
rejected: 1,
repo: 1,
repoName: 1,
timepstamp: 1,
timestamp: 1,
type: 1,
url: 1,
},
Expand Down
2 changes: 1 addition & 1 deletion src/service/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ router.get('/profile', async (req: Request, res: Response) => {

const userVal = await db.findUser((req.user as User).username);
if (!userVal) {
res.status(404).send('User not found').end();
res.status(404).send({ message: 'User not found' }).end();
return;
}

Expand Down
315 changes: 315 additions & 0 deletions test/db/mongo/helper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest';
import { MongoClient } from 'mongodb';

const mockCollection = {
find: vi.fn(),
findOne: vi.fn(),
};

const mockDb = {
collection: vi.fn(() => mockCollection),
};

const mockClient = {
connect: vi.fn().mockResolvedValue(undefined),
db: vi.fn(() => mockDb),
};

const mockToArray = vi.fn();

vi.mock('mongodb', async () => {
const actual = await vi.importActual('mongodb');
return {
...actual,
MongoClient: vi.fn(() => mockClient),
};
});

const mockGetDatabase = vi.fn();

vi.mock('../../../src/config', () => ({
getDatabase: mockGetDatabase,
}));

const mockFromNodeProviderChain = vi.fn();

vi.mock('@aws-sdk/credential-providers', () => ({
fromNodeProviderChain: mockFromNodeProviderChain,
}));

const mockMongoDBStore = vi.fn();

vi.mock('connect-mongo', () => ({
default: mockMongoDBStore,
}));

describe('MongoDB Helper', () => {
beforeEach(() => {
vi.clearAllMocks();
mockCollection.find.mockReturnValue({ toArray: mockToArray });

// Clear cached db
vi.resetModules();
});

afterEach(() => {
vi.restoreAllMocks();
});

describe('connect', () => {
it('should connect to MongoDB and return collection', async () => {
mockGetDatabase.mockReturnValue({
connectionString: 'mongodb://localhost:27017/testdb',
options: {},
});

const { connect } = await import('../../../src/db/mongo/helper');

const result = await connect('testCollection');

expect(MongoClient).toHaveBeenCalledWith('mongodb://localhost:27017/testdb', {});
expect(mockClient.connect).toHaveBeenCalled();
expect(mockClient.db).toHaveBeenCalled();
expect(mockDb.collection).toHaveBeenCalledWith('testCollection');
expect(result).toBe(mockCollection);
});

it('should reuse existing connection', async () => {
mockGetDatabase.mockReturnValue({
connectionString: 'mongodb://localhost:27017/testdb',
options: {},
});

const { connect } = await import('../../../src/db/mongo/helper');

await connect('collection1');

vi.clearAllMocks();
mockDb.collection.mockReturnValue(mockCollection);

await connect('collection2');

expect(MongoClient).not.toHaveBeenCalled();
expect(mockClient.connect).not.toHaveBeenCalled();
expect(mockDb.collection).toHaveBeenCalledWith('collection2');
});

it('should throw error when connection string is not provided', async () => {
mockGetDatabase.mockReturnValue({
connectionString: '',
options: {},
});

const { connect } = await import('../../../src/db/mongo/helper');

await expect(connect('testCollection')).rejects.toThrow(
'MongoDB connection string is not provided',
);
});

it('should throw error when connection string is undefined', async () => {
mockGetDatabase.mockReturnValue({
connectionString: undefined,
options: {},
});

const { connect } = await import('../../../src/db/mongo/helper');

await expect(connect('testCollection')).rejects.toThrow(
'MongoDB connection string is not provided',
);
});

it('should handle AWS credential provider', async () => {
const mockCredentialProvider = vi.fn();
mockFromNodeProviderChain.mockReturnValue(mockCredentialProvider);

mockGetDatabase.mockReturnValue({
connectionString: 'mongodb://localhost:27017/testdb',
options: {
authMechanismProperties: {
AWS_CREDENTIAL_PROVIDER: 'placeholder',
},
},
});

const { connect } = await import('../../../src/db/mongo/helper');

await connect('testCollection');

expect(mockFromNodeProviderChain).toHaveBeenCalled();
expect(MongoClient).toHaveBeenCalledWith(
'mongodb://localhost:27017/testdb',
expect.objectContaining({
authMechanismProperties: {
AWS_CREDENTIAL_PROVIDER: mockCredentialProvider,
},
}),
);
});

it('should pass options to MongoClient', async () => {
const options = {
maxPoolSize: 10,
minPoolSize: 5,
serverSelectionTimeoutMS: 5000,
};

mockGetDatabase.mockReturnValue({
connectionString: 'mongodb://localhost:27017/testdb',
options,
});

const { connect } = await import('../../../src/db/mongo/helper');

await connect('testCollection');

expect(MongoClient).toHaveBeenCalledWith('mongodb://localhost:27017/testdb', options);
});
});

describe('findDocuments', () => {
beforeEach(async () => {
mockGetDatabase.mockReturnValue({
connectionString: 'mongodb://localhost:27017/testdb',
options: {},
});
});

it('should find documents with default filter and options', async () => {
const mockDocs = [
{ id: 1, name: 'test1' },
{ id: 2, name: 'test2' },
];
mockToArray.mockResolvedValue(mockDocs);

const { findDocuments } = await import('../../../src/db/mongo/helper');

const result = await findDocuments('testCollection');

expect(mockDb.collection).toHaveBeenCalledWith('testCollection');
expect(mockCollection.find).toHaveBeenCalledWith({}, {});
expect(mockToArray).toHaveBeenCalled();
expect(result).toEqual(mockDocs);
});

it('should find documents with custom filter', async () => {
const mockDocs = [{ id: 1, name: 'test1' }];
mockToArray.mockResolvedValue(mockDocs);

const { findDocuments } = await import('../../../src/db/mongo/helper');

const filter = { name: 'test1' };
const result = await findDocuments('testCollection', filter);

expect(mockCollection.find).toHaveBeenCalledWith(filter, {});
expect(result).toEqual(mockDocs);
});

it('should find documents with custom options', async () => {
const mockDocs = [{ id: 1, name: 'test1' }];
mockToArray.mockResolvedValue(mockDocs);

const { findDocuments } = await import('../../../src/db/mongo/helper');

const filter = { name: 'test1' };
const options = { projection: { _id: 0, name: 1 }, limit: 10 };
const result = await findDocuments('testCollection', filter, options);

expect(mockCollection.find).toHaveBeenCalledWith(filter, options);
expect(result).toEqual(mockDocs);
});

it('should return empty array when no documents found', async () => {
mockToArray.mockResolvedValue([]);

const { findDocuments } = await import('../../../src/db/mongo/helper');

const result = await findDocuments('testCollection');

expect(result).toEqual([]);
});
});

describe('findOneDocument', () => {
beforeEach(async () => {
mockGetDatabase.mockReturnValue({
connectionString: 'mongodb://localhost:27017/testdb',
options: {},
});
});

it('should find one document with default filter and options', async () => {
const mockDoc = { id: 1, name: 'test1' };
mockCollection.findOne.mockResolvedValue(mockDoc);

const { findOneDocument } = await import('../../../src/db/mongo/helper');

const result = await findOneDocument('testCollection');

expect(mockDb.collection).toHaveBeenCalledWith('testCollection');
expect(mockCollection.findOne).toHaveBeenCalledWith({}, {});
expect(result).toEqual(mockDoc);
});

it('should find one document with custom filter', async () => {
const mockDoc = { id: 1, name: 'test1' };
mockCollection.findOne.mockResolvedValue(mockDoc);

const { findOneDocument } = await import('../../../src/db/mongo/helper');

const filter = { id: 1 };
const result = await findOneDocument('testCollection', filter);

expect(mockCollection.findOne).toHaveBeenCalledWith(filter, {});
expect(result).toEqual(mockDoc);
});

it('should find one document with custom options', async () => {
const mockDoc = { id: 1, name: 'test1' };
mockCollection.findOne.mockResolvedValue(mockDoc);

const { findOneDocument } = await import('../../../src/db/mongo/helper');

const filter = { id: 1 };
const options = { projection: { _id: 0, name: 1 } };
const result = await findOneDocument('testCollection', filter, options);

expect(mockCollection.findOne).toHaveBeenCalledWith(filter, options);
expect(result).toEqual(mockDoc);
});

it('should return null when document not found', async () => {
mockCollection.findOne.mockResolvedValue(null);

const { findOneDocument } = await import('../../../src/db/mongo/helper');

const result = await findOneDocument('testCollection', { id: 999 });

expect(result).toBeNull();
});
});

describe('getSessionStore', () => {
it('should create MongoDBStore with connection string and options', async () => {
const connectionString = 'mongodb://localhost:27017/testdb';
const options = { maxPoolSize: 10 };

mockGetDatabase.mockReturnValue({
connectionString,
options,
});

const { getSessionStore } = await import('../../../src/db/mongo/helper');

const result = getSessionStore();

expect(result).toBeDefined();
expect(mockMongoDBStore).toHaveBeenCalledWith({
mongoUrl: connectionString,
collectionName: 'user_session',
mongoOptions: options,
});
});
});
});
Loading
Loading