UNPKG

@kenniy/godeye-data-contracts

Version:

Enterprise-grade base repository architecture for GOD-EYE microservices with zero overhead and maximum code reuse

319 lines (318 loc) 13.4 kB
"use strict"; /** * Mongoose Aggregate Repository Tests * * Tests the MongoDB implementation of unified aggregation * Validates pipeline building, aggregations, and pagination */ Object.defineProperty(exports, "__esModule", { value: true }); const mongoose_aggregate_repository_1 = require("../repositories/mongoose-aggregate.repository"); // Test repository implementation class TestMongooseAggregateRepository extends mongoose_aggregate_repository_1.MongooseAggregateRepository { constructor(model) { super(model); } // Expose protected methods for testing buildMongoosePipelinePublic(config) { return this.buildMongoosePipeline(config); } buildLookupStagePublic(join) { return this.buildLookupStage(join); } buildGroupStagePublic(aggregations, groupBy) { return this.buildGroupStage(aggregations, groupBy); } } describe('MongooseAggregateRepository', () => { let repository; let mockModel; let mockAggregate; let mockExec; beforeEach(async () => { // Create mock aggregation chain mockExec = jest.fn(); mockAggregate = jest.fn().mockReturnValue({ exec: mockExec }); // Create mock model mockModel = { aggregate: mockAggregate, collection: { name: 'test_entities' } }; repository = new TestMongooseAggregateRepository(mockModel); }); afterEach(() => { jest.clearAllMocks(); }); describe('Pipeline Building', () => { it('should build basic match pipeline', () => { const config = { conditions: { status: 'ACTIVE', business_id: '123' } }; const pipeline = repository.buildMongoosePipelinePublic(config); expect(pipeline).toEqual([ { $match: { status: 'ACTIVE', business_id: '123' } } ]); }); it('should build pipeline with joins (lookups)', () => { const config = { conditions: { status: 'ACTIVE' }, joins: [ { collection: 'businesses', localField: 'business_id', foreignField: '_id', as: 'business' } ] }; const pipeline = repository.buildMongoosePipelinePublic(config); expect(pipeline).toEqual([ { $match: { status: 'ACTIVE' } }, { $lookup: { from: 'businesses', localField: 'business_id', foreignField: '_id', as: 'business' } }, { $unwind: { path: '$business', preserveNullAndEmptyArrays: true } } ]); }); it('should build pipeline with aggregations', () => { const config = { conditions: { business_id: '123' }, aggregations: [ { operation: 'COUNT', field: 'status', alias: 'active_count', conditions: { status: 'ACTIVE' } }, { operation: 'SUM', field: 'amount', alias: 'total_amount' } ] }; const pipeline = repository.buildMongoosePipelinePublic(config); expect(pipeline).toContainEqual({ $match: { business_id: '123' } }); expect(pipeline).toContainEqual({ $group: { _id: null, active_count: { $sum: { $cond: [{ $eq: ['$status', 'ACTIVE'] }, 1, 0] } }, total_amount: { $sum: '$amount' } } }); }); it('should build pipeline with sorting', () => { const config = { conditions: { status: 'ACTIVE' }, sort: { created_at: 'DESC', name: 'ASC' } }; const pipeline = repository.buildMongoosePipelinePublic(config); expect(pipeline).toContainEqual({ $sort: { created_at: -1, name: 1 } }); }); it('should build pipeline with field selection', () => { const config = { conditions: { status: 'ACTIVE' }, select: ['name', 'status', 'created_at'] }; const pipeline = repository.buildMongoosePipelinePublic(config); expect(pipeline).toContainEqual({ $project: { name: 1, status: 1, created_at: 1 } }); }); }); describe('Lookup Stage Building', () => { it('should build correct lookup stage', () => { const join = { collection: 'businesses', localField: 'business_id', foreignField: '_id', as: 'business_data' }; const lookupStage = repository.buildLookupStagePublic(join); expect(lookupStage).toEqual({ $lookup: { from: 'businesses', localField: 'business_id', foreignField: '_id', as: 'business_data' } }); }); }); describe('Group Stage Building', () => { it('should build group stage with simple count', () => { const aggregations = [ { operation: 'COUNT', field: '*', alias: 'total_count' } ]; const groupStage = repository.buildGroupStagePublic(aggregations); expect(groupStage).toEqual({ $group: { _id: null, total_count: { $sum: 1 } } }); }); it('should build group stage with conditional count', () => { const aggregations = [ { operation: 'COUNT', field: 'status', alias: 'active_count', conditions: { status: 'ACTIVE' } } ]; const groupStage = repository.buildGroupStagePublic(aggregations); expect(groupStage).toEqual({ $group: { _id: null, active_count: { $sum: { $cond: [{ $eq: ['$status', 'ACTIVE'] }, 1, 0] } } } }); }); it('should build group stage with multiple aggregations', () => { const aggregations = [ { operation: 'COUNT', field: '*', alias: 'total_count' }, { operation: 'SUM', field: 'amount', alias: 'total_amount' }, { operation: 'AVG', field: 'rating', alias: 'avg_rating' }, { operation: 'MIN', field: 'created_at', alias: 'first_created' }, { operation: 'MAX', field: 'updated_at', alias: 'last_updated' } ]; const groupStage = repository.buildGroupStagePublic(aggregations); expect(groupStage).toEqual({ $group: { _id: null, total_count: { $sum: 1 }, total_amount: { $sum: '$amount' }, avg_rating: { $avg: '$rating' }, first_created: { $min: '$created_at' }, last_updated: { $max: '$updated_at' } } }); }); it('should build group stage with group by fields', () => { const aggregations = [ { operation: 'COUNT', field: '*', alias: 'count_per_status' } ]; const groupBy = ['status', 'business_id']; const groupStage = repository.buildGroupStagePublic(aggregations, groupBy); expect(groupStage).toEqual({ $group: { _id: { status: '$status', business_id: '$business_id' }, count_per_status: { $sum: 1 } } }); }); }); describe('Aggregate Execution', () => { it('should execute simple aggregation', async () => { const mockResults = [ { _id: '1', name: 'Test 1', status: 'ACTIVE' }, { _id: '2', name: 'Test 2', status: 'INACTIVE' } ]; mockExec.mockResolvedValue(mockResults); const pipeline = [{ $match: { status: 'ACTIVE' } }]; const results = await repository.aggregate(pipeline); expect(mockModel.aggregate).toHaveBeenCalledWith(pipeline, { allowDiskUse: true, maxTimeMS: 30000 }); expect(results).toEqual(mockResults); }); it('should execute aggregation with pagination', async () => { const mockResults = [{ data: [ { _id: '1', name: 'Test 1', status: 'ACTIVE' }, { _id: '2', name: 'Test 2', status: 'ACTIVE' } ], totalCount: [{ count: 10 }] }]; mockExec.mockResolvedValue(mockResults); const pipeline = [{ $match: { status: 'ACTIVE' } }]; const pagination = { page: 1, limit: 2 }; const result = await repository.aggregateWithPagination(pipeline, pagination); expect(result).toEqual({ items: mockResults[0].data, total: 10, page: 1, limit: 2, totalPages: 5, hasNext: true, hasPrev: false }); // Verify the pagination pipeline structure const calledPipeline = mockModel.aggregate.mock.calls[0][0]; expect(calledPipeline).toContainEqual({ $match: { status: 'ACTIVE' } }); expect(calledPipeline).toContainEqual({ $facet: { data: [{ $skip: 0 }, { $limit: 2 }], totalCount: [{ $count: 'count' }] } }); }); }); describe('Complex Query Integration', () => { it('should execute complex query with all features', async () => { const mockResults = [{ data: [ { _id: '1', name: 'Agent 1', status: 'ACTIVE', business: { _id: 'b1', name: 'Hospital A' }, active_count: 5, pending_count: 2 } ], totalCount: [{ count: 1 }] }]; mockExec.mockResolvedValue(mockResults); const config = { joins: [{ collection: 'businesses', localField: 'business_id', foreignField: '_id', as: 'business' }], aggregations: [ { operation: 'COUNT', field: 'status', alias: 'active_count', conditions: { status: 'ACTIVE' } }, { operation: 'COUNT', field: 'status', alias: 'pending_count', conditions: { status: 'PENDING' } } ], conditions: { business_id: 'b1' }, sort: { created_at: 'DESC' }, pagination: { page: 1, limit: 10 } }; const result = await repository.complexQuery(config); expect(result.items).toEqual(mockResults[0].data); expect(result.total).toBe(1); expect(result.page).toBe(1); expect(result.limit).toBe(10); }); }); describe('Error Handling', () => { it('should handle aggregation errors gracefully', async () => { const error = new Error('Aggregation failed'); mockExec.mockRejectedValue(error); const pipeline = [{ $match: { status: 'ACTIVE' } }]; await expect(repository.aggregate(pipeline)).rejects.toThrow('Aggregation failed'); }); it('should handle pagination errors gracefully', async () => { const error = new Error('Pagination failed'); mockExec.mockRejectedValue(error); const pipeline = [{ $match: { status: 'ACTIVE' } }]; const pagination = { page: 1, limit: 10 }; await expect(repository.aggregateWithPagination(pipeline, pagination)).rejects.toThrow('Pagination failed'); }); }); describe('Performance Metrics', () => { it('should log performance metrics for successful queries', async () => { // Set NODE_ENV to development to trigger logging const originalEnv = process.env.NODE_ENV; process.env.NODE_ENV = 'development'; const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); mockExec.mockResolvedValue([]); const pipeline = [{ $match: { status: 'ACTIVE' } }]; await repository.aggregate(pipeline); // Should log performance metrics in development expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Query: mongooseAggregate on test_entities')); consoleSpy.mockRestore(); process.env.NODE_ENV = originalEnv; }); }); });