godcrm/backend/__tests__/api-keys/api-keys.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

152 lines
4.9 KiB
JavaScript

/**
* API Keys Integration Tests
* TDD: 🔴 RED → 🟢 GREEN → 🔵 REFACTOR
*/
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import crypto from 'crypto';
// Mock database connection
const TEST_DB_TYPE = process.env.DATABASE_TYPE || 'postgres';
describe('API Keys System', () => {
describe('Key Hash Generation', () => {
it('should generate consistent SHA-256 hash for API key', () => {
const apiKey = 'sk-test1234567890abcdef12345678';
const hash1 = crypto.createHash('sha256').update(apiKey).digest('hex');
const hash2 = crypto.createHash('sha256').update(apiKey).digest('hex');
expect(hash1).toBe(hash2);
expect(hash1.length).toBe(64);
});
it('should extract 7-char prefix from API key', () => {
const apiKey = 'sk-a6814fc13a53dd76a54e52eff53ca403';
const prefix = apiKey.substring(0, 7);
expect(prefix).toBe('sk-a681');
expect(prefix.length).toBe(7);
});
});
describe('API Key Validation', () => {
it('should validate API key format (sk-XXXXXXXX)', () => {
const validKey = 'sk-a6814fc13a53dd76a54e52eff53ca403';
const invalidKeys = [
'invalid',
'pk-12345',
'sk-',
'',
null,
undefined
];
expect(validKey.startsWith('sk-')).toBe(true);
expect(validKey.length).toBe(35); // sk- + 32 hex chars
invalidKeys.forEach(key => {
expect(key?.startsWith?.('sk-') && key?.length === 35).toBeFalsy();
});
});
});
describe('API Key Storage', () => {
it('should store key with prefix and hash, not plain text', async () => {
const apiKey = 'sk-a6814fc13a53dd76a54e52eff53ca403';
const prefix = apiKey.substring(0, 7);
const hash = crypto.createHash('sha256').update(apiKey).digest('hex');
// Simulate storage structure
const storedKey = {
key_prefix: prefix,
key_hash: hash,
// api_key should NOT be stored
};
expect(storedKey.key_prefix).toBe('sk-a681');
expect(storedKey.key_hash).toBe(hash);
expect(storedKey.api_key).toBeUndefined();
});
});
describe('API Key Authentication', () => {
it('should authenticate valid key by matching prefix and hash', async () => {
const apiKey = 'sk-a6814fc13a53dd76a54e52eff53ca403';
const storedPrefix = 'sk-a681';
const storedHash = crypto.createHash('sha256').update(apiKey).digest('hex');
// Simulate authentication
const inputPrefix = apiKey.substring(0, 7);
const inputHash = crypto.createHash('sha256').update(apiKey).digest('hex');
expect(inputPrefix).toBe(storedPrefix);
expect(inputHash).toBe(storedHash);
});
it('should reject key with wrong hash', async () => {
const apiKey = 'sk-a6814fc13a53dd76a54e52eff53ca403';
const wrongKey = 'sk-a681000000000000000000000000000';
const correctHash = crypto.createHash('sha256').update(apiKey).digest('hex');
const wrongHash = crypto.createHash('sha256').update(wrongKey).digest('hex');
expect(correctHash).not.toBe(wrongHash);
});
});
});
describe('API Keys Route', () => {
describe('GET /api/v3/api-keys', () => {
it('should require project_id parameter', async () => {
// This test validates the route requires project_id
const requiresProjectId = true; // Based on route code
expect(requiresProjectId).toBe(true);
});
it('should return empty array if no keys exist', async () => {
const emptyResult = { success: true, data: [] };
expect(emptyResult.success).toBe(true);
expect(emptyResult.data).toEqual([]);
});
});
describe('POST /api/v3/api-keys', () => {
it('should create API key and return full key only once', async () => {
// Simulate key creation response
const response = {
success: true,
data: {
id: 1,
key: 'sk-newkey123456789012345678901234', // Only returned once!
key_prefix: 'sk-newk',
name: 'Test Key',
scopes: ['*'],
is_active: true
}
};
expect(response.success).toBe(true);
expect(response.data.key).toBeDefined();
expect(response.data.key_prefix).toBe(response.data.key.substring(0, 7));
});
it('should store key_hash but NOT the full key', async () => {
const apiKey = 'sk-newkey123456789012345678901234';
// What gets stored in DB
const dbRecord = {
key_prefix: apiKey.substring(0, 7),
key_hash: crypto.createHash('sha256').update(apiKey).digest('hex'),
name: 'Test Key',
scopes: JSON.stringify(['*']),
is_active: 1
};
// Verify no plain key stored
expect(dbRecord.api_key).toBeUndefined();
expect(dbRecord.key).toBeUndefined();
expect(dbRecord.key_hash.length).toBe(64);
});
});
});