This guide provides comprehensive documentation for using mock mode in the mc-aws project. Mock mode enables offline development and testing without requiring AWS resources.
New to mock mode? Start with the Quick Start Guide to get up and running in 5 minutes.
- Quick Start
- Authentication in Mock Mode
- Environment Variables
- NPM Scripts
- Scenarios
- Mock Control API
- Common Development Workflows
- Fault Injection
- Persistence
- Troubleshooting
# Start in mock mode with dev login enabled
pnpm dev:mock
# The server will be available at http://localhost:3000
# You'll be automatically logged in as an admin user# Run E2E tests in mock mode
pnpm test:e2e:mock
# Run unit tests in mock mode
pnpm test:mock# List available scenarios
pnpm mock:scenario
# Apply a specific scenario
pnpm mock:scenario running
# Reset to default state
pnpm mock:resetMock mode provides a streamlined authentication experience that doesn't require Google OAuth or AWS credentials. This makes local development faster and easier.
In mock mode, you can use the dev login feature to authenticate instantly:
- Set
ENABLE_DEV_LOGIN=truein your.envfile - Visit
http://localhost:3000/api/auth/dev-loginin your browser - You're automatically logged in with a real session cookie (valid for 30 days)
The dev login creates a real JWT token with the following credentials:
- Email:
dev@localhost - Role:
admin(full access to all features)
The dev login endpoint includes multiple security safeguards:
- Production blocking: Returns 404 in production (
NODE_ENV=production) - Explicit opt-in: Only works when
ENABLE_DEV_LOGIN=trueis set - Non-production secret: Uses your local
AUTH_SECRETfor signing - Same session mechanism: Uses the same JWT verification as production
To test different permission levels, you can modify the dev login endpoint:
Edit app/api/auth/dev-login/route.ts and change the role value:
const token = await new SignJWT({
email: "dev@localhost",
role: "admin", // Change to "allowed" or "public" to test different roles
})Role Permissions:
| Role | Can View Status | Can Start Server | Can Backup/Restore/Hibernate |
|---|---|---|---|
admin |
✅ | ✅ | ✅ |
allowed |
✅ | ✅ | ❌ |
public |
✅ | ❌ | ❌ |
Benefits:
- Fast: No Google OAuth flow, no popup windows
- Deterministic: Same credentials every time
- Realistic: Uses the same session mechanism as production
- Flexible: Easy to test different permission levels
Compared to bypassing auth:
- Catches auth bugs during development
- Tests the same code paths as production
- No need to remember to re-enable auth before deploying
Option 1: Using the convenience script (recommended)
# The dev:mock script automatically sets both MC_BACKEND_MODE and ENABLE_DEV_LOGIN
pnpm dev:mockOption 2: Manual configuration
-
Copy the example configuration:
cp .env.mock.example .env.local
-
The
.env.localfile now has the minimal mock mode configuration:MC_BACKEND_MODE=mock ENABLE_DEV_LOGIN=true AUTH_SECRET=dev-secret-change-in-production NEXT_PUBLIC_APP_URL=http://localhost:3000
-
Start the dev server:
pnpm install --frozen-lockfile pnpm dev:mock
-
Visit the dev login endpoint:
open http://localhost:3000/api/auth/dev-login
After visiting the dev login endpoint, you can verify you're authenticated:
Via browser:
- The login button in the header should change to "Sign out"
- You should see admin-only features (cost dashboard, email management)
Via API:
curl http://localhost:3000/api/auth/me
# Returns: {"authenticated":true,"email":"dev@localhost","role":"admin"}To sign out and test the unauthenticated experience:
# Via browser: Click "Sign out" button in the header
# Via API:
curl -X POST http://localhost:3000/api/auth/logoutDev login returns 403:
- Ensure
ENABLE_DEV_LOGIN=trueis set in.env.local - Restart the dev server after changing environment variables
Dev login returns 404:
- Check that
NODE_ENVis not set toproduction - In development, Next.js sets
NODE_ENV=developmentautomatically
Session expires quickly:
- Dev login creates a 30-day session by default
- If you need to refresh, just visit
/api/auth/dev-loginagain
Protected routes still block access:
- Verify the session cookie was set (check browser dev tools → Application → Cookies)
- Check that
AUTH_SECRETis set in.env.local
The mock mode E2E tests use dev login automatically:
// From tests/mock-mode-e2e.spec.ts
async function authenticateAsDev(page: any): Promise<void> {
// Navigate to dev login endpoint (it will set the cookie and redirect)
await page.goto("/api/auth/dev-login");
// Wait for redirect to home page
await page.waitForURL("/");
// Verify we're authenticated
const authCheck = await page.request.get("/api/auth/me");
const authData = await authCheck.json();
expect(authData.authenticated).toBe(true);
}This ensures tests are fast, deterministic, and don't require real Google OAuth credentials.
Mock mode is controlled by environment variables. These can be set in .env for local development.
| Variable | Description | Example |
|---|---|---|
MC_BACKEND_MODE |
Backend mode: aws or mock |
mock |
ENABLE_DEV_LOGIN |
Enable dev login route for local auth testing | true |
AUTH_SECRET |
Secret for signing JWT tokens | dev-secret-... |
NEXT_PUBLIC_APP_URL |
App URL for redirects | http://localhost:3000 |
| Variable | Description | Example |
|---|---|---|
MOCK_STATE_PATH |
Path for mock state persistence file | ./mock-state.json |
MOCK_SCENARIO |
Default scenario to apply on startup | running |
# Enable mock mode
MC_BACKEND_MODE=mock
# Enable dev login for easy authentication
ENABLE_DEV_LOGIN=true
# Required for authentication
AUTH_SECRET=dev-secret-change-in-production
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Optional: Persist mock state to file
MOCK_STATE_PATH=./mock-state.json
# Optional: Apply default scenario on startup
MOCK_SCENARIO=defaultNote: AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, etc.) are not required in mock mode. The mock backend simulates all AWS operations locally.
Mock mode provides several convenient npm scripts:
pnpm dev:mockStarts the Next.js development server in mock mode with:
MC_BACKEND_MODE=mockENABLE_DEV_LOGIN=true- Port 3000
pnpm test:mockRuns Vitest unit tests in mock mode.
pnpm test:e2e:mockRuns Playwright E2E tests in mock mode.
pnpm mock:resetResets the mock state to default values.
pnpm mock:scenarioLists all available scenarios.
pnpm mock:scenario <name>Applies a specific scenario by name.
Scenarios are predefined states that set up the mock backend for specific testing situations. All scenarios reset the state before applying their configuration.
Description: Normal operation, instance stopped with default settings
State:
- Instance:
stopped - Public IP: None
- Volume: Attached
- Player count: 0
- Backups: 3 sample backups
Use case: Baseline state for testing
Description: Instance is already running with public IP assigned
State:
- Instance:
running - Public IP:
203.0.113.42 - Volume: Attached
- Player count: 5
Use case: Testing UI when server is active
Description: Instance is in pending state, transitioning to running
State:
- Instance:
pending - Public IP: None
- Volume: Attached
Use case: Testing loading states and transitions
Description: Instance is in stopping state, transitioning to stopped
State:
- Instance:
stopping - Public IP:
203.0.113.42 - Volume: Attached
Use case: Testing shutdown transitions
Description: Instance is stopped without volumes (hibernated state)
State:
- Instance:
stopped - Public IP: None
- Volume: Not attached
- Block device mappings: Empty
Use case: Testing hibernation/resume flows
Description: Instance with high monthly costs for testing cost alerts
State:
- Instance:
running - Public IP:
203.0.113.42 - Current month cost: $125.50
- Last month cost: $118.75
- Last 30 days cost: $244.25
Use case: Testing cost alert UI and thresholds
Description: No backups available for testing backup error handling
State:
- Backups: Empty array
Use case: Testing error handling when no backups exist
Description: Instance running with high player count for testing scaling
State:
- Instance:
running - Public IP:
203.0.113.42 - Player count: 18
Use case: Testing UI with high player counts
Description: CloudFormation stack is in CREATE_IN_PROGRESS state
State:
- Stack exists:
true - Stack status:
CREATE_IN_PROGRESS - Stack ID:
arn:aws:cloudformation:us-east-1:123456789012:stack/minecraft-stack/abc123
Use case: Testing stack deployment flows
Description: All operations fail with errors for testing error handling
State:
startInstance: Fails withInstanceLimitExceededstopInstance: Fails withIncorrectStategetCosts: Fails withAccessDeniedexecuteSSMCommand: Fails withInvalidInstanceIdgetStackStatus: Fails withValidationErrorcheckStackExists: Fails withValidationError
Use case: Comprehensive error handling testing
When running in mock mode, you can control the mock state via HTTP endpoints. These endpoints are only available when MC_BACKEND_MODE=mock.
GET /api/mock/stateReturns the complete mock state including instance, SSM parameters, backups, costs, and fault configurations.
Response:
{
"success": true,
"data": {
"instance": {
"instanceId": "i-mock1234567890abcdef",
"state": "stopped",
"publicIp": null,
"hasVolume": true,
"lastUpdated": "2026-01-30T12:00:00.000Z"
},
"ssm": {
"parameters": {
"/minecraft/player-count": {
"value": "0",
"type": "String",
"lastModified": "2026-01-30T12:00:00.000Z"
}
},
"commands": []
},
"backups": [...],
"costs": {...},
"cloudformation": {...},
"faults": {...}
},
"timestamp": "2026-01-30T12:00:00.000Z"
}GET /api/mock/scenarioReturns all available scenarios and the currently active scenario.
Response:
{
"success": true,
"data": {
"currentScenario": "default",
"availableScenarios": [
{
"name": "default",
"description": "Normal operation, instance stopped with default settings"
},
{
"name": "running",
"description": "Instance is already running with public IP assigned"
}
// ... more scenarios
]
},
"timestamp": "2026-01-30T12:00:00.000Z"
}POST /api/mock/scenario
Content-Type: application/json
{
"scenario": "running"
}Applies a specific scenario to the mock state.
Response:
{
"success": true,
"data": {
"scenario": "running",
"message": "Scenario \"running\" applied successfully"
},
"timestamp": "2026-01-30T12:00:00.000Z"
}POST /api/mock/resetResets the mock state to default values.
Response:
{
"success": true,
"data": {
"message": "Mock state reset to defaults successfully"
},
"timestamp": "2026-01-30T12:00:00.000Z"
}POST /api/mock/fault
Content-Type: application/json
{
"operation": "startInstance",
"failNext": true,
"errorCode": "InstanceLimitExceeded",
"errorMessage": "You have reached the maximum number of running instances"
}Injects fault configuration for a specific operation.
Response:
{
"success": true,
"data": {
"operation": "startInstance",
"message": "Fault injected successfully"
},
"timestamp": "2026-01-30T12:00:00.000Z"
}# 1. Reset to default state (server stopped)
pnpm mock:reset
# 2. Start dev server
pnpm dev:mock
# 3. Use the web UI or API to start the server
curl -X POST http://localhost:3000/api/start
# 4. Check the state transitions
curl http://localhost:3000/api/status
# Should show: stopped → pending → running
# 5. Stop the server
curl -X POST http://localhost:3000/api/stop
# 6. Verify state returns to stopped
curl http://localhost:3000/api/status# 1. Apply the errors scenario
pnpm mock:scenario errors
# 2. Start dev server
pnpm dev:mock
# 3. Try to start the server (will fail)
curl -X POST http://localhost:3000/api/start
# 4. Verify error handling in UI
# The UI should display the error message gracefully
# 5. Reset when done
pnpm mock:reset# 1. Apply the high-cost scenario
pnpm mock:scenario high-cost
# 2. Start dev server
pnpm dev:mock
# 3. Open http://localhost:3000 in your browser
# 4. Verify:
# - Cost dashboard shows high costs
# - Cost alerts and warnings are displayed
# - Cost breakdown is accurate
# 5. Test with different scenarios
pnpm mock:scenario running
# Refresh browser and verify UI updates# 1. Apply default scenario (has backups)
pnpm mock:reset
# 2. Start dev server
pnpm dev:mock
# 3. List backups
curl http://localhost:3000/api/backups
# 4. Create a backup
curl -X POST http://localhost:3000/api/backup
# 5. Restore from backup
curl -X POST http://localhost:3000/api/restore \
-H "Content-Type: application/json" \
-d '{"backupName": "backup-2026-01-30"}'
# 6. Test with no backups scenario
pnpm mock:scenario no-backups
# Verify error handling when no backups exist# 1. Apply hibernated scenario
pnpm mock:scenario hibernated
# 2. Start dev server
pnpm dev:mock
# 3. Verify state shows hibernated (no volume)
curl http://localhost:3000/api/status
# 4. Resume from hibernation
curl -X POST http://localhost:3000/api/resume
# 5. Verify volume is created and instance starts
curl http://localhost:3000/api/status
# 6. Hibernate again
curl -X POST http://localhost:3000/api/hibernate
# 7. Verify volume is deleted
curl http://localhost:3000/api/statusFault injection allows you to simulate AWS API failures for testing error handling.
The next call to the operation will fail, then the fault is cleared.
curl -X POST http://localhost:3000/api/mock/fault \
-H "Content-Type: application/json" \
-d '{
"operation": "startInstance",
"failNext": true,
"errorCode": "InstanceLimitExceeded",
"errorMessage": "You have reached the maximum number of running instances"
}'All calls to the operation will fail until the fault is cleared.
curl -X POST http://localhost:3000/api/mock/fault \
-H "Content-Type: application/json" \
-d '{
"operation": "getCosts",
"alwaysFail": true,
"errorCode": "AccessDenied",
"errorMessage": "User is not authorized to access Cost Explorer"
}'Add artificial delay to operations for testing loading states.
curl -X POST http://localhost:3000/api/mock/fault \
-H "Content-Type: application/json" \
-d '{
"operation": "startInstance",
"latency": 5000
}'# Clear a specific fault
curl -X POST http://localhost:3000/api/mock/fault \
-H "Content-Type: application/json" \
-d '{
"operation": "startInstance",
"clear": true
}'
# Or use the errors scenario to set multiple faults
pnpm mock:scenario errors
# Then reset to clear all faults
pnpm mock:resetstartInstance- Start EC2 instancestopInstance- Stop EC2 instancegetInstanceState- Get instance stateexecuteSSMCommand- Execute SSM commandgetSSMParameter- Get SSM parametersetSSMParameter- Set SSM parametergetCosts- Get cost datagetStackStatus- Get CloudFormation stack statuscheckStackExists- Check if stack exists
Mock state can optionally be persisted to a JSON file for debugging and issue reproduction.
Add to .env:
MOCK_STATE_PATH=./mock-state.jsonThe state will be automatically saved to this file on every change.
- Debugging: Inspect the state after a test run
- Reproduction: Share the state file to reproduce issues
- Development: Save interesting states for later testing
# Enable persistence
echo "MOCK_STATE_PATH=./mock-state.json" >> .env
# Apply a scenario
pnpm mock:scenario running
# Start dev server
pnpm dev:mock
# Make some changes via the API
curl -X POST http://localhost:3000/api/start
# Check the persisted state
cat mock-state.json
# The file contains the complete mock state
# You can share this file to reproduce the exact stateProblem: Application is still trying to connect to AWS
Solution: Verify MC_BACKEND_MODE=mock is set:
# Check the environment variable
echo $MC_BACKEND_MODE
# Or use the dev:mock script which sets it automatically
pnpm dev:mockProblem: Scenario changes don't seem to take effect
Solution: Ensure you're running in mock mode and restart the dev server:
# Stop the dev server (Ctrl+C)
# Reset state
pnpm mock:reset
# Apply scenario
pnpm mock:scenario running
# Start dev server
pnpm dev:mockProblem: Can't access protected endpoints in mock mode
Solution: Enable dev login:
# Add to .env
ENABLE_DEV_LOGIN=true
# Restart the dev server
# (Environment variables are only loaded on startup)
# Visit the dev login route
open http://localhost:3000/api/auth/dev-login
# You'll be logged in as an admin userProblem: Dev login returns 403 Forbidden
Solution: Verify environment variables:
# Check that ENABLE_DEV_LOGIN is set
echo $ENABLE_DEV_LOGIN
# If not set, add to .env and restart dev server
echo "ENABLE_DEV_LOGIN=true" >> .env
pnpm dev:mockProblem: Dev login returns 404 Not Found
Solution: Check production mode:
# Ensure NODE_ENV is not "production"
# In development, Next.js sets this automatically
# If you manually set NODE_ENV=production, dev login will be disabled
# Check current value
echo $NODE_ENV
# Unset if needed
unset NODE_ENV
pnpm dev:mockProblem: Session cookie not being set
Solution: Check AUTH_SECRET:
# Ensure AUTH_SECRET is set in .env
grep AUTH_SECRET .env
# If missing, add it:
echo "AUTH_SECRET=dev-secret-change-in-production" >> .env
# Restart dev server
pnpm dev:mockProblem: Protected routes still block access after dev login
Solution: Verify session cookie:
- Open browser dev tools (F12)
- Go to Application → Cookies
- Look for
mc_sessioncookie - If missing, visit
/api/auth/dev-loginagain - If present but still blocked, check the cookie value is valid
Problem: Want to test different user roles
Solution: Modify dev login endpoint:
Edit app/api/auth/dev-login/route.ts:
// Change role to test different permissions
role: "allowed", // or "public"Then restart dev server and visit /api/auth/dev-login again.
Problem: Mock state resets on server restart
Solution: Enable persistence:
# Add to .env
MOCK_STATE_PATH=./mock-state.json
# Restart the dev server
# State will now persist across restartsProblem: Tests pass in AWS mode but fail in mock mode
Solution: Check for AWS-specific behavior:
- Verify the scenario matches your test expectations
- Check for timing issues (mock mode is instant, AWS has latency)
- Ensure fault injection is cleared between tests
- Verify authentication is properly mocked
# Reset state before tests
pnpm mock:reset
# Run tests
pnpm test:e2e:mockProblem: Can't start dev server, port 3000 is in use
Solution: Kill the existing process or use a different port:
# Find and kill the process
lsof -ti:3000 | xargs kill -9
# Or use a different port
MC_BACKEND_MODE=mock ENABLE_DEV_LOGIN=true next dev -p 3002- Always reset between test runs: Use
pnpm mock:resetto ensure a clean state - Use scenarios for setup: Apply scenarios instead of manually configuring state
- Test error cases: Use the
errorsscenario to verify error handling - Enable persistence for debugging: Use
MOCK_STATE_PATHwhen investigating issues - Keep tests isolated: Each test should reset state and apply its own scenario
- Document custom scenarios: If you create custom scenarios, document their purpose
- README.md - Main project documentation
- Mock Scenarios Implementation - Technical details
- Mock State Store - State management
- Mock Provider - AWS client mocking
If you encounter issues not covered in this guide:
- Check the troubleshooting section
- Review the mock state using
/api/mock/state - Enable persistence and inspect the state file
- Check the console logs for error messages
- Open an issue on GitHub with details about your scenario