godcrm/backend/routes/v3/__tests__/agent-users.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

230 lines
7.9 KiB
JavaScript

/**
* Agent User API Tests
* ADR-023: Agent-as-User & Infinite Chat Architecture
*
* Tests for creating agent users, listing agent users, and generating API keys
*
* NOTE: These tests are skipped for SQLite because agent-users.js uses
* PostgreSQL-specific SQL syntax ($1, $2, RETURNING, NOW()).
* Run with DATABASE_TYPE=postgres for full test coverage.
*/
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
import { dbGet, dbAll, dbRun, sqlNow, isPostgres, resetAdapter } from '../../../database/connection.js';
// Skip all tests if using SQLite (agent-users.js uses PostgreSQL syntax)
const describePostgres = isPostgres() ? describe : describe.skip;
// Mock authenticate middleware for testing
vi.mock('../../../middleware/auth.js', () => ({
authenticate: (req, res, next) => {
req.user = { id: 1, email: 'test@example.com', role: 'admin' };
next();
}
}));
describePostgres('Agent User API', () => {
let testAgentTableId;
let testAgentRowId;
let testProjectId;
let testSpaceId;
beforeAll(async () => {
// Skip dev user creation (depends on NeoMetal which doesn't exist in tests)
process.env.SKIP_DEV_USER = 'true';
// Initialize database schema (required for SQLite in-memory tests)
await resetAdapter();
// Create test user first (foreign key constraint)
await dbRun(`
INSERT INTO users (id, email, password_hash, name, encryption_key_encrypted)
VALUES (1, 'test@example.com', 'hash', 'Test User', 'encrypted_key')
ON CONFLICT (id) DO NOTHING
`);
// Create test space
const spaceResult = await dbRun(`
INSERT INTO spaces (name, owner_id, type, created_at, updated_at)
VALUES (?, 1, 'business', ${sqlNow()}, ${sqlNow()})
`, ['Test AI Space']);
testSpaceId = spaceResult.lastInsertRowid;
// Create test project
const projectResult = await dbRun(`
INSERT INTO projects (name, space_id, owner_id, type, created_at, updated_at)
VALUES (?, ?, 1, 'custom', ${sqlNow()}, ${sqlNow()})
`, ['Test AI Project', testSpaceId]);
testProjectId = projectResult.lastInsertRowid;
// Create test AI Agents table
const tableResult = await dbRun(`
INSERT INTO universal_tables (name, project_id, created_at, updated_at)
VALUES (?, ?, ${sqlNow()}, ${sqlNow()})
`, ['AI Agents', testProjectId]);
testAgentTableId = tableResult.lastInsertRowid;
// Create test agent row
const agentData = {
name: 'Test Agent',
description: 'A test AI agent',
icon: '🤖',
system_prompt: 'You are a test agent',
status: 'active'
};
const rowResult = await dbRun(`
INSERT INTO table_rows (table_id, base_id, data, created_at, updated_at)
VALUES (?, ?, ?, ${sqlNow()}, ${sqlNow()})
`, [testAgentTableId, 'test-agent-1', JSON.stringify(agentData)]);
testAgentRowId = rowResult.lastInsertRowid;
});
afterAll(async () => {
// Cleanup test data (may fail if tables dropped - ignore errors)
try {
await dbRun(`DELETE FROM table_rows WHERE table_id = ?`, [testAgentTableId]);
await dbRun(`DELETE FROM universal_tables WHERE id = ?`, [testAgentTableId]);
await dbRun(`DELETE FROM projects WHERE id = ?`, [testProjectId]);
await dbRun(`DELETE FROM spaces WHERE id = ?`, [testSpaceId]);
await dbRun(`DELETE FROM users WHERE user_type = 'agent' AND email LIKE '%test-agent%'`);
} catch (e) {
// Ignore cleanup errors in test mode
}
});
describe('POST /api/v3/users/create-agent-user', () => {
it('should create a user with user_type=agent linked to agent row', async () => {
const { createAgentUser } = await import('../agent-users.js');
const result = await createAgentUser({
agentTableId: testAgentTableId,
agentRowId: testAgentRowId,
name: 'Test Agent User',
createdBy: 1
});
expect(result.success).toBe(true);
expect(result.user).toBeDefined();
expect(result.user.user_type).toBe('agent');
expect(result.user.managed_by_agent_table_id).toBe(testAgentTableId);
expect(result.user.managed_by_agent_row_id).toBe(testAgentRowId);
expect(result.user.email).toContain('@agents.godcrm.local');
});
it('should auto-generate email from agent name if not provided', async () => {
const { createAgentUser } = await import('../agent-users.js');
const result = await createAgentUser({
agentTableId: testAgentTableId,
agentRowId: testAgentRowId,
createdBy: 1
});
expect(result.user.email).toMatch(/test-agent.*@agents\.godcrm\.local/);
});
it('should store agent_config JSON in user record', async () => {
const { createAgentUser } = await import('../agent-users.js');
const agentConfig = {
auto_respond: true,
respond_only_when_mentioned: false,
max_response_tokens: 2000
};
const result = await createAgentUser({
agentTableId: testAgentTableId,
agentRowId: testAgentRowId,
agentConfig,
createdBy: 1
});
expect(result.user.agent_config).toMatchObject(agentConfig);
});
it('should fail if agent row does not exist', async () => {
const { createAgentUser } = await import('../agent-users.js');
const result = await createAgentUser({
agentTableId: testAgentTableId,
agentRowId: 999999,
createdBy: 1
});
expect(result.success).toBe(false);
expect(result.error).toContain('Agent not found');
});
});
describe('GET /api/v3/users/agents', () => {
it('should return all users with user_type=agent', async () => {
const { getAgentUsers } = await import('../agent-users.js');
const result = await getAgentUsers();
expect(result.success).toBe(true);
expect(Array.isArray(result.users)).toBe(true);
// All returned users should be agents
for (const user of result.users) {
expect(user.user_type).toBe('agent');
}
});
it('should include linked agent data', async () => {
const { getAgentUsers } = await import('../agent-users.js');
const result = await getAgentUsers();
if (result.users.length > 0) {
const agentUser = result.users[0];
expect(agentUser.managed_by_agent_table_id).toBeDefined();
expect(agentUser.managed_by_agent_row_id).toBeDefined();
}
});
});
describe('POST /api/v3/users/:id/generate-api-key', () => {
it('should generate API key for agent user', async () => {
const { createAgentUser, generateUserApiKey } = await import('../agent-users.js');
// First create an agent user
const createResult = await createAgentUser({
agentTableId: testAgentTableId,
agentRowId: testAgentRowId,
createdBy: 1
});
const userId = createResult.user.id;
// Generate API key
const keyResult = await generateUserApiKey(userId, { createdBy: 1 });
expect(keyResult.success).toBe(true);
expect(keyResult.apiKey).toBeDefined();
expect(keyResult.apiKey).toMatch(/^sk-agent-/);
});
it('should fail for non-existent user', async () => {
const { generateUserApiKey } = await import('../agent-users.js');
const result = await generateUserApiKey(999999, { createdBy: 1 });
expect(result.success).toBe(false);
expect(result.error).toContain('User not found');
});
});
});
describe('Agent User Integration', () => {
describe('Agent Authentication', () => {
it('should allow agent user to authenticate via API key', async () => {
// This test verifies that the auth middleware recognizes agent users
// and allows them to make API calls
// Implementation depends on how API key auth is set up
expect(true).toBe(true); // Placeholder for integration test
});
});
});