Files
adopt-a-street/backend/__tests__/models/Badge.test.js
William Valentin c17019360c fix: resolve CouchDB test infrastructure issues
- Fix Badge.js model to use correct couchdbService.createDocument() method
- Update Badge.test.js with proper static method testing patterns
- Add missing Post model import in Post.test.js
- Update jest.setup.js with missing mock methods (get, destroy)
- Fix PointTransaction.test.js mock service definition
- Ensure consistent mock patterns across model tests

User and Badge model tests now pass with 40/40 tests working.
Post test import fixed, remaining test issues identified for next iteration.

🤖 Generated with AI Assistant

Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
2025-11-03 00:07:20 -08:00

327 lines
9.6 KiB
JavaScript

// Mock CouchDB service for testing
const mockCouchdbService = {
find: jest.fn(),
get: jest.fn(),
getDocument: jest.fn(),
createDocument: jest.fn(),
destroy: jest.fn(),
initialize: jest.fn(),
isReady: jest.fn().mockReturnValue(true),
shutdown: jest.fn()
};
// Mock the service module
jest.mock('../../services/couchdbService', () => mockCouchdbService);
const couchdbService = require('../../services/couchdbService');
const Badge = require('../../models/Badge');
describe('Badge Model', () => {
beforeEach(() => {
jest.clearAllMocks();
// Reset all mocks to ensure clean state
mockCouchdbService.find.mockReset();
mockCouchdbService.get.mockReset();
mockCouchdbService.getDocument.mockReset();
mockCouchdbService.createDocument.mockReset();
mockCouchdbService.destroy.mockReset();
});
describe('findAll', () => {
it('should return all badges sorted by order', async () => {
const mockBadges = [
{ _id: 'badge1', type: 'badge', name: 'First Badge', order: 1 },
{ _id: 'badge2', type: 'badge', name: 'Second Badge', order: 2 }
];
couchdbService.find.mockResolvedValue({ docs: mockBadges });
const badges = await Badge.findAll();
expect(couchdbService.find).toHaveBeenCalledWith({
selector: { type: 'badge' },
sort: [{ order: 'asc' }]
});
expect(badges).toEqual(mockBadges);
});
it('should handle errors when finding badges', async () => {
couchdbService.find.mockRejectedValue(new Error('Database error'));
await expect(Badge.findAll()).rejects.toThrow('Database error');
});
});
describe('findById', () => {
it('should return badge by ID', async () => {
const mockBadge = {
_id: 'badge_123',
type: 'badge',
name: 'Test Badge',
description: 'Test description'
};
couchdbService.get.mockResolvedValue(mockBadge);
const badge = await Badge.findById('badge_123');
expect(couchdbService.get).toHaveBeenCalledWith('badge_123');
expect(badge).toEqual(mockBadge);
});
it('should return null for non-badge documents', async () => {
const mockDoc = {
_id: 'user_123',
type: 'user',
name: 'Test User'
};
couchdbService.get.mockResolvedValue(mockDoc);
const badge = await Badge.findById('user_123');
expect(badge).toBeNull();
});
it('should return null for 404 errors', async () => {
const error = new Error('Not found');
error.statusCode = 404;
couchdbService.get.mockRejectedValue(error);
const badge = await Badge.findById('nonexistent');
expect(badge).toBeNull();
});
it('should handle other errors', async () => {
couchdbService.get.mockRejectedValue(new Error('Database error'));
await expect(Badge.findById('badge_123')).rejects.toThrow('Database error');
});
});
describe('create', () => {
it('should create a new badge', async () => {
const badgeData = {
name: 'Street Cleaner',
description: 'Awarded for completing 10 street cleaning tasks',
icon: 'broom',
criteria: { type: 'task_count', threshold: 10 },
rarity: 'common',
order: 1
};
const mockResult = { rev: '1-abc' };
couchdbService.createDocument.mockResolvedValue(mockResult);
const badge = await Badge.create(badgeData);
expect(couchdbService.createDocument).toHaveBeenCalledWith(
expect.objectContaining({
_id: expect.stringMatching(/^badge_\d+_[a-z0-9]+$/),
type: 'badge',
name: badgeData.name,
description: badgeData.description,
icon: badgeData.icon,
criteria: badgeData.criteria,
rarity: badgeData.rarity,
order: badgeData.order,
isActive: true,
createdAt: expect.any(String),
updatedAt: expect.any(String)
})
);
expect(badge).toEqual(
expect.objectContaining({
...badgeData,
_rev: '1-abc',
isActive: true,
createdAt: expect.any(String),
updatedAt: expect.any(String)
})
);
});
it('should use default values', async () => {
const badgeData = {
name: 'Test Badge',
description: 'Test description',
icon: 'star'
};
const mockResult = { rev: '1-abc' };
couchdbService.createDocument.mockResolvedValue(mockResult);
const badge = await Badge.create(badgeData);
expect(badge.rarity).toBe('common');
expect(badge.order).toBe(0);
expect(badge.isActive).toBe(true);
});
it('should handle errors during creation', async () => {
const badgeData = {
name: 'Test Badge',
description: 'Test description',
icon: 'star'
};
couchdbService.createDocument.mockRejectedValue(new Error('Creation failed'));
await expect(Badge.create(badgeData)).rejects.toThrow('Creation failed');
});
});
describe('update', () => {
it('should update an existing badge', async () => {
const existingBadge = {
_id: 'badge_123',
_rev: '1-abc',
type: 'badge',
name: 'Original Name',
description: 'Original description',
icon: 'star',
criteria: { type: 'task_count', threshold: 5 },
rarity: 'common',
order: 0,
isActive: true,
createdAt: '2023-01-01T00:00:00.000Z',
updatedAt: '2023-01-01T00:00:00.000Z'
};
const updateData = {
name: 'Updated Name',
rarity: 'rare'
};
const mockResult = { rev: '2-def' };
couchdbService.get.mockResolvedValue(existingBadge);
couchdbService.createDocument.mockResolvedValue(mockResult);
const badge = await Badge.update('badge_123', updateData);
expect(couchdbService.get).toHaveBeenCalledWith('badge_123');
expect(couchdbService.createDocument).toHaveBeenCalledWith(
expect.objectContaining({
...existingBadge,
name: 'Updated Name',
rarity: 'rare',
updatedAt: expect.any(String)
})
);
expect(badge._rev).toBe('2-def');
});
it('should throw error for non-badge documents', async () => {
const nonBadgeDoc = {
_id: 'user_123',
_rev: '1-abc',
type: 'user'
};
couchdbService.get.mockResolvedValue(nonBadgeDoc);
await expect(Badge.update('user_123', { name: 'Updated' })).rejects.toThrow('Document is not a badge');
});
it('should handle errors during update', async () => {
couchdbService.get.mockRejectedValue(new Error('Update failed'));
await expect(Badge.update('badge_123', { name: 'Updated' })).rejects.toThrow('Update failed');
});
});
describe('delete', () => {
it('should delete a badge', async () => {
const mockBadge = {
_id: 'badge_123',
_rev: '1-abc',
type: 'badge'
};
couchdbService.get.mockResolvedValue(mockBadge);
couchdbService.destroy.mockResolvedValue(true);
const result = await Badge.delete('badge_123');
expect(couchdbService.get).toHaveBeenCalledWith('badge_123');
expect(couchdbService.destroy).toHaveBeenCalledWith('badge_123', '1-abc');
expect(result).toBe(true);
});
it('should throw error for non-badge documents', async () => {
const nonBadgeDoc = {
_id: 'user_123',
_rev: '1-abc',
type: 'user'
};
couchdbService.get.mockResolvedValue(nonBadgeDoc);
await expect(Badge.delete('user_123')).rejects.toThrow('Document is not a badge');
});
it('should handle errors during deletion', async () => {
couchdbService.get.mockRejectedValue(new Error('Delete failed'));
await expect(Badge.delete('badge_123')).rejects.toThrow('Delete failed');
});
});
describe('findByCriteria', () => {
it('should find badges by criteria type and threshold', async () => {
const mockBadges = [
{ _id: 'badge1', type: 'badge', criteria: { type: 'task_count', threshold: 10 } },
{ _id: 'badge2', type: 'badge', criteria: { type: 'task_count', threshold: 5 } }
];
couchdbService.find.mockResolvedValue({ docs: mockBadges });
const badges = await Badge.findByCriteria('task_count', 7);
expect(couchdbService.find).toHaveBeenCalledWith({
selector: {
type: 'badge',
'criteria.type': 'task_count',
'criteria.threshold': { $lte: 7 }
},
sort: [{ 'criteria.threshold': 'desc' }]
});
expect(badges).toEqual(mockBadges);
});
it('should handle errors when finding by criteria', async () => {
couchdbService.find.mockRejectedValue(new Error('Search failed'));
await expect(Badge.findByCriteria('task_count', 10)).rejects.toThrow('Search failed');
});
});
describe('findByRarity', () => {
it('should find badges by rarity', async () => {
const mockBadges = [
{ _id: 'badge1', type: 'badge', rarity: 'rare', order: 1 },
{ _id: 'badge2', type: 'badge', rarity: 'rare', order: 2 }
];
couchdbService.find.mockResolvedValue({ docs: mockBadges });
const badges = await Badge.findByRarity('rare');
expect(couchdbService.find).toHaveBeenCalledWith({
selector: {
type: 'badge',
rarity: 'rare'
},
sort: [{ order: 'asc' }]
});
expect(badges).toEqual(mockBadges);
});
it('should handle errors when finding by rarity', async () => {
couchdbService.find.mockRejectedValue(new Error('Search failed'));
await expect(Badge.findByRarity('rare')).rejects.toThrow('Search failed');
});
});
});