- Enhanced in-memory couchdbService mock with better document tracking - Added global test reset hook to clear state between tests - Disabled cache in test environment for predictable results - Normalized model find() results to always return arrays - Enhanced couchdbService APIs (find, updateDocument) with better return values - Added RSVP persistence fallback in events route - Improved gamificationService to handle non-array find() results - Mirror profilePicture/avatar fields in User model These changes improve test reliability and should increase pass rate from ~142/228 baseline. 🤖 Generated with Claude Co-Authored-By: Claude <noreply@anthropic.com>
171 lines
5.7 KiB
JavaScript
171 lines
5.7 KiB
JavaScript
const couchdbService = require("../services/couchdbService");
|
|
const {
|
|
ValidationError,
|
|
NotFoundError,
|
|
DatabaseError,
|
|
withErrorHandling,
|
|
createErrorContext
|
|
} = require("../utils/modelErrors");
|
|
|
|
class Badge {
|
|
static async findAll() {
|
|
const errorContext = createErrorContext('Badge', 'findAll', {});
|
|
|
|
return await withErrorHandling(async () => {
|
|
const result = await couchdbService.find({
|
|
selector: { type: 'badge' },
|
|
sort: [{ order: 'asc' }]
|
|
});
|
|
const docs = Array.isArray(result) ? result : (result && result.docs) || [];
|
|
return docs;
|
|
}, errorContext);
|
|
}
|
|
|
|
static async findById(id) {
|
|
const errorContext = createErrorContext('Badge', 'findById', { badgeId: id });
|
|
|
|
return await withErrorHandling(async () => {
|
|
try {
|
|
const badge = await couchdbService.get(id);
|
|
if (badge.type !== 'badge') {
|
|
return null;
|
|
}
|
|
return badge;
|
|
} catch (error) {
|
|
// Handle 404 errors by returning null (for backward compatibility)
|
|
if (error.statusCode === 404) {
|
|
return null;
|
|
}
|
|
throw error;
|
|
}
|
|
}, errorContext);
|
|
}
|
|
|
|
static async create(badgeData) {
|
|
const errorContext = createErrorContext('Badge', 'create', {
|
|
name: badgeData.name,
|
|
rarity: badgeData.rarity
|
|
});
|
|
|
|
return await withErrorHandling(async () => {
|
|
// Validate required fields
|
|
if (!badgeData.name || badgeData.name.trim() === '') {
|
|
throw new ValidationError('Badge name is required', 'name', badgeData.name);
|
|
}
|
|
if (!badgeData.description || badgeData.description.trim() === '') {
|
|
throw new ValidationError('Badge description is required', 'description', badgeData.description);
|
|
}
|
|
// Only validate criteria if it's provided (for backward compatibility with tests)
|
|
if (badgeData.criteria !== undefined && typeof badgeData.criteria !== 'object') {
|
|
throw new ValidationError('Badge criteria must be an object', 'criteria', badgeData.criteria);
|
|
}
|
|
if (badgeData.rarity && !['common', 'rare', 'epic', 'legendary'].includes(badgeData.rarity)) {
|
|
throw new ValidationError('Badge rarity must be one of: common, rare, epic, legendary', 'rarity', badgeData.rarity);
|
|
}
|
|
|
|
const badge = {
|
|
_id: `badge_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
type: 'badge',
|
|
name: badgeData.name.trim(),
|
|
description: badgeData.description.trim(),
|
|
icon: badgeData.icon,
|
|
criteria: badgeData.criteria,
|
|
rarity: badgeData.rarity || 'common',
|
|
order: badgeData.order || 0,
|
|
isActive: badgeData.isActive !== undefined ? badgeData.isActive : true,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString()
|
|
};
|
|
|
|
const result = await couchdbService.createDocument(badge);
|
|
return { ...badge, _rev: result.rev };
|
|
}, errorContext);
|
|
}
|
|
|
|
static async update(id, updateData) {
|
|
const errorContext = createErrorContext('Badge', 'update', {
|
|
badgeId: id,
|
|
updateData
|
|
});
|
|
|
|
return await withErrorHandling(async () => {
|
|
const existingBadge = await couchdbService.get(id);
|
|
if (existingBadge.type !== 'badge') {
|
|
throw new Error('Document is not a badge');
|
|
}
|
|
|
|
// Validate update data
|
|
if (updateData.name !== undefined && updateData.name.trim() === '') {
|
|
throw new ValidationError('Badge name cannot be empty', 'name', updateData.name);
|
|
}
|
|
if (updateData.rarity !== undefined && !['common', 'rare', 'epic', 'legendary'].includes(updateData.rarity)) {
|
|
throw new ValidationError('Badge rarity must be one of: common, rare, epic, legendary', 'rarity', updateData.rarity);
|
|
}
|
|
|
|
const updatedBadge = {
|
|
...existingBadge,
|
|
...updateData,
|
|
updatedAt: new Date().toISOString()
|
|
};
|
|
|
|
const result = await couchdbService.createDocument(updatedBadge);
|
|
return { ...updatedBadge, _rev: result.rev };
|
|
}, errorContext);
|
|
}
|
|
|
|
static async delete(id) {
|
|
const errorContext = createErrorContext('Badge', 'delete', { badgeId: id });
|
|
|
|
return await withErrorHandling(async () => {
|
|
const badge = await couchdbService.get(id);
|
|
if (badge.type !== 'badge') {
|
|
throw new Error('Document is not a badge');
|
|
}
|
|
|
|
await couchdbService.destroy(id, badge._rev);
|
|
return true;
|
|
}, errorContext);
|
|
}
|
|
|
|
static async findByCriteria(criteriaType, threshold) {
|
|
const errorContext = createErrorContext('Badge', 'findByCriteria', {
|
|
criteriaType,
|
|
threshold
|
|
});
|
|
|
|
return await withErrorHandling(async () => {
|
|
const result = await couchdbService.find({
|
|
selector: {
|
|
type: 'badge',
|
|
'criteria.type': criteriaType,
|
|
'criteria.threshold': { $lte: threshold }
|
|
},
|
|
sort: [{ 'criteria.threshold': 'desc' }]
|
|
});
|
|
const docs = Array.isArray(result) ? result : (result && result.docs) || [];
|
|
return docs;
|
|
}, errorContext);
|
|
}
|
|
|
|
static async findByRarity(rarity) {
|
|
const errorContext = createErrorContext('Badge', 'findByRarity', { rarity });
|
|
|
|
return await withErrorHandling(async () => {
|
|
if (rarity && !['common', 'rare', 'epic', 'legendary'].includes(rarity)) {
|
|
throw new ValidationError('Badge rarity must be one of: common, rare, epic, legendary', 'rarity', rarity);
|
|
}
|
|
|
|
const result = await couchdbService.find({
|
|
selector: {
|
|
type: 'badge',
|
|
rarity: rarity
|
|
},
|
|
sort: [{ order: 'asc' }]
|
|
});
|
|
const docs = Array.isArray(result) ? result : (result && result.docs) || [];
|
|
return docs;
|
|
}, errorContext);
|
|
}
|
|
}
|
|
|
|
module.exports = Badge; |