test(backend): add comprehensive testing infrastructure

Implement complete backend testing infrastructure with Jest and Supertest:

Test Setup:
- Configure Jest for Node.js environment
- Add MongoDB Memory Server for isolated testing
- Create test setup with database connection helpers
- Add test scripts: test, test:coverage, test:watch

Test Files (176 total tests, 109 passing):
- Middleware tests: auth.test.js (100% coverage)
- Model tests: User, Street, Task, Post (82.5% coverage)
- Route tests: auth, streets, tasks, posts, events, rewards, reports

Test Coverage:
- Overall: 54.75% (on track for 70% target)
- Models: 82.5%
- Middleware: 100%
- Routes: 45.84%

Test Utilities:
- Helper functions for creating test users, streets, tasks, posts
- Test database setup and teardown
- MongoDB Memory Server configuration
- Coverage reporting with lcov

Testing Features:
- Isolated test environment (no production data pollution)
- Async/await test patterns
- Proper setup/teardown for each test
- Authentication testing with JWT tokens
- Validation testing for all routes
- Error handling verification

Scripts:
- Database seeding scripts for development
- Test data generation utilities

Dependencies:
- jest@29.7.0
- supertest@7.0.0
- mongodb-memory-server@10.1.2

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
William Valentin
2025-11-01 10:43:20 -07:00
parent 7c70a8d098
commit 17e5c90a90
16 changed files with 3430 additions and 0 deletions

View File

@@ -0,0 +1,269 @@
require("dotenv").config();
const mongoose = require("mongoose");
const Badge = require("../models/Badge");
/**
* Initial badge definitions
* These badges will be auto-awarded when users meet the criteria
*/
const badges = [
// Street Adoption Badges
{
name: "First Adoption",
description: "Adopted your first street",
icon: "🏡",
criteria: {
type: "street_adoptions",
threshold: 1,
},
rarity: "common",
order: 1,
},
{
name: "Street Adopter",
description: "Adopted 5 streets",
icon: "🏘️",
criteria: {
type: "street_adoptions",
threshold: 5,
},
rarity: "rare",
order: 2,
},
{
name: "Neighborhood Champion",
description: "Adopted 10 streets",
icon: "🌆",
criteria: {
type: "street_adoptions",
threshold: 10,
},
rarity: "epic",
order: 3,
},
{
name: "City Guardian",
description: "Adopted 25 streets",
icon: "🏙️",
criteria: {
type: "street_adoptions",
threshold: 25,
},
rarity: "legendary",
order: 4,
},
// Task Completion Badges
{
name: "First Task",
description: "Completed your first task",
icon: "✅",
criteria: {
type: "task_completions",
threshold: 1,
},
rarity: "common",
order: 5,
},
{
name: "Task Master",
description: "Completed 10 tasks",
icon: "🎯",
criteria: {
type: "task_completions",
threshold: 10,
},
rarity: "rare",
order: 6,
},
{
name: "Dedicated Worker",
description: "Completed 50 tasks",
icon: "🛠️",
criteria: {
type: "task_completions",
threshold: 50,
},
rarity: "epic",
order: 7,
},
{
name: "Maintenance Legend",
description: "Completed 100 tasks",
icon: "⚡",
criteria: {
type: "task_completions",
threshold: 100,
},
rarity: "legendary",
order: 8,
},
// Post Creation Badges
{
name: "First Post",
description: "Created your first post",
icon: "📝",
criteria: {
type: "post_creations",
threshold: 1,
},
rarity: "common",
order: 9,
},
{
name: "Social Butterfly",
description: "Created 25 posts",
icon: "🦋",
criteria: {
type: "post_creations",
threshold: 25,
},
rarity: "rare",
order: 10,
},
{
name: "Community Voice",
description: "Created 100 posts",
icon: "📢",
criteria: {
type: "post_creations",
threshold: 100,
},
rarity: "epic",
order: 11,
},
{
name: "Social Media Star",
description: "Created 250 posts",
icon: "⭐",
criteria: {
type: "post_creations",
threshold: 250,
},
rarity: "legendary",
order: 12,
},
// Event Participation Badges
{
name: "Event Participant",
description: "Participated in your first event",
icon: "🎉",
criteria: {
type: "event_participations",
threshold: 1,
},
rarity: "common",
order: 13,
},
{
name: "Community Leader",
description: "Participated in 5 events",
icon: "👥",
criteria: {
type: "event_participations",
threshold: 5,
},
rarity: "rare",
order: 14,
},
{
name: "Event Enthusiast",
description: "Participated in 15 events",
icon: "🎊",
criteria: {
type: "event_participations",
threshold: 15,
},
rarity: "epic",
order: 15,
},
{
name: "Community Pillar",
description: "Participated in 30 events",
icon: "🏛️",
criteria: {
type: "event_participations",
threshold: 30,
},
rarity: "legendary",
order: 16,
},
// Points Badges
{
name: "Point Collector",
description: "Earned 1,000 points",
icon: "💰",
criteria: {
type: "points_earned",
threshold: 1000,
},
rarity: "rare",
order: 17,
},
{
name: "Point Hoarder",
description: "Earned 5,000 points",
icon: "💎",
criteria: {
type: "points_earned",
threshold: 5000,
},
rarity: "epic",
order: 18,
},
{
name: "Point Master",
description: "Earned 10,000 points",
icon: "👑",
criteria: {
type: "points_earned",
threshold: 10000,
},
rarity: "legendary",
order: 19,
},
];
/**
* Seed badges into the database
*/
async function seedBadges() {
try {
// Connect to MongoDB
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log("Connected to MongoDB");
// Clear existing badges (optional - remove if you want to preserve existing badges)
await Badge.deleteMany({});
console.log("Cleared existing badges");
// Insert new badges
const createdBadges = await Badge.insertMany(badges);
console.log(`Successfully seeded ${createdBadges.length} badges`);
// Display created badges
createdBadges.forEach((badge) => {
console.log(
` ${badge.icon} ${badge.name} (${badge.rarity}) - ${badge.description}`
);
});
// Close connection
await mongoose.connection.close();
console.log("\nDatabase connection closed");
process.exit(0);
} catch (error) {
console.error("Error seeding badges:", error);
process.exit(1);
}
}
// Run the seeder
seedBadges();