@kenniy/godeye-data-contracts
Version:
Enterprise-grade base repository architecture for GOD-EYE microservices with zero overhead and maximum code reuse
271 lines (270 loc) • 10.8 kB
JavaScript
"use strict";
/**
* Mongoose Mock Utilities
* Provides comprehensive mocking for Mongoose models, queries, and operations
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MockRepositoryBuilder = exports.MongooseMockFactory = void 0;
/**
* Mongoose Mock Factory - Creates comprehensive Mongoose mocks
*/
class MongooseMockFactory {
/**
* Creates a mock query with chainable methods
*/
static createMockQuery(returnValue) {
const mockQuery = {
populate: jest.fn().mockReturnThis(),
select: jest.fn().mockReturnThis(),
sort: jest.fn().mockReturnThis(),
limit: jest.fn().mockReturnThis(),
skip: jest.fn().mockReturnThis(),
lean: jest.fn().mockReturnThis(),
exec: jest.fn().mockResolvedValue(returnValue),
};
return mockQuery;
}
/**
* Creates a mock document with save functionality
*/
static createMockDocument(data = {}) {
const document = {
_id: data._id || 'mock_doc_id',
...data,
save: jest.fn().mockResolvedValue({ _id: data._id || 'mock_doc_id', ...data }),
toObject: jest.fn().mockReturnValue({ ...data }),
toJSON: jest.fn().mockReturnValue({ ...data }),
};
return document;
}
/**
* Creates a mock session for transaction testing
*/
static createMockSession() {
return {
startTransaction: jest.fn(),
commitTransaction: jest.fn().mockResolvedValue(undefined),
abortTransaction: jest.fn().mockResolvedValue(undefined),
endSession: jest.fn().mockResolvedValue(undefined),
};
}
/**
* Creates a mock aggregate query
*/
static createMockAggregate(returnValue = []) {
const aggregate = {
exec: jest.fn().mockResolvedValue(returnValue),
allowDiskUse: jest.fn().mockReturnThis(),
maxTimeMS: jest.fn().mockReturnThis(),
append: jest.fn().mockReturnThis(),
};
return aggregate;
}
/**
* Creates a comprehensive mock model with all methods
*/
static createMockModel(modelName = 'TestModel', collectionName = 'test', schemaPaths = ['_id', '__v', 'name', 'email', 'status', 'createdAt']) {
const mockSession = this.createMockSession();
// Create schema mock with paths
const mockSchema = {
eachPath: jest.fn((callback) => {
schemaPaths.forEach(path => {
const schemaType = this.createSchemaType(path);
callback(path, schemaType);
});
}),
paths: schemaPaths.reduce((acc, path) => {
acc[path] = this.createSchemaType(path);
return acc;
}, {}),
};
const mockDb = {
startSession: jest.fn().mockResolvedValue(mockSession),
transaction: jest.fn().mockImplementation(async (callback) => {
const session = mockSession;
return await callback(session);
}),
};
// Constructor function for creating documents
function MockModelConstructor(data = {}) {
return MongooseMockFactory.createMockDocument(data);
}
// Add static methods to constructor
const mockModel = MockModelConstructor;
// Collection info
mockModel.collection = { name: collectionName };
mockModel.modelName = modelName;
mockModel.schema = mockSchema;
mockModel.db = mockDb;
// Query methods
mockModel.findOne = jest.fn(() => this.createMockQuery(null));
mockModel.find = jest.fn(() => this.createMockQuery([]));
mockModel.findById = jest.fn(() => this.createMockQuery(null));
mockModel.findByIdAndUpdate = jest.fn(() => this.createMockQuery(null));
mockModel.findByIdAndDelete = jest.fn(() => this.createMockQuery(null));
mockModel.findOneAndUpdate = jest.fn(() => this.createMockQuery(null));
mockModel.findOneAndDelete = jest.fn(() => this.createMockQuery(null));
// Bulk operations
mockModel.updateMany = jest.fn(() => this.createMockQuery({ modifiedCount: 0 }));
mockModel.deleteMany = jest.fn(() => this.createMockQuery({ deletedCount: 0 }));
mockModel.insertMany = jest.fn().mockResolvedValue([]);
mockModel.bulkWrite = jest.fn().mockResolvedValue({});
// Count and aggregation
mockModel.countDocuments = jest.fn(() => this.createMockQuery(0));
mockModel.estimatedDocumentCount = jest.fn(() => this.createMockQuery(0));
mockModel.aggregate = jest.fn(() => this.createMockAggregate([]));
// Index operations
mockModel.createIndexes = jest.fn().mockResolvedValue({});
mockModel.ensureIndexes = jest.fn().mockResolvedValue({});
// Validation
mockModel.validate = jest.fn().mockResolvedValue({});
return mockModel;
}
/**
* Creates schema type information for a given path
*/
static createSchemaType(path) {
const relationPaths = {
'profile': { options: { ref: 'Profile' } },
'business': { options: { ref: 'Business' } },
'user': { options: { ref: 'User' } },
'owner': { options: { ref: 'User' } },
'author': { options: { ref: 'User' } },
'posts': { options: { type: [{ ref: 'Post' }] } },
'comments': { options: { type: [{ ref: 'Comment' }] } },
'files': { options: { type: [{ ref: 'File' }] } },
'tags': { options: { type: [{ ref: 'Tag' }] } },
'folder': { options: { ref: 'Folder' } },
'permissions': { options: { type: [{ ref: 'Permission' }] } },
};
return relationPaths[path] || { options: {} };
}
/**
* Creates a repository-specific mock model with common schema
*/
static createUserModel() {
return this.createMockModel('User', 'users', [
'_id', '__v', 'name', 'firstName', 'lastName', 'email', 'status', 'userType',
'verified', 'phone', 'createdAt', 'updatedAt', 'profile', 'business', 'posts'
]);
}
static createFileModel() {
return this.createMockModel('File', 'files', [
'_id', '__v', 'name', 'originalName', 'mimeType', 'size', 'fileType',
'userId', 'folderId', 'tags', 'createdAt', 'updatedAt', 'user', 'folder'
]);
}
static createProfileModel() {
return this.createMockModel('Profile', 'profiles', [
'_id', '__v', 'bio', 'avatar', 'userId', 'profileKind', 'user'
]);
}
static createBusinessModel() {
return this.createMockModel('Business', 'businesses', [
'_id', '__v', 'name', 'type', 'ownerId', 'owner', 'contact'
]);
}
}
exports.MongooseMockFactory = MongooseMockFactory;
/**
* Mock Repository Builder - Fluent interface for building repository mocks
*/
class MockRepositoryBuilder {
constructor(modelName = 'TestModel', collectionName = 'test') {
this.returnValues = {};
this.model = MongooseMockFactory.createMockModel(modelName, collectionName);
}
static create(modelName, collectionName) {
return new MockRepositoryBuilder(modelName, collectionName);
}
/**
* Configure return values for query methods
*/
withFindOneResult(result) {
this.model.findOne.mockReturnValue(MongooseMockFactory.createMockQuery(result));
return this;
}
withFindResult(results) {
this.model.find.mockReturnValue(MongooseMockFactory.createMockQuery(results));
return this;
}
withFindByIdResult(result) {
this.model.findById.mockReturnValue(MongooseMockFactory.createMockQuery(result));
return this;
}
withCountResult(count) {
this.model.countDocuments.mockReturnValue(MongooseMockFactory.createMockQuery(count));
return this;
}
withAggregateResult(results) {
this.model.aggregate.mockReturnValue(MongooseMockFactory.createMockAggregate(results));
return this;
}
withInsertManyResult(results) {
this.model.insertMany.mockResolvedValue(results);
return this;
}
withUpdateResult(result) {
this.model.findByIdAndUpdate.mockReturnValue(MongooseMockFactory.createMockQuery(result));
this.model.updateMany.mockReturnValue(MongooseMockFactory.createMockQuery(result));
return this;
}
withDeleteResult(success = true) {
const result = success ? { deletedCount: 1 } : { deletedCount: 0 };
this.model.findByIdAndDelete.mockReturnValue(MongooseMockFactory.createMockQuery(success ? {} : null));
this.model.deleteMany.mockReturnValue(MongooseMockFactory.createMockQuery(result));
return this;
}
/**
* Configure error scenarios
*/
withFindOneError(error) {
const mockQuery = MongooseMockFactory.createMockQuery(null);
mockQuery.exec.mockRejectedValue(error);
this.model.findOne.mockReturnValue(mockQuery);
return this;
}
withSaveError(error) {
// Override the constructor to return a document that fails on save
const originalModel = this.model;
function FailingMockModel(data) {
const doc = MongooseMockFactory.createMockDocument(data);
doc.save.mockRejectedValue(error);
return doc;
}
// Copy static methods
Object.assign(FailingMockModel, originalModel);
this.model = FailingMockModel;
return this;
}
/**
* Configure complex aggregation results (for pagination)
*/
withPaginationResult(items, total) {
const aggregateResult = [{
data: items,
totalCount: [{ count: total }],
}];
this.model.aggregate.mockReturnValue(MongooseMockFactory.createMockAggregate(aggregateResult));
return this;
}
/**
* Reset all mocks to default state
*/
reset() {
jest.clearAllMocks();
// Restore default behaviors
this.model.findOne.mockReturnValue(MongooseMockFactory.createMockQuery(null));
this.model.find.mockReturnValue(MongooseMockFactory.createMockQuery([]));
this.model.countDocuments.mockReturnValue(MongooseMockFactory.createMockQuery(0));
this.model.aggregate.mockReturnValue(MongooseMockFactory.createMockAggregate([]));
return this;
}
/**
* Get the built mock model
*/
build() {
return this.model;
}
}
exports.MockRepositoryBuilder = MockRepositoryBuilder;