godcrm/backend/routes/v3/wa/__tests__/waApi.test.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

409 lines
13 KiB
JavaScript

/**
* WorkAdventure Admin API Tests - ADR-063
* Testing Admin API endpoints for room access control
*/
import { describe, test, expect, beforeEach, afterEach } from 'vitest';
import request from 'supertest';
import express from 'express';
import jwt from 'jsonwebtoken';
import { dbAll, dbGet, dbRun, destroyAdapter, resetAdapter } from '../../../../database/connection.js';
// Import routes (will be created)
import waRoutes from '../index.js';
// Create test app
const app = express();
app.use(express.json());
// Mock authenticate middleware for testing
app.use((req, res, next) => {
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'test-secret');
req.user = decoded;
} catch (e) {
// Invalid token - continue without user
}
}
next();
});
app.use('/api/v3/wa', waRoutes);
// Test JWT secret
const TEST_JWT_SECRET = 'test-jwt-secret-for-wa';
process.env.JWT_SECRET = TEST_JWT_SECRET;
// Helper functions
async function createTestUser(role = 'user') {
const uniqueEmail = `test-wa-${Date.now()}-${Math.random().toString(36).substring(7)}@hltrn.cc`;
const result = await dbRun(
'INSERT INTO users (email, password_hash, name, role, avatar, encryption_key_encrypted, email_verified) VALUES (?, ?, ?, ?, ?, ?, ?)',
[uniqueEmail, 'hash', 'Test User', role, 'https://example.com/avatar.png', 'encrypted_key', 1]
);
return { id: result.lastInsertRowid, email: uniqueEmail, role };
}
async function createTestSpace(ownerId) {
const result = await dbRun(
'INSERT INTO spaces (owner_id, name, type) VALUES (?, ?, ?)',
[ownerId, 'Test Space', 'business']
);
return result.lastInsertRowid;
}
function createToken(user) {
return jwt.sign(
{ id: user.id, email: user.email, role: user.role },
TEST_JWT_SECRET,
{ expiresIn: '1h' }
);
}
describe('WorkAdventure Admin API - ADR-063', () => {
let testUser;
let adminUser;
let spaceId;
beforeEach(async () => {
process.env.TEST_MODE = 'true';
process.env.SKIP_DEV_USER = 'true';
await resetAdapter();
testUser = await createTestUser('user');
adminUser = await createTestUser('admin');
spaceId = await createTestSpace(adminUser.id);
});
afterEach(async () => {
await destroyAdapter();
});
// ============================================================
// GET /api/v3/wa/map
// ============================================================
describe('GET /api/v3/wa/map', () => {
test('should return map URL for authenticated user', async () => {
const token = createToken(testUser);
const response = await request(app)
.get('/api/v3/wa/map')
.set('Authorization', `Bearer ${token}`)
.query({
playUri: 'https://play.workadventure.localhost/@/crm/office/main',
userIdentifier: testUser.email
})
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.mapUrl).toBeDefined();
expect(response.body.data.mapUrl).toContain('.json');
});
test('should return 401 without authentication', async () => {
const response = await request(app)
.get('/api/v3/wa/map')
.query({
playUri: 'https://play.workadventure.localhost/@/crm/office/main'
})
.expect(401);
expect(response.body.success).toBe(false);
});
test('should return default map for unknown room', async () => {
const token = createToken(testUser);
const response = await request(app)
.get('/api/v3/wa/map')
.set('Authorization', `Bearer ${token}`)
.query({
playUri: 'https://play.workadventure.localhost/@/crm/unknown/room'
})
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.mapUrl).toBeDefined();
});
});
// ============================================================
// GET /api/v3/wa/room/access
// ============================================================
describe('GET /api/v3/wa/room/access', () => {
test('should grant access to public room', async () => {
const token = createToken(testUser);
const response = await request(app)
.get('/api/v3/wa/room/access')
.set('Authorization', `Bearer ${token}`)
.query({
playUri: 'https://play.workadventure.localhost/@/crm/public/lobby',
userIdentifier: testUser.email
})
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.access).toBe(true);
expect(response.body.data.tags).toBeDefined();
});
test('should deny access to restricted room for regular user', async () => {
const token = createToken(testUser);
const response = await request(app)
.get('/api/v3/wa/room/access')
.set('Authorization', `Bearer ${token}`)
.query({
playUri: 'https://play.workadventure.localhost/@/crm/admin/control-room',
userIdentifier: testUser.email
})
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.access).toBe(false);
expect(response.body.data.reason).toBeDefined();
});
test('should grant access to admin room for admin user', async () => {
const token = createToken(adminUser);
const response = await request(app)
.get('/api/v3/wa/room/access')
.set('Authorization', `Bearer ${token}`)
.query({
playUri: 'https://play.workadventure.localhost/@/crm/admin/control-room',
userIdentifier: adminUser.email
})
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.access).toBe(true);
});
test('should return 401 without authentication', async () => {
const response = await request(app)
.get('/api/v3/wa/room/access')
.query({
playUri: 'https://play.workadventure.localhost/@/crm/public/lobby'
})
.expect(401);
expect(response.body.success).toBe(false);
});
});
// ============================================================
// GET /api/v3/wa/member
// ============================================================
describe('GET /api/v3/wa/member', () => {
test('should return member info with tags', async () => {
const token = createToken(testUser);
const response = await request(app)
.get('/api/v3/wa/member')
.set('Authorization', `Bearer ${token}`)
.query({
userIdentifier: testUser.email,
playUri: 'https://play.workadventure.localhost/@/crm/office/main'
})
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.email).toBe(testUser.email);
expect(response.body.data.tags).toBeDefined();
expect(Array.isArray(response.body.data.tags)).toBe(true);
expect(response.body.data.textures).toBeDefined();
});
test('should return admin tag for admin user', async () => {
const token = createToken(adminUser);
const response = await request(app)
.get('/api/v3/wa/member')
.set('Authorization', `Bearer ${token}`)
.query({
userIdentifier: adminUser.email,
playUri: 'https://play.workadventure.localhost/@/crm/office/main'
})
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.tags).toContain('admin');
});
test('should return 404 for unknown user', async () => {
const token = createToken(adminUser);
const response = await request(app)
.get('/api/v3/wa/member')
.set('Authorization', `Bearer ${token}`)
.query({
userIdentifier: 'unknown@example.com',
playUri: 'https://play.workadventure.localhost/@/crm/office/main'
})
.expect(404);
expect(response.body.success).toBe(false);
});
});
// ============================================================
// POST /api/v3/wa/webhook
// ============================================================
describe('POST /api/v3/wa/webhook', () => {
test('should handle user join event', async () => {
const response = await request(app)
.post('/api/v3/wa/webhook')
.set('X-WA-Webhook-Secret', process.env.WA_WEBHOOK_SECRET || 'test-webhook-secret')
.send({
event: 'user.join',
data: {
userIdentifier: testUser.email,
roomId: '@/crm/office/main',
timestamp: new Date().toISOString()
}
})
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.processed).toBe(true);
// Verify presence was recorded
const presence = await dbGet(
'SELECT * FROM wa_presence WHERE user_id = ? AND room_id = ?',
[testUser.id, '@/crm/office/main']
);
expect(presence).toBeDefined();
expect(presence.status).toBe('online');
});
test('should handle user leave event', async () => {
// First, create a presence record
await dbRun(
'INSERT INTO wa_presence (user_id, room_id, status, joined_at) VALUES (?, ?, ?, ?)',
[testUser.id, '@/crm/office/main', 'online', new Date().toISOString()]
);
const response = await request(app)
.post('/api/v3/wa/webhook')
.set('X-WA-Webhook-Secret', process.env.WA_WEBHOOK_SECRET || 'test-webhook-secret')
.send({
event: 'user.leave',
data: {
userIdentifier: testUser.email,
roomId: '@/crm/office/main',
timestamp: new Date().toISOString()
}
})
.expect(200);
expect(response.body.success).toBe(true);
// Verify presence was updated
const presence = await dbGet(
'SELECT * FROM wa_presence WHERE user_id = ? AND room_id = ?',
[testUser.id, '@/crm/office/main']
);
expect(presence.left_at).toBeDefined();
});
test('should reject webhook without secret', async () => {
const response = await request(app)
.post('/api/v3/wa/webhook')
.send({
event: 'user.join',
data: {
userIdentifier: testUser.email,
roomId: '@/crm/office/main'
}
})
.expect(401);
expect(response.body.success).toBe(false);
});
test('should reject webhook with invalid secret', async () => {
const response = await request(app)
.post('/api/v3/wa/webhook')
.set('X-WA-Webhook-Secret', 'wrong-secret')
.send({
event: 'user.join',
data: {
userIdentifier: testUser.email,
roomId: '@/crm/office/main'
}
})
.expect(401);
expect(response.body.success).toBe(false);
});
test('should handle unknown event gracefully', async () => {
const response = await request(app)
.post('/api/v3/wa/webhook')
.set('X-WA-Webhook-Secret', process.env.WA_WEBHOOK_SECRET || 'test-webhook-secret')
.send({
event: 'unknown.event',
data: {}
})
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.processed).toBe(false);
expect(response.body.data.reason).toBe('unknown_event');
});
});
// ============================================================
// GET /api/v3/wa/presence
// ============================================================
describe('GET /api/v3/wa/presence', () => {
test('should return online users in a room', async () => {
const token = createToken(adminUser);
// Create presence records
await dbRun(
'INSERT INTO wa_presence (user_id, room_id, status, joined_at) VALUES (?, ?, ?, ?)',
[testUser.id, '@/crm/office/main', 'online', new Date().toISOString()]
);
const response = await request(app)
.get('/api/v3/wa/presence')
.set('Authorization', `Bearer ${token}`)
.query({ roomId: '@/crm/office/main' })
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.users).toBeDefined();
expect(response.body.data.users.length).toBeGreaterThan(0);
});
test('should return all online users without room filter', async () => {
const token = createToken(adminUser);
// Clean existing presence records to ensure test isolation
await dbRun('DELETE FROM wa_presence');
// Create presence records in different rooms
await dbRun(
'INSERT INTO wa_presence (user_id, room_id, status, joined_at) VALUES (?, ?, ?, ?)',
[testUser.id, '@/crm/office/main', 'online', new Date().toISOString()]
);
await dbRun(
'INSERT INTO wa_presence (user_id, room_id, status, joined_at) VALUES (?, ?, ?, ?)',
[adminUser.id, '@/crm/office/meeting', 'online', new Date().toISOString()]
);
const response = await request(app)
.get('/api/v3/wa/presence')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(response.body.success).toBe(true);
expect(response.body.data.users.length).toBe(2);
});
});
});