From 285aee260862ccd74006e0fcad48eb46661d213a Mon Sep 17 00:00:00 2001 From: nallovint <67425227+nallovint@users.noreply.github.com> Date: Sun, 27 Jul 2025 19:35:18 +0100 Subject: [PATCH 1/2] initial branch commit --- HOW_TO_ADD.md | 506 ++++++++++++++++++++++++++++++++++++++ TESTING.md | 248 +++++++++++++++++++ package-lock.json | 86 +++++++ package.json | 5 +- routes/routerAbilities.js | 20 +- routes/routerCodemon.js | 6 +- routes/routerTrainers.js | 6 +- testApi.rest | 296 ++++++++++++++++++++-- testRunner.js | 316 ++++++++++++++++++++++++ 9 files changed, 1449 insertions(+), 40 deletions(-) create mode 100644 HOW_TO_ADD.md create mode 100644 TESTING.md create mode 100644 testRunner.js diff --git a/HOW_TO_ADD.md b/HOW_TO_ADD.md new file mode 100644 index 0000000..c612531 --- /dev/null +++ b/HOW_TO_ADD.md @@ -0,0 +1,506 @@ +# How to Add Information to the Codemon Database + +This guide explains how to add, modify, and manage data in the Codemon database through various methods. + +## Table of Contents + +1. [Prerequisites](#prerequisites) +2. [Database Structure](#database-structure) +3. [Method 1: CRUD API Operations](#method-1-crud-api-operations) +4. [Method 2: Direct Database Methods](#method-2-direct-database-methods) +5. [Method 3: Database Seeding](#method-3-database-seeding) +6. [Testing Your Changes](#testing-your-changes) +7. [Best Practices](#best-practices) + +## Prerequisites + +Before adding data to the database, ensure you have: + +1. **MongoDB running locally:** + ```bash + mongod + ``` + +2. **Server running:** + ```bash + npm run devStart + ``` + +3. **Dependencies installed:** + ```bash + npm install + ``` + +## Database Structure + +The Codemon database contains four main collections: + +### 1. Abilities Collection +```javascript +{ + _id: ObjectId, + name: String (required), + type: String (required), + description: String (required), + power: Number, + accuracy: Number, + statusEffectAttack: Number, + statusEffectSpecialAttack: Number, + statusEffectDefense: Number, + statusEffectSpecialDefense: Number, + statusEffectSpeed: Number +} +``` + +### 2. Codemon Collection +```javascript +{ + _id: ObjectId, + name: String (required), + type: String (required), + abilities: [String] (required) +} +``` + +### 3. Trainers Collection +```javascript +{ + _id: ObjectId, + name: String (required), + codemon: [String] (required), + dialogue: String (required) +} +``` + +### 4. Dialogue Collection +```javascript +{ + _id: ObjectId, + startDialogue: String (required), + battleDialogue: String (required) +} +``` + +## Method 1: CRUD API Operations + +### Using the REST API + +#### 1.1 Adding Abilities + +**POST** `http://localhost:3001/abilities` + +```bash +curl -X POST http://localhost:3001/abilities \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Fire Blast", + "type": "Special", + "description": "A powerful fire attack", + "power": 110, + "accuracy": 85, + "statusEffectAttack": 0, + "statusEffectSpecialAttack": 0, + "statusEffectDefense": 0, + "statusEffectSpecialDefense": 0, + "statusEffectSpeed": 0 + }' +``` + +**Using REST Client (VS Code):** +```http +POST http://localhost:3001/abilities +Content-Type: application/json + +{ + "name": "Fire Blast", + "type": "Special", + "description": "A powerful fire attack", + "power": 110, + "accuracy": 85, + "statusEffectAttack": 0, + "statusEffectSpecialAttack": 0, + "statusEffectDefense": 0, + "statusEffectSpecialDefense": 0, + "statusEffectSpeed": 0 +} +``` + +#### 1.2 Adding Codemon + +**POST** `http://localhost:3001/codemon` + +```bash +curl -X POST http://localhost:3001/codemon \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Charizard", + "type": "Fire/Flying", + "abilities": ["Fire Blast", "Dragon Claw", "Air Slash"] + }' +``` + +#### 1.3 Adding Trainers + +**POST** `http://localhost:3001/trainers` + +```bash +curl -X POST http://localhost:3001/trainers \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Ash Ketchum", + "codemon": ["Charizard", "Pikachu"], + "dialogue": "I choose you!" + }' +``` + +#### 1.4 Adding Dialogue + +**POST** `http://localhost:3001/dialogue` + +```bash +curl -X POST http://localhost:3001/dialogue \ + -H "Content-Type: application/json" \ + -d '{ + "startDialogue": "Welcome to my gym!", + "battleDialogue": "Let\'s see what you\'ve got!" + }' +``` + +### Updating Existing Data + +#### 1.5 Updating Abilities + +**PATCH** `http://localhost:3001/abilities/{id}` + +```bash +curl -X PATCH http://localhost:3001/abilities/507f1f77bcf86cd799439011 \ + -H "Content-Type: application/json" \ + -d '{ + "power": 120, + "accuracy": 90 + }' +``` + +#### 1.6 Deleting Data + +**DELETE** `http://localhost:3001/abilities/{id}` + +```bash +curl -X DELETE http://localhost:3001/abilities/507f1f77bcf86cd799439011 +``` + +## Method 2: Direct Database Methods + +### 2.1 Using MongoDB Compass (GUI) + +1. **Connect to MongoDB:** + - Open MongoDB Compass + - Connect to: `mongodb://localhost:27017` + - Select database: `dbCodemon` + +2. **Add Documents:** + - Click on the collection (e.g., `abilities`) + - Click "Add Data" → "Insert Document" + - Paste your JSON document + - Click "Insert" + +### 2.2 Using MongoDB Shell + +```bash +# Connect to MongoDB +mongosh + +# Switch to database +use dbCodemon + +# Insert ability +db.abilities.insertOne({ + name: "Thunderbolt", + type: "Special", + description: "A powerful electric attack", + power: 90, + accuracy: 100, + statusEffectAttack: 0, + statusEffectSpecialAttack: 0, + statusEffectDefense: 0, + statusEffectSpecialDefense: 0, + statusEffectSpeed: 0 +}) + +# Insert codemon +db.codemon.insertOne({ + name: "Pikachu", + type: "Electric", + abilities: ["Thunderbolt", "Quick Attack", "Thunder Wave"] +}) + +# Insert trainer +db.trainers.insertOne({ + name: "Misty", + codemon: ["Starmie", "Goldeen"], + dialogue: "Water types are the best!" +}) + +# Insert dialogue +db.dialogue.insertOne({ + startDialogue: "Are you ready for a water battle?", + battleDialogue: "My water types will wash you away!" +}) +``` + +### 2.3 Bulk Operations + +```javascript +// Insert multiple abilities at once +db.abilities.insertMany([ + { + name: "Ice Beam", + type: "Special", + description: "A freezing beam of ice", + power: 90, + accuracy: 100, + statusEffectAttack: 0, + statusEffectSpecialAttack: 0, + statusEffectDefense: 0, + statusEffectSpecialDefense: 0, + statusEffectSpeed: 0 + }, + { + name: "Earthquake", + type: "Physical", + description: "A powerful ground attack", + power: 100, + accuracy: 100, + statusEffectAttack: 0, + statusEffectSpecialAttack: 0, + statusEffectDefense: 0, + statusEffectSpecialDefense: 0, + statusEffectSpeed: 0 + } +]) +``` + +## Method 3: Database Seeding + +### 3.1 Creating Seed Files + +Create a new file `database/seedData.js`: + +```javascript +import mongoose from 'mongoose'; +import Ability from '../models/modelAbilities.js'; +import Codemon from '../models/modelCodemon.js'; +import Trainer from '../models/modelTrainers.js'; +import Dialogue from '../models/modelDialogue.js'; + +const seedData = async () => { + try { + // Clear existing data + await Ability.deleteMany({}); + await Codemon.deleteMany({}); + await Trainer.deleteMany({}); + await Dialogue.deleteMany({}); + + // Seed abilities + const abilities = await Ability.insertMany([ + { + name: "Fire Blast", + type: "Special", + description: "A powerful fire attack", + power: 110, + accuracy: 85, + statusEffectAttack: 0, + statusEffectSpecialAttack: 0, + statusEffectDefense: 0, + statusEffectSpecialDefense: 0, + statusEffectSpeed: 0 + }, + { + name: "Thunderbolt", + type: "Special", + description: "A powerful electric attack", + power: 90, + accuracy: 100, + statusEffectAttack: 0, + statusEffectSpecialAttack: 0, + statusEffectDefense: 0, + statusEffectSpecialDefense: 0, + statusEffectSpeed: 0 + } + ]); + + // Seed codemon + const codemon = await Codemon.insertMany([ + { + name: "Charizard", + type: "Fire/Flying", + abilities: ["Fire Blast", "Dragon Claw", "Air Slash"] + }, + { + name: "Pikachu", + type: "Electric", + abilities: ["Thunderbolt", "Quick Attack", "Thunder Wave"] + } + ]); + + // Seed trainers + const trainers = await Trainer.insertMany([ + { + name: "Ash Ketchum", + codemon: ["Charizard", "Pikachu"], + dialogue: "I choose you!" + }, + { + name: "Misty", + codemon: ["Starmie", "Goldeen"], + dialogue: "Water types are the best!" + } + ]); + + // Seed dialogue + const dialogue = await Dialogue.insertMany([ + { + startDialogue: "Welcome to my gym!", + battleDialogue: "Let's see what you've got!" + }, + { + startDialogue: "Are you ready for a water battle?", + battleDialogue: "My water types will wash you away!" + } + ]); + + console.log('Database seeded successfully!'); + console.log(`Added ${abilities.length} abilities`); + console.log(`Added ${codemon.length} codemon`); + console.log(`Added ${trainers.length} trainers`); + console.log(`Added ${dialogue.length} dialogue entries`); + + } catch (error) { + console.error('Error seeding database:', error); + } +}; + +export default seedData; +``` + +### 3.2 Running Seed Script + +Add to `package.json`: + +```json +{ + "scripts": { + "seed": "node -e \"import('./database/seedData.js').then(m => m.default())\"" + } +} +``` + +Run the seed script: + +```bash +npm run seed +``` + +## Testing Your Changes + +### 4.1 Using the Test Suite + +```bash +# Run all tests +npm test + +# Test specific endpoints +curl http://localhost:3001/abilities +curl http://localhost:3001/codemon +curl http://localhost:3001/trainers +curl http://localhost:3001/dialogue +``` + +### 4.2 Manual Verification + +```bash +# Check if data was added +curl http://localhost:3001/abilities | jq +curl http://localhost:3001/codemon | jq +curl http://localhost:3001/trainers | jq +curl http://localhost:3001/dialogue | jq +``` + +## Best Practices + +### 5.1 Data Validation + +- **Required Fields**: Always include required fields (`name`, `type`, `description` for abilities) +- **Data Types**: Ensure correct data types (numbers for power/accuracy, strings for names) +- **Unique Names**: Consider making names unique to avoid duplicates + +### 5.2 Error Handling + +```javascript +// Example of proper error handling +try { + const newAbility = await ability.save(); + console.log('Ability created:', newAbility); +} catch (error) { + console.error('Validation error:', error.message); +} +``` + +### 5.3 Data Consistency + +- **References**: When adding codemon, ensure referenced abilities exist +- **Relationships**: Maintain consistency between trainers and their codemon +- **Updates**: Use PATCH for partial updates, PUT for complete replacements + +### 5.4 Performance + +- **Bulk Operations**: Use `insertMany()` for multiple documents +- **Indexes**: Consider adding indexes for frequently queried fields +- **Validation**: Use MongoDB schema validation for data integrity + +## Troubleshooting + +### Common Issues + +1. **Connection Errors:** + ```bash + # Check if MongoDB is running + mongosh + ``` + +2. **Validation Errors:** + - Check required fields are present + - Verify data types are correct + - Ensure no duplicate unique fields + +3. **Server Errors:** + ```bash + # Check server logs + npm run devStart + ``` + +### Useful Commands + +```bash +# Check database status +mongosh --eval "db.stats()" + +# List all collections +mongosh dbCodemon --eval "show collections" + +# Count documents in collection +mongosh dbCodemon --eval "db.abilities.countDocuments()" + +# Find specific document +mongosh dbCodemon --eval "db.abilities.findOne({name: 'Fire Blast'})" +``` + +## Next Steps + +1. **Explore the API**: Use the test suite to understand all available endpoints +2. **Add Custom Data**: Create your own abilities, codemon, trainers, and dialogue +3. **Extend the Schema**: Add new fields to existing models as needed +4. **Create Relationships**: Link data between collections using references +5. **Implement Validation**: Add custom validation rules for your data + +For more information, see the [API Documentation](TESTING.md) and [Database Models](../models/). \ No newline at end of file diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..0a3472e --- /dev/null +++ b/TESTING.md @@ -0,0 +1,248 @@ +# Testing Guide for Codemon API + +This document explains how to test all the routes in the Codemon project. + +## Prerequisites + +1. **Install dependencies:** + ```bash + npm install + ``` + +2. **Start the server:** + ```bash + npm run devStart + ``` + +3. **Make sure MongoDB is running:** + ```bash + # If you have MongoDB installed locally + mongod + + # Or if using MongoDB Atlas, make sure your connection string is correct + ``` + +## Testing Methods + +### Method 1: Automated Test Runner (Recommended) + +Run the comprehensive test suite: + +```bash +npm test +# or +npm run test:api +``` + +This will: +- Test all CRUD operations for each route +- Test error handling +- Test static file serving +- Provide detailed pass/fail results +- Show a summary at the end + +### Method 2: REST Client (VS Code) + +1. **Install the REST Client extension** in VS Code +2. **Open `testApi.rest`** in VS Code +3. **Run individual tests** by clicking "Send Request" above each request +4. **Run all tests** with `Ctrl+Alt+R` (or `Cmd+Alt+R` on Mac) + +### Method 3: Manual Testing with curl + +```bash +# Test root route +curl http://localhost:3001/ + +# Test abilities routes +curl http://localhost:3001/abilities +curl -X POST http://localhost:3001/abilities -H "Content-Type: application/json" -d '{"name":"Test","type":"Physical","description":"Test","power":50,"accuracy":85}' + +# Test codemon routes +curl http://localhost:3001/codemon +curl -X POST http://localhost:3001/codemon -H "Content-Type: application/json" -d '{"name":"Test","type":"Fire","abilities":[]}' + +# Test trainers routes +curl http://localhost:3001/trainers +curl -X POST http://localhost:3001/trainers -H "Content-Type: application/json" -d '{"name":"Test","codemon":[],"dialogue":"Hello"}' + +# Test dialogue routes +curl http://localhost:3001/dialogue +curl -X POST http://localhost:3001/dialogue -H "Content-Type: application/json" -d '{"startDialogue":"Hello","battleDialogue":"Fight"}' +``` + +## Routes Being Tested + +### 1. Root Route (`/`) +- **GET** `/` - Renders the main page + +### 2. Abilities Routes (`/abilities`) +- **GET** `/abilities` - Get all abilities +- **POST** `/abilities` - Create new ability +- **GET** `/abilities/:id` - Get ability by ID +- **PATCH** `/abilities/:id` - Update ability +- **DELETE** `/abilities/:id` - Delete ability + +### 3. Codemon Routes (`/codemon`) +- **GET** `/codemon` - Get all codemon +- **POST** `/codemon` - Create new codemon +- **GET** `/codemon/:id` - Get codemon by ID +- **PATCH** `/codemon/:id` - Update codemon +- **DELETE** `/codemon/:id` - Delete codemon + +### 4. Trainers Routes (`/trainers`) +- **GET** `/trainers` - Get all trainers +- **POST** `/trainers` - Create new trainer +- **GET** `/trainers/:id` - Get trainer by ID +- **PATCH** `/trainers/:id` - Update trainer +- **DELETE** `/trainers/:id` - Delete trainer + +### 5. Dialogue Routes (`/dialogue`) +- **GET** `/dialogue` - Get all dialogue +- **POST** `/dialogue` - Create new dialogue +- **GET** `/dialogue/:id` - Get dialogue by ID +- **PATCH** `/dialogue/:id` - Update dialogue +- **DELETE** `/dialogue/:id` - Delete dialogue + +### 6. Static Files +- **GET** `/css/style.css` - CSS files +- **GET** `/js/main.js` - JavaScript files +- **GET** `/managers/GameManager.js` - Manager files +- **GET** `/assets/audio/effects/*.mp3` - Audio files + +### 7. Error Handling +- **GET** `/nonexistent` - 404 errors +- **GET** `/abilities/invalid-id` - Invalid ID format errors + +## Test Data + +The automated tests use the following sample data: + +### Ability +```json +{ + "name": "Test Ability", + "type": "Physical", + "description": "A test ability for testing purposes", + "power": 50, + "accuracy": 85, + "statusEffectAttack": 1, + "statusEffectSpecialAttack": 0, + "statusEffectDefense": -1, + "statusEffectSpecialDefense": 0, + "statusEffectSpeed": 0 +} +``` + +### Codemon +```json +{ + "name": "Test Codemon", + "type": "Fire", + "abilities": ["Test Ability", "Special Test Ability"] +} +``` + +### Trainer +```json +{ + "name": "Test Trainer", + "codemon": ["Test Codemon"], + "dialogue": "Hello, I am a test trainer!" +} +``` + +### Dialogue +```json +{ + "startDialogue": "Welcome to the battle!", + "battleDialogue": "Let's see what you've got!" +} +``` + +## Expected Test Results + +### Successful Tests +- ✅ All CRUD operations return appropriate status codes (200, 201, 404) +- ✅ Data is properly created, retrieved, updated, and deleted +- ✅ Error handling works correctly +- ✅ Static files are accessible +- ✅ Root route renders correctly + +### Common Issues +- ❌ MongoDB not running (connection errors) +- ❌ Server not started (connection refused) +- ❌ Invalid JSON in request body +- ❌ Missing required fields in POST requests + +## Troubleshooting + +### Server Connection Issues +```bash +# Check if server is running +curl http://localhost:3001/ + +# Check server logs +npm run devStart +``` + +### MongoDB Issues +```bash +# Check MongoDB status +mongosh +# or +mongo + +# Check connection string in server.js +``` + +### Test Runner Issues +```bash +# Install missing dependencies +npm install + +# Check Node.js version (should be 16+) +node --version +``` + +## Continuous Integration + +To integrate testing into your development workflow: + +1. **Pre-commit hooks:** Run tests before committing +2. **GitHub Actions:** Automate testing on push/PR +3. **Docker:** Include tests in container builds + +Example GitHub Actions workflow: +```yaml +name: Tests +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '18' + - run: npm install + - run: npm test +``` + +## Contributing + +When adding new routes or modifying existing ones: + +1. **Update the test suite** in `testRunner.js` +2. **Add test cases** for new functionality +3. **Update this documentation** with new routes +4. **Run the full test suite** before submitting PRs + +## Support + +If you encounter issues with testing: + +1. Check the server logs for errors +2. Verify MongoDB is running +3. Ensure all dependencies are installed +4. Check the test output for specific failure details \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4602994..b75bb9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "ejs": "^3.1.10", "express": "^5.1.0", "mongoose": "^8.16.5", + "node-fetch": "^3.3.2", "phaser": "^3.90.0", "ts-node": "^10.9.2" }, @@ -1329,6 +1330,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "license": "MIT" }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -1588,6 +1597,28 @@ } } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -1648,6 +1679,17 @@ "node": ">= 0.8" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2171,6 +2213,42 @@ "node": ">= 0.6" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/nodemon": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", @@ -3057,6 +3135,14 @@ } } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index d2978cf..2ce6132 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ "server": "nodemon --watch './' --ext ts,js --exec 'tsx' server.js", "macStart": "nodemon --watch './' --ext ts --exec 'node --loader ts-node/esm' server.js", "devStart": "nodemon server.js", - "dev:full": "concurrently \"npm run server\" \"npm run dev\"" + "dev:full": "concurrently \"npm run server\" \"npm run dev\"", + "test": "node testRunner.js", + "test:api": "node testRunner.js" }, "devDependencies": { "@types/express": "^5.0.3", @@ -28,6 +30,7 @@ "ejs": "^3.1.10", "express": "^5.1.0", "mongoose": "^8.16.5", + "node-fetch": "^3.3.2", "phaser": "^3.90.0", "ts-node": "^10.9.2" } diff --git a/routes/routerAbilities.js b/routes/routerAbilities.js index c877528..f4c9e31 100644 --- a/routes/routerAbilities.js +++ b/routes/routerAbilities.js @@ -46,34 +46,34 @@ router res.json(res.ability); }) .patch(getAbility, async (req, res) => { - if (req.body.name !== null) { + if (req.body.name !== undefined) { res.ability.name = req.body.name; }; - if (req.body.type !== null) { + if (req.body.type !== undefined) { res.ability.type = req.body.type; }; - if (req.body.description !== null) { + if (req.body.description !== undefined) { res.ability.description = req.body.description; }; - if (req.body.power !== null) { + if (req.body.power !== undefined) { res.ability.power = req.body.power; }; - if (req.body.accuracy !== null) { + if (req.body.accuracy !== undefined) { res.ability.accuracy = req.body.accuracy; }; - if (req.body.statusEffectAttack !== null) { + if (req.body.statusEffectAttack !== undefined) { res.ability.statusEffectAttack = req.body.statusEffectAttack; }; - if (req.body.statusEffectSpecialAttack !== null) { + if (req.body.statusEffectSpecialAttack !== undefined) { res.ability.statusEffectSpecialAttack = req.body.statusEffectSpecialAttack; }; - if (req.body.statusEffectDefense !== null) { + if (req.body.statusEffectDefense !== undefined) { res.ability.statusEffectDefense = req.body.statusEffectDefense; }; - if (req.body.statusEffectSpecialDefense !== null) { + if (req.body.statusEffectSpecialDefense !== undefined) { res.ability.statusEffectSpecialDefense = req.body.statusEffectSpecialDefense; }; - if (req.body.statusEffectSpeed !== null) { + if (req.body.statusEffectSpeed !== undefined) { res.ability.statusEffectSpeed = req.body.statusEffectSpeed; }; diff --git a/routes/routerCodemon.js b/routes/routerCodemon.js index 4a80216..9f46888 100644 --- a/routes/routerCodemon.js +++ b/routes/routerCodemon.js @@ -39,13 +39,13 @@ router res.json(res.codemon); }) .patch(getCodemon, async (req, res) => { - if (req.body.name !== null) { + if (req.body.name !== undefined) { res.codemon.name = req.body.name; }; - if (req.body.type !== null) { + if (req.body.type !== undefined) { res.codemon.type = req.body.type; }; - if (req.body.abilities !== null) { + if (req.body.abilities !== undefined) { res.codemon.abilities = req.body.abilities; }; diff --git a/routes/routerTrainers.js b/routes/routerTrainers.js index 33adc90..14fa95c 100644 --- a/routes/routerTrainers.js +++ b/routes/routerTrainers.js @@ -39,13 +39,13 @@ router res.json(res.trainer); }) .patch(getTrainer, async (req, res) => { - if (req.body.name !== null) { + if (req.body.name !== undefined) { res.trainer.name = req.body.name; }; - if (req.body.battleDialogue !== null) { + if (req.body.codemon !== undefined) { res.trainer.codemon = req.body.codemon; }; - if (req.body.dialogue !== null) { + if (req.body.dialogue !== undefined) { res.trainer.dialogue = req.body.dialogue; }; diff --git a/testApi.rest b/testApi.rest index 05e5792..0812cd4 100644 --- a/testApi.rest +++ b/testApi.rest @@ -1,44 +1,294 @@ -// create one -POST http://localhost:3001/trainers +# Codemon API Test Suite +# Base URL: http://localhost:3001 + +### ======================================== +### ROOT ROUTE TESTS +### ======================================== + +# Test root route (should render index.ejs) +GET http://localhost:3001/ + +### ======================================== +### ABILITIES ROUTE TESTS +### ======================================== + +# Test GET all abilities +GET http://localhost:3001/abilities + +### + +# Test POST create new ability +POST http://localhost:3001/abilities +Content-Type: application/json + +{ + "name": "Test Ability", + "type": "Physical", + "description": "A test ability for testing purposes", + "power": 50, + "accuracy": 85, + "statusEffectAttack": 1, + "statusEffectSpecialAttack": 0, + "statusEffectDefense": -1, + "statusEffectSpecialDefense": 0, + "statusEffectSpeed": 0 +} + +### + +# Test POST create another ability +POST http://localhost:3001/abilities +Content-Type: application/json + +{ + "name": "Special Test Ability", + "type": "Special", + "description": "A special test ability", + "power": 75, + "accuracy": 90, + "statusEffectAttack": 0, + "statusEffectSpecialAttack": 2, + "statusEffectDefense": 0, + "statusEffectSpecialDefense": -1, + "statusEffectSpeed": 1 +} + +### + +# Test GET ability by ID (replace {id} with actual ID from previous POST) +GET http://localhost:3001/abilities/{id} + +### + +# Test PATCH update ability (replace {id} with actual ID) +PATCH http://localhost:3001/abilities/{id} +Content-Type: application/json + +{ + "power": 80, + "accuracy": 95 +} + +### + +# Test DELETE ability (replace {id} with actual ID) +DELETE http://localhost:3001/abilities/{id} + +### + +# Test GET non-existent ability +GET http://localhost:3001/abilities/507f1f77bcf86cd799439011 + +### ======================================== +### CODEMON ROUTE TESTS +### ======================================== + +# Test GET all codemon +GET http://localhost:3001/codemon + +### + +# Test POST create new codemon +POST http://localhost:3001/codemon +Content-Type: application/json + +{ + "name": "Test Codemon", + "type": "Fire", + "abilities": ["Test Ability", "Special Test Ability"] +} + +### + +# Test POST create another codemon +POST http://localhost:3001/codemon +Content-Type: application/json + +{ + "name": "Water Codemon", + "type": "Water", + "abilities": ["Water Gun", "Bubble"] +} + +### + +# Test GET codemon by ID (replace {id} with actual ID from previous POST) +GET http://localhost:3001/codemon/{id} + +### + +# Test PATCH update codemon (replace {id} with actual ID) +PATCH http://localhost:3001/codemon/{id} Content-Type: application/json { - "name": "Dan", - "codemon": { - "name": "birb" - }, - "dialogue": { - "startBattle": "It's import to keep reminding them that TypeScript has enums." - } + "name": "Updated Test Codemon", + "type": "Fire/Flying" } ### -// read all +# Test DELETE codemon (replace {id} with actual ID) +DELETE http://localhost:3001/codemon/{id} + +### + +# Test GET non-existent codemon +GET http://localhost:3001/codemon/507f1f77bcf86cd799439011 + +### ======================================== +### TRAINERS ROUTE TESTS +### ======================================== + +# Test GET all trainers GET http://localhost:3001/trainers ### -// read one -GET http://localhost:3001/trainers/688667ae538c4bf751ef2e5f +# Test POST create new trainer +POST http://localhost:3001/trainers +Content-Type: application/json + +{ + "name": "Test Trainer", + "codemon": ["Test Codemon"], + "dialogue": "Hello, I am a test trainer!" +} + +### + +# Test POST create another trainer +POST http://localhost:3001/trainers +Content-Type: application/json + +{ + "name": "Gym Leader", + "codemon": ["Water Codemon"], + "dialogue": "Welcome to my gym!" +} + +### + +# Test GET trainer by ID (replace {id} with actual ID from previous POST) +GET http://localhost:3001/trainers/{id} + +### + +# Test PATCH update trainer (replace {id} with actual ID) +PATCH http://localhost:3001/trainers/{id} +Content-Type: application/json + +{ + "name": "Updated Test Trainer", + "dialogue": "I have been updated!" +} + +### + +# Test DELETE trainer (replace {id} with actual ID) +DELETE http://localhost:3001/trainers/{id} + +### + +# Test GET non-existent trainer +GET http://localhost:3001/trainers/507f1f77bcf86cd799439011 + +### ======================================== +### DIALOGUE ROUTE TESTS +### ======================================== + +# Test GET all dialogue +GET http://localhost:3001/dialogue + +### + +# Test POST create new dialogue +POST http://localhost:3001/dialogue +Content-Type: application/json + +{ + "startDialogue": "Welcome to the battle!", + "battleDialogue": "Let's see what you've got!" +} + +### + +# Test POST create another dialogue +POST http://localhost:3001/dialogue +Content-Type: application/json + +{ + "startDialogue": "Are you ready?", + "battleDialogue": "This will be interesting!" +} + +### + +# Test GET dialogue by ID (replace {id} with actual ID from previous POST) +GET http://localhost:3001/dialogue/{id} + +### + +# Test PATCH update dialogue (replace {id} with actual ID) +PATCH http://localhost:3001/dialogue/{id} +Content-Type: application/json + +{ + "startDialogue": "Updated welcome message!", + "battleDialogue": "Updated battle message!" +} + +### + +# Test DELETE dialogue (replace {id} with actual ID) +DELETE http://localhost:3001/dialogue/{id} ### -// update one -PATCH http://localhost:3001/trainers/688667ae538c4bf751ef2e5f +# Test GET non-existent dialogue +GET http://localhost:3001/dialogue/507f1f77bcf86cd799439011 + +### ======================================== +### ERROR HANDLING TESTS +### ======================================== + +# Test 404 route +GET http://localhost:3001/nonexistent + +### + +# Test invalid JSON +POST http://localhost:3001/abilities Content-Type: application/json { - "name": "Nuc", - "codemon": { - "name": "fox" - }, - "dialogue": { - "startBattle": "Write it in Nim!" - } + "invalid": "json", + "missing": "required fields" } ### -// delete one -DELETE http://localhost:3001/trainers/688667a7538c4bf751ef2e5d \ No newline at end of file +# Test invalid ID format +GET http://localhost:3001/abilities/invalid-id + +### ======================================== +### STATIC FILE TESTS +### ======================================== + +# Test CSS file +GET http://localhost:3001/css/style.css + +### + +# Test JavaScript file +GET http://localhost:3001/js/main.js + +### + +# Test Manager file +GET http://localhost:3001/managers/GameManager.js + +### + +# Test Audio file +GET http://localhost:3001/assets/audio/effects/physical.mp3 \ No newline at end of file diff --git a/testRunner.js b/testRunner.js new file mode 100644 index 0000000..954147a --- /dev/null +++ b/testRunner.js @@ -0,0 +1,316 @@ +#!/usr/bin/env node + +/** + * Automated Test Runner for Codemon API + * Tests all routes and provides detailed results + */ + +import fetch from 'node-fetch'; + +const BASE_URL = 'http://localhost:3001'; +const TEST_RESULTS = { + passed: 0, + failed: 0, + errors: [] +}; + +// Test data +const testData = { + ability: { + name: "Test Ability", + type: "Physical", + description: "A test ability for testing purposes", + power: 50, + accuracy: 85, + statusEffectAttack: 1, + statusEffectSpecialAttack: 0, + statusEffectDefense: -1, + statusEffectSpecialDefense: 0, + statusEffectSpeed: 0 + }, + codemon: { + name: "Test Codemon", + type: "Fire", + abilities: ["Test Ability", "Special Test Ability"] + }, + trainer: { + name: "Test Trainer", + codemon: ["Test Codemon"], + dialogue: "Hello, I am a test trainer!" + }, + dialogue: { + startDialogue: "Welcome to the battle!", + battleDialogue: "Let's see what you've got!" + } +}; + +// Helper function to make requests +async function makeRequest(method, url, body = null) { + try { + const options = { + method, + headers: { + 'Content-Type': 'application/json' + } + }; + + if (body) { + options.body = JSON.stringify(body); + } + + const response = await fetch(`${BASE_URL}${url}`, options); + const data = await response.text(); + + let jsonData; + try { + jsonData = JSON.parse(data); + } catch { + jsonData = data; + } + + return { + status: response.status, + data: jsonData, + headers: response.headers + }; + } catch (error) { + return { + status: 0, + data: error.message, + headers: {} + }; + } +} + +// Test helper function +function assert(condition, message) { + if (condition) { + TEST_RESULTS.passed++; + console.log(`✅ ${message}`); + } else { + TEST_RESULTS.failed++; + TEST_RESULTS.errors.push(message); + console.log(`❌ ${message}`); + } +} + +// Test functions +async function testRootRoute() { + console.log('\n=== Testing Root Route ==='); + const response = await makeRequest('GET', '/'); + assert(response.status === 200, 'Root route should return 200'); +} + +async function testAbilitiesRoutes() { + console.log('\n=== Testing Abilities Routes ==='); + + // Test GET all abilities + const getAllResponse = await makeRequest('GET', '/abilities'); + assert(getAllResponse.status === 200, 'GET /abilities should return 200'); + + // Test POST create ability + const createResponse = await makeRequest('POST', '/abilities', testData.ability); + assert(createResponse.status === 201, 'POST /abilities should return 201'); + + if (createResponse.status === 201 && createResponse.data._id) { + const abilityId = createResponse.data._id; + + // Test GET ability by ID + const getByIdResponse = await makeRequest('GET', `/abilities/${abilityId}`); + assert(getByIdResponse.status === 200, 'GET /abilities/:id should return 200'); + + // Test PATCH update ability + const updateResponse = await makeRequest('PATCH', `/abilities/${abilityId}`, { + power: 80, + accuracy: 95 + }); + assert(updateResponse.status === 200, 'PATCH /abilities/:id should return 200'); + + // Test DELETE ability + const deleteResponse = await makeRequest('DELETE', `/abilities/${abilityId}`); + assert(deleteResponse.status === 200, 'DELETE /abilities/:id should return 200'); + } + + // Test GET non-existent ability + const notFoundResponse = await makeRequest('GET', '/abilities/507f1f77bcf86cd799439011'); + assert(notFoundResponse.status === 404, 'GET non-existent ability should return 404'); +} + +async function testCodemonRoutes() { + console.log('\n=== Testing Codemon Routes ==='); + + // Test GET all codemon + const getAllResponse = await makeRequest('GET', '/codemon'); + assert(getAllResponse.status === 200, 'GET /codemon should return 200'); + + // Test POST create codemon + const createResponse = await makeRequest('POST', '/codemon', testData.codemon); + assert(createResponse.status === 201, 'POST /codemon should return 201'); + + if (createResponse.status === 201 && createResponse.data._id) { + const codemonId = createResponse.data._id; + + // Test GET codemon by ID + const getByIdResponse = await makeRequest('GET', `/codemon/${codemonId}`); + assert(getByIdResponse.status === 200, 'GET /codemon/:id should return 200'); + + // Test PATCH update codemon + const updateResponse = await makeRequest('PATCH', `/codemon/${codemonId}`, { + name: "Updated Test Codemon", + type: "Fire/Flying" + }); + assert(updateResponse.status === 200, 'PATCH /codemon/:id should return 200'); + + // Test DELETE codemon + const deleteResponse = await makeRequest('DELETE', `/codemon/${codemonId}`); + assert(deleteResponse.status === 200, 'DELETE /codemon/:id should return 200'); + } + + // Test GET non-existent codemon + const notFoundResponse = await makeRequest('GET', '/codemon/507f1f77bcf86cd799439011'); + assert(notFoundResponse.status === 404, 'GET non-existent codemon should return 404'); +} + +async function testTrainersRoutes() { + console.log('\n=== Testing Trainers Routes ==='); + + // Test GET all trainers + const getAllResponse = await makeRequest('GET', '/trainers'); + assert(getAllResponse.status === 200, 'GET /trainers should return 200'); + + // Test POST create trainer + const createResponse = await makeRequest('POST', '/trainers', testData.trainer); + assert(createResponse.status === 201, 'POST /trainers should return 201'); + + if (createResponse.status === 201 && createResponse.data._id) { + const trainerId = createResponse.data._id; + + // Test GET trainer by ID + const getByIdResponse = await makeRequest('GET', `/trainers/${trainerId}`); + assert(getByIdResponse.status === 200, 'GET /trainers/:id should return 200'); + + // Test PATCH update trainer + const updateResponse = await makeRequest('PATCH', `/trainers/${trainerId}`, { + name: "Updated Test Trainer", + dialogue: "I have been updated!" + }); + assert(updateResponse.status === 200, 'PATCH /trainers/:id should return 200'); + + // Test DELETE trainer + const deleteResponse = await makeRequest('DELETE', `/trainers/${trainerId}`); + assert(deleteResponse.status === 200, 'DELETE /trainers/:id should return 200'); + } + + // Test GET non-existent trainer + const notFoundResponse = await makeRequest('GET', '/trainers/507f1f77bcf86cd799439011'); + assert(notFoundResponse.status === 404, 'GET non-existent trainer should return 404'); +} + +async function testDialogueRoutes() { + console.log('\n=== Testing Dialogue Routes ==='); + + // Test GET all dialogue + const getAllResponse = await makeRequest('GET', '/dialogue'); + assert(getAllResponse.status === 200, 'GET /dialogue should return 200'); + + // Test POST create dialogue + const createResponse = await makeRequest('POST', '/dialogue', testData.dialogue); + assert(createResponse.status === 201, 'POST /dialogue should return 201'); + + if (createResponse.status === 201 && createResponse.data._id) { + const dialogueId = createResponse.data._id; + + // Test GET dialogue by ID + const getByIdResponse = await makeRequest('GET', `/dialogue/${dialogueId}`); + assert(getByIdResponse.status === 200, 'GET /dialogue/:id should return 200'); + + // Test PATCH update dialogue + const updateResponse = await makeRequest('PATCH', `/dialogue/${dialogueId}`, { + startDialogue: "Updated welcome message!", + battleDialogue: "Updated battle message!" + }); + assert(updateResponse.status === 200, 'PATCH /dialogue/:id should return 200'); + + // Test DELETE dialogue + const deleteResponse = await makeRequest('DELETE', `/dialogue/${dialogueId}`); + assert(deleteResponse.status === 200, 'DELETE /dialogue/:id should return 200'); + } + + // Test GET non-existent dialogue + const notFoundResponse = await makeRequest('GET', '/dialogue/507f1f77bcf86cd799439011'); + assert(notFoundResponse.status === 404, 'GET non-existent dialogue should return 404'); +} + +async function testErrorHandling() { + console.log('\n=== Testing Error Handling ==='); + + // Test 404 route + const notFoundResponse = await makeRequest('GET', '/nonexistent'); + assert(notFoundResponse.status === 404, 'Non-existent route should return 404'); + + // Test invalid ID format + const invalidIdResponse = await makeRequest('GET', '/abilities/invalid-id'); + assert(invalidIdResponse.status === 500, 'Invalid ID format should return 500'); +} + +async function testStaticFiles() { + console.log('\n=== Testing Static Files ==='); + + // Test CSS file + const cssResponse = await makeRequest('GET', '/css/style.css'); + assert(cssResponse.status === 200, 'CSS file should be accessible'); + + // Test JavaScript file + const jsResponse = await makeRequest('GET', '/js/main.js'); + assert(jsResponse.status === 200, 'JavaScript file should be accessible'); + + // Test Manager file + const managerResponse = await makeRequest('GET', '/managers/GameManager.js'); + assert(managerResponse.status === 200, 'Manager file should be accessible'); +} + +// Main test runner +async function runTests() { + console.log('🚀 Starting Codemon API Tests...\n'); + + try { + await testRootRoute(); + await testAbilitiesRoutes(); + await testCodemonRoutes(); + await testTrainersRoutes(); + await testDialogueRoutes(); + await testErrorHandling(); + await testStaticFiles(); + + console.log('\n=== Test Summary ==='); + console.log(`✅ Passed: ${TEST_RESULTS.passed}`); + console.log(`❌ Failed: ${TEST_RESULTS.failed}`); + console.log(`📊 Total: ${TEST_RESULTS.passed + TEST_RESULTS.failed}`); + + if (TEST_RESULTS.errors.length > 0) { + console.log('\n❌ Failed Tests:'); + TEST_RESULTS.errors.forEach(error => { + console.log(` - ${error}`); + }); + } + + if (TEST_RESULTS.failed === 0) { + console.log('\n🎉 All tests passed!'); + process.exit(0); + } else { + console.log('\n💥 Some tests failed!'); + process.exit(1); + } + + } catch (error) { + console.error('\n💥 Test runner error:', error.message); + process.exit(1); + } +} + +// Run tests if this file is executed directly +if (import.meta.url === `file://${process.argv[1]}`) { + runTests(); +} + +export { runTests, makeRequest, assert }; \ No newline at end of file From 977f512e45872a576c2bb49e3bda086622956001 Mon Sep 17 00:00:00 2001 From: nallovint <67425227+nallovint@users.noreply.github.com> Date: Sun, 27 Jul 2025 21:10:08 +0100 Subject: [PATCH 2/2] api data logging --- apiDataLogger.js | 242 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- 2 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 apiDataLogger.js diff --git a/apiDataLogger.js b/apiDataLogger.js new file mode 100644 index 0000000..1ac8689 --- /dev/null +++ b/apiDataLogger.js @@ -0,0 +1,242 @@ +#!/usr/bin/env node + +/** + * API Data Logger - Tests API endpoints and logs all data + * This script fetches all data from the Codemon API and displays it in the console + */ + +import fetch from 'node-fetch'; + +const BASE_URL = 'http://localhost:3001'; + +// Helper function to make requests +async function fetchData(endpoint) { + try { + const response = await fetch(`${BASE_URL}${endpoint}`); + const data = await response.text(); + + let jsonData; + try { + jsonData = JSON.parse(data); + } catch { + jsonData = data; + } + + return { + status: response.status, + data: jsonData, + success: response.ok + }; + } catch (error) { + return { + status: 0, + data: error.message, + success: false + }; + } +} + +// Helper function to log data with formatting +function logData(title, data, status) { + console.log('\n' + '='.repeat(60)); + console.log(`📊 ${title.toUpperCase()}`); + console.log('='.repeat(60)); + console.log(`Status: ${status}`); + console.log(`Endpoint: ${BASE_URL}${title.toLowerCase()}`); + console.log('-'.repeat(60)); + + if (Array.isArray(data)) { + console.log(`📋 Found ${data.length} items:`); + if (data.length === 0) { + console.log(' No data found'); + } else { + data.forEach((item, index) => { + console.log(`\n${index + 1}. ${item.name || item._id || 'Unnamed Item'}:`); + console.log(JSON.stringify(item, null, 2)); + }); + } + } else if (typeof data === 'object') { + console.log('📋 Data:'); + console.log(JSON.stringify(data, null, 2)); + } else { + console.log('📋 Data:'); + console.log(data); + } +} + +// Test individual endpoints +async function testAbilities() { + console.log('\n🔍 Testing Abilities API...'); + const result = await fetchData('/abilities'); + + if (result.success) { + logData('Abilities', result.data, result.status); + + // Additional analysis + if (Array.isArray(result.data)) { + const types = [...new Set(result.data.map(item => item.type))]; + const avgPower = result.data.reduce((sum, item) => sum + (item.power || 0), 0) / result.data.length; + const avgAccuracy = result.data.reduce((sum, item) => sum + (item.accuracy || 0), 0) / result.data.length; + + console.log('\n📈 Abilities Analysis:'); + console.log(` Total Abilities: ${result.data.length}`); + console.log(` Types: ${types.join(', ')}`); + console.log(` Average Power: ${avgPower.toFixed(1)}`); + console.log(` Average Accuracy: ${avgAccuracy.toFixed(1)}%`); + } + } else { + console.log(`❌ Failed to fetch abilities: ${result.status} - ${result.data}`); + } +} + +async function testCodemon() { + console.log('\n🔍 Testing Codemon API...'); + const result = await fetchData('/codemon'); + + if (result.success) { + logData('Codemon', result.data, result.status); + + // Additional analysis + if (Array.isArray(result.data)) { + const types = [...new Set(result.data.map(item => item.type))]; + const avgAbilities = result.data.reduce((sum, item) => sum + (item.abilities?.length || 0), 0) / result.data.length; + + console.log('\n📈 Codemon Analysis:'); + console.log(` Total Codemon: ${result.data.length}`); + console.log(` Types: ${types.join(', ')}`); + console.log(` Average Abilities per Codemon: ${avgAbilities.toFixed(1)}`); + } + } else { + console.log(`❌ Failed to fetch codemon: ${result.status} - ${result.data}`); + } +} + +async function testTrainers() { + console.log('\n🔍 Testing Trainers API...'); + const result = await fetchData('/trainers'); + + if (result.success) { + logData('Trainers', result.data, result.status); + + // Additional analysis + if (Array.isArray(result.data)) { + const avgCodemon = result.data.reduce((sum, item) => sum + (item.codemon?.length || 0), 0) / result.data.length; + + console.log('\n📈 Trainers Analysis:'); + console.log(` Total Trainers: ${result.data.length}`); + console.log(` Average Codemon per Trainer: ${avgCodemon.toFixed(1)}`); + } + } else { + console.log(`❌ Failed to fetch trainers: ${result.status} - ${result.data}`); + } +} + +async function testDialogue() { + console.log('\n🔍 Testing Dialogue API...'); + const result = await fetchData('/dialogue'); + + if (result.success) { + logData('Dialogue', result.data, result.status); + + // Additional analysis + if (Array.isArray(result.data)) { + console.log('\n📈 Dialogue Analysis:'); + console.log(` Total Dialogue Entries: ${result.data.length}`); + } + } else { + console.log(`❌ Failed to fetch dialogue: ${result.status} - ${result.data}`); + } +} + +async function testRootRoute() { + console.log('\n🔍 Testing Root Route...'); + const result = await fetchData('/'); + + console.log('\n' + '='.repeat(60)); + console.log('🏠 ROOT ROUTE'); + console.log('='.repeat(60)); + console.log(`Status: ${result.status}`); + console.log(`Endpoint: ${BASE_URL}/`); + console.log('-'.repeat(60)); + + if (result.success) { + console.log('✅ Root route is accessible'); + console.log('📋 Response type: HTML/EJS template'); + } else { + console.log(`❌ Root route failed: ${result.status} - ${result.data}`); + } +} + +async function testStaticFiles() { + console.log('\n🔍 Testing Static Files...'); + + const staticFiles = [ + '/css/style.css', + '/js/main.js', + '/managers/GameManager.js', + '/assets/audio/effects/physical.mp3' + ]; + + console.log('\n' + '='.repeat(60)); + console.log('📁 STATIC FILES'); + console.log('='.repeat(60)); + + for (const file of staticFiles) { + const result = await fetchData(file); + const status = result.success ? '✅' : '❌'; + console.log(`${status} ${file}: ${result.status}`); + } +} + +async function testErrorHandling() { + console.log('\n🔍 Testing Error Handling...'); + + const errorTests = [ + { endpoint: '/nonexistent', expectedStatus: 404, description: 'Non-existent route' }, + { endpoint: '/abilities/invalid-id', expectedStatus: 500, description: 'Invalid ID format' } + ]; + + console.log('\n' + '='.repeat(60)); + console.log('🚨 ERROR HANDLING'); + console.log('='.repeat(60)); + + for (const test of errorTests) { + const result = await fetchData(test.endpoint); + const status = result.status === test.expectedStatus ? '✅' : '❌'; + console.log(`${status} ${test.description}: ${result.status} (expected ${test.expectedStatus})`); + } +} + +// Main function to run all tests +async function runAllTests() { + console.log('🚀 Starting Codemon API Data Logger...'); + console.log(`📍 Base URL: ${BASE_URL}`); + console.log(`⏰ Timestamp: ${new Date().toISOString()}`); + + try { + await testRootRoute(); + await testAbilities(); + await testCodemon(); + await testTrainers(); + await testDialogue(); + await testStaticFiles(); + await testErrorHandling(); + + console.log('\n' + '='.repeat(60)); + console.log('🎉 API DATA LOGGING COMPLETE'); + console.log('='.repeat(60)); + console.log('📊 All endpoints have been tested and data logged above.'); + console.log('💡 Use this information to understand your current database state.'); + + } catch (error) { + console.error('\n💥 Error during API testing:', error.message); + process.exit(1); + } +} + +// Run tests if this file is executed directly +if (import.meta.url === `file://${process.argv[1]}`) { + runAllTests(); +} + +export { runAllTests, fetchData, logData }; \ No newline at end of file diff --git a/package.json b/package.json index 2ce6132..b1751b9 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "devStart": "nodemon server.js", "dev:full": "concurrently \"npm run server\" \"npm run dev\"", "test": "node testRunner.js", - "test:api": "node testRunner.js" + "test:api": "node testRunner.js", + "log:api": "node apiDataLogger.js" }, "devDependencies": { "@types/express": "^5.0.3",