feat: Complete CouchDB test infrastructure migration for route tests

- Fixed 5/7 route test suites (auth, events, reports, rewards, streets)
- Updated Jest configuration with global CouchDB mocks
- Created comprehensive test helper utilities with proper ID generation
- Fixed pagination response format expectations (.data property)
- Added proper model method mocks (populate, save, toJSON, etc.)
- Resolved ID validation issues for different entity types
- Implemented proper CouchDB service method mocking
- Updated test helpers to generate valid IDs matching validator patterns

Remaining work:
- posts.test.js: needs model mocking and response format fixes
- tasks.test.js: needs Task model constructor fixes and mocking

🤖 Generated with [AI Assistant]

Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
This commit is contained in:
William Valentin
2025-11-02 22:57:08 -08:00
parent d9b7b78b0d
commit 6070474404
19 changed files with 1141 additions and 394 deletions

View File

@@ -1,40 +1,45 @@
// Mock CouchDB service before importing anything else
jest.mock('../services/couchdbService', () => ({
initialize: jest.fn().mockResolvedValue(true),
isReady: jest.fn().mockReturnValue(true),
create: jest.fn(),
getById: jest.fn(),
find: jest.fn(),
createDocument: jest.fn(),
updateDocument: jest.fn(),
deleteDocument: jest.fn(),
findByType: jest.fn().mockResolvedValue([]),
findUserById: jest.fn(),
findUserByEmail: jest.fn(),
update: jest.fn(),
getDocument: jest.fn(),
}));
const request = require("supertest");
const mongoose = require("mongoose");
const { MongoMemoryServer } = require("mongodb-memory-server");
const app = require("../server");
const User = require("../models/User");
const { generateTestId } = require('./utils/idGenerator');
describe("Error Handling", () => {
let mongoServer;
let testUser;
let authToken;
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
const mongoUri = mongoServer.getUri();
await mongoose.connect(mongoUri);
// Create test user
testUser = new User({
testUser = await User.create({
name: "Test User",
email: "test@example.com",
password: "password123",
});
await testUser.save();
// Generate auth token
const jwt = require("jsonwebtoken");
authToken = jwt.sign(
{ user: { id: testUser._id.toString() } },
{ user: { id: testUser._id } },
process.env.JWT_SECRET || "test_secret"
);
});
afterAll(async () => {
await mongoose.disconnect();
await mongoServer.stop();
});
describe("Authentication Errors", () => {
test("should reject requests without token", async () => {
const response = await request(app)
@@ -81,7 +86,7 @@ describe("Error Handling", () => {
test("should reject requests when user not found", async () => {
const jwt = require("jsonwebtoken");
const tokenWithNonExistentUser = jwt.sign(
{ user: { id: new mongoose.Types.ObjectId().toString() } },
{ user: { id: generateTestId() } },
process.env.JWT_SECRET || "test_secret"
);
@@ -189,7 +194,7 @@ describe("Error Handling", () => {
describe("Resource Not Found Errors", () => {
test("should handle non-existent street", async () => {
const nonExistentId = new mongoose.Types.ObjectId().toString();
const nonExistentId = generateTestId();
const response = await request(app)
.get(`/api/streets/${nonExistentId}`)
@@ -199,7 +204,7 @@ describe("Error Handling", () => {
});
test("should handle non-existent task", async () => {
const nonExistentId = new mongoose.Types.ObjectId().toString();
const nonExistentId = generateTestId();
const response = await request(app)
.put(`/api/tasks/${nonExistentId}/complete`)
@@ -210,7 +215,7 @@ describe("Error Handling", () => {
});
test("should handle non-existent event", async () => {
const nonExistentId = new mongoose.Types.ObjectId().toString();
const nonExistentId = generateTestId();
const response = await request(app)
.put(`/api/events/rsvp/${nonExistentId}`)
@@ -221,7 +226,7 @@ describe("Error Handling", () => {
});
test("should handle non-existent post", async () => {
const nonExistentId = new mongoose.Types.ObjectId().toString();
const nonExistentId = generateTestId();
const response = await request(app)
.get(`/api/posts/${nonExistentId}`)
@@ -235,7 +240,7 @@ describe("Error Handling", () => {
let testStreet;
beforeEach(async () => {
testStreet = new mongoose.Types.ObjectId();
testStreet = generateTestId();
});
test("should prevent duplicate user registration", async () => {
@@ -320,9 +325,11 @@ describe("Error Handling", () => {
});
describe("Database Connection Errors", () => {
test("should handle database disconnection gracefully", async () => {
// Disconnect from database
await mongoose.connection.close();
test("should handle database service unavailable", async () => {
// Mock CouchDB service to be unavailable
const couchdbService = require('../services/couchdbService');
const originalIsReady = couchdbService.isReady;
couchdbService.isReady = jest.fn().mockReturnValue(false);
const response = await request(app)
.get("/api/streets")
@@ -330,14 +337,15 @@ describe("Error Handling", () => {
expect(response.body.msg).toBeDefined();
// Reconnect for other tests
await mongoose.connect(mongoServer.getUri());
// Restore original function
couchdbService.isReady = originalIsReady;
});
test("should handle database operation timeouts", async () => {
// Mock a slow database operation
const originalFind = mongoose.Model.find;
mongoose.Model.find = jest.fn().mockImplementation(() => {
// Mock a slow CouchDB operation
const couchdbService = require('../services/couchdbService');
const originalFindByType = couchdbService.findByType;
couchdbService.findByType = jest.fn().mockImplementation(() => {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Database timeout")), 100);
});
@@ -349,8 +357,8 @@ describe("Error Handling", () => {
expect(response.body.msg).toBeDefined();
// Restore original method
mongoose.Model.find = originalFind;
// Restore original function
couchdbService.findByType = originalFindByType;
});
});