Files
ccs/tests/unit/cliproxy/auth-token-manager.test.js
T
kaitranntt ed6776aadc test(cliproxy): add comprehensive auth token test suite
- auth-token-manager.test.js: token generation, masking, inheritance (35 tests)
- tokens-command.test.js: CLI parsing and flag handling (24 tests)
- settings-routes-auth.test.js: API endpoint responses (22 tests)
- Total: 81 new tests covering all auth token functionality
2025-12-25 16:43:39 -05:00

426 lines
15 KiB
JavaScript

/**
* Auth Token Manager Tests
*
* Comprehensive test suite for CLIProxy auth token management including:
* - Token generation (cryptographic security)
* - Token masking
* - Pure function logic tests
*/
const assert = require('assert');
const crypto = require('crypto');
describe('Auth Token Manager', () => {
// =========================================================================
// Token Generation Tests (Pure Functions)
// =========================================================================
describe('generateSecureToken', () => {
let generateSecureToken;
beforeEach(() => {
// Clear cache and reload
delete require.cache[require.resolve('../../../dist/cliproxy/auth-token-manager')];
const authTokenManager = require('../../../dist/cliproxy/auth-token-manager');
generateSecureToken = authTokenManager.generateSecureToken;
});
it('generates token of correct length (base64url encoding)', () => {
const token = generateSecureToken(32);
// 32 bytes = 43 chars in base64url (without padding)
assert.strictEqual(token.length, 43);
});
it('generates unique tokens each call', () => {
const tokens = new Set();
for (let i = 0; i < 100; i++) {
tokens.add(generateSecureToken(32));
}
assert.strictEqual(tokens.size, 100, 'All 100 tokens should be unique');
});
it('uses base64url encoding (no +/= characters)', () => {
// Generate many tokens to ensure we hit all character ranges
for (let i = 0; i < 50; i++) {
const token = generateSecureToken(32);
assert(!token.includes('+'), 'Should not contain +');
assert(!token.includes('/'), 'Should not contain /');
assert(!token.includes('='), 'Should not contain padding =');
}
});
it('accepts custom length parameter', () => {
const short = generateSecureToken(16);
const long = generateSecureToken(64);
// 16 bytes = 22 chars, 64 bytes = 86 chars in base64url
assert.strictEqual(short.length, 22);
assert.strictEqual(long.length, 86);
});
it('handles edge case: length = 0', () => {
const empty = generateSecureToken(0);
assert.strictEqual(empty.length, 0);
});
it('handles edge case: very large length', () => {
const large = generateSecureToken(256);
assert.strictEqual(large.length, 342); // 256 bytes = 342 chars in base64url
});
it('uses cryptographically secure random bytes', () => {
// Verify the function uses crypto.randomBytes internally
// by checking statistical properties of generated tokens
const tokens = [];
for (let i = 0; i < 1000; i++) {
tokens.push(generateSecureToken(32));
}
// All tokens should be unique
const uniqueTokens = new Set(tokens);
assert.strictEqual(uniqueTokens.size, 1000, 'All tokens should be unique');
// Check that first character has good distribution (not always same)
const firstChars = new Set(tokens.map((t) => t[0]));
assert(firstChars.size > 10, 'First character should have good distribution');
});
});
// =========================================================================
// Token Masking Tests (Pure Function Simulation)
// =========================================================================
describe('maskToken (logic validation)', () => {
// Simulates the maskToken function from tokens-command.ts and settings-routes.ts
function maskToken(token) {
if (token.length <= 8) return '****';
return `${token.slice(0, 4)}...${token.slice(-4)}`;
}
it('masks tokens showing first 4 and last 4 characters', () => {
assert.strictEqual(maskToken('1234567890abcdef'), '1234...cdef');
});
it('returns **** for tokens <= 8 characters', () => {
assert.strictEqual(maskToken('12345678'), '****');
assert.strictEqual(maskToken('1234567'), '****');
assert.strictEqual(maskToken('abc'), '****');
assert.strictEqual(maskToken(''), '****');
});
it('handles exactly 9 character tokens', () => {
assert.strictEqual(maskToken('123456789'), '1234...6789');
});
it('handles long tokens (typical API keys)', () => {
const longToken = 'test_key_1234567890abcdefghijklmnopqrstuvwxyz';
assert.strictEqual(maskToken(longToken), 'test...wxyz');
});
it('preserves special characters', () => {
assert.strictEqual(maskToken('!@#$abcd____wxyz'), '!@#$...wxyz');
});
it('handles default CCS internal key', () => {
const internalKey = 'ccs-internal-managed';
assert.strictEqual(maskToken(internalKey), 'ccs-...aged');
});
it('handles default CCS control panel secret', () => {
const secret = 'ccs';
assert.strictEqual(maskToken(secret), '****');
});
});
// =========================================================================
// Inheritance Chain Logic Tests
// =========================================================================
describe('getEffectiveApiKey logic', () => {
/**
* Simulates the inheritance logic from auth-token-manager.ts
*/
function getEffectiveApiKey(config, variantName, defaultKey) {
// Priority 1: Per-variant override
if (variantName) {
const variant = config.cliproxy?.variants?.[variantName];
if (variant?.auth?.api_key) {
return variant.auth.api_key;
}
}
// Priority 2: Global cliproxy.auth
if (config.cliproxy?.auth?.api_key) {
return config.cliproxy.auth.api_key;
}
// Priority 3: Default constant
return defaultKey;
}
const DEFAULT_KEY = 'ccs-internal-managed';
it('returns default when no custom config', () => {
const config = { cliproxy: { variants: {} } };
assert.strictEqual(getEffectiveApiKey(config, undefined, DEFAULT_KEY), DEFAULT_KEY);
});
it('returns global auth when set', () => {
const config = {
cliproxy: {
variants: {},
auth: { api_key: 'global-custom' },
},
};
assert.strictEqual(getEffectiveApiKey(config, undefined, DEFAULT_KEY), 'global-custom');
});
it('returns variant auth when set (highest priority)', () => {
const config = {
cliproxy: {
variants: {
gemini: { auth: { api_key: 'variant-key' } },
},
auth: { api_key: 'global-custom' },
},
};
assert.strictEqual(getEffectiveApiKey(config, 'gemini', DEFAULT_KEY), 'variant-key');
});
it('falls back to global when variant has no auth', () => {
const config = {
cliproxy: {
variants: {
gemini: { type: 'claude' },
},
auth: { api_key: 'global-custom' },
},
};
assert.strictEqual(getEffectiveApiKey(config, 'gemini', DEFAULT_KEY), 'global-custom');
});
it('falls back to default when variant and global missing', () => {
const config = {
cliproxy: {
variants: {
gemini: { type: 'claude' },
},
},
};
assert.strictEqual(getEffectiveApiKey(config, 'gemini', DEFAULT_KEY), DEFAULT_KEY);
});
it('ignores variant name when variant does not exist', () => {
const config = {
cliproxy: {
variants: {},
auth: { api_key: 'global-custom' },
},
};
assert.strictEqual(getEffectiveApiKey(config, 'non-existent', DEFAULT_KEY), 'global-custom');
});
});
describe('getEffectiveManagementSecret logic', () => {
/**
* Simulates the management secret logic from auth-token-manager.ts
*/
function getEffectiveManagementSecret(config, defaultSecret) {
// Priority 1: Global cliproxy.auth
if (config.cliproxy?.auth?.management_secret) {
return config.cliproxy.auth.management_secret;
}
// Priority 2: Default constant
return defaultSecret;
}
const DEFAULT_SECRET = 'ccs';
it('returns default when no custom config', () => {
const config = { cliproxy: { variants: {} } };
assert.strictEqual(getEffectiveManagementSecret(config, DEFAULT_SECRET), DEFAULT_SECRET);
});
it('returns custom secret when set', () => {
const config = {
cliproxy: {
variants: {},
auth: { management_secret: 'custom-secret' },
},
};
assert.strictEqual(getEffectiveManagementSecret(config, DEFAULT_SECRET), 'custom-secret');
});
it('is global only (no per-variant override)', () => {
// Management secret does not support per-variant override
const config = {
cliproxy: {
variants: {
gemini: { auth: { management_secret: 'should-be-ignored' } },
},
auth: { management_secret: 'global-secret' },
},
};
// The function only checks global auth
assert.strictEqual(getEffectiveManagementSecret(config, DEFAULT_SECRET), 'global-secret');
});
});
// =========================================================================
// Auth Summary Logic Tests
// =========================================================================
describe('getAuthSummary logic', () => {
function getAuthSummary(config, defaultApiKey, defaultSecret) {
const customApiKey = config.cliproxy?.auth?.api_key;
const customSecret = config.cliproxy?.auth?.management_secret;
return {
apiKey: {
value: customApiKey || defaultApiKey,
isCustom: !!customApiKey,
},
managementSecret: {
value: customSecret || defaultSecret,
isCustom: !!customSecret,
},
};
}
const DEFAULT_KEY = 'ccs-internal-managed';
const DEFAULT_SECRET = 'ccs';
it('returns defaults with isCustom=false when no custom config', () => {
const config = { cliproxy: { variants: {} } };
const summary = getAuthSummary(config, DEFAULT_KEY, DEFAULT_SECRET);
assert.strictEqual(summary.apiKey.value, DEFAULT_KEY);
assert.strictEqual(summary.apiKey.isCustom, false);
assert.strictEqual(summary.managementSecret.value, DEFAULT_SECRET);
assert.strictEqual(summary.managementSecret.isCustom, false);
});
it('returns custom values with isCustom=true when set', () => {
const config = {
cliproxy: {
variants: {},
auth: { api_key: 'custom-key', management_secret: 'custom-secret' },
},
};
const summary = getAuthSummary(config, DEFAULT_KEY, DEFAULT_SECRET);
assert.strictEqual(summary.apiKey.value, 'custom-key');
assert.strictEqual(summary.apiKey.isCustom, true);
assert.strictEqual(summary.managementSecret.value, 'custom-secret');
assert.strictEqual(summary.managementSecret.isCustom, true);
});
it('handles partial custom config', () => {
const config = {
cliproxy: {
variants: {},
auth: { api_key: 'custom-key' },
},
};
const summary = getAuthSummary(config, DEFAULT_KEY, DEFAULT_SECRET);
assert.strictEqual(summary.apiKey.isCustom, true);
assert.strictEqual(summary.managementSecret.isCustom, false);
});
it('treats empty string as no custom value', () => {
const config = {
cliproxy: {
variants: {},
auth: { api_key: '' },
},
};
const summary = getAuthSummary(config, DEFAULT_KEY, DEFAULT_SECRET);
// Empty string is falsy, so isCustom should be false
assert.strictEqual(summary.apiKey.value, DEFAULT_KEY);
assert.strictEqual(summary.apiKey.isCustom, false);
});
});
// =========================================================================
// Edge Cases
// =========================================================================
describe('Edge Cases', () => {
it('handles undefined config gracefully', () => {
function getEffectiveApiKey(config, variantName, defaultKey) {
if (variantName) {
const variant = config?.cliproxy?.variants?.[variantName];
if (variant?.auth?.api_key) return variant.auth.api_key;
}
if (config?.cliproxy?.auth?.api_key) return config.cliproxy.auth.api_key;
return defaultKey;
}
assert.strictEqual(getEffectiveApiKey(undefined, undefined, 'default'), 'default');
assert.strictEqual(getEffectiveApiKey(null, undefined, 'default'), 'default');
assert.strictEqual(getEffectiveApiKey({}, undefined, 'default'), 'default');
});
it('handles deeply nested missing properties', () => {
function getEffectiveApiKey(config, variantName, defaultKey) {
if (variantName) {
const variant = config?.cliproxy?.variants?.[variantName];
if (variant?.auth?.api_key) return variant.auth.api_key;
}
if (config?.cliproxy?.auth?.api_key) return config.cliproxy.auth.api_key;
return defaultKey;
}
const config = { cliproxy: {} };
assert.strictEqual(getEffectiveApiKey(config, 'test', 'default'), 'default');
});
it('crypto.randomBytes is available and working', () => {
// Ensure crypto module is available
const bytes = crypto.randomBytes(32);
assert.strictEqual(bytes.length, 32);
assert(Buffer.isBuffer(bytes));
});
it('base64url encoding produces valid characters', () => {
const bytes = crypto.randomBytes(32);
const token = bytes.toString('base64url');
// Valid base64url characters: A-Z, a-z, 0-9, -, _
const validChars = /^[A-Za-z0-9_-]+$/;
assert(validChars.test(token), 'Token should only contain base64url characters');
});
});
// =========================================================================
// Constants Validation
// =========================================================================
describe('Default Constants', () => {
let CCS_INTERNAL_API_KEY;
let CCS_CONTROL_PANEL_SECRET;
beforeEach(() => {
delete require.cache[require.resolve('../../../dist/cliproxy/config-generator')];
const configGenerator = require('../../../dist/cliproxy/config-generator');
CCS_INTERNAL_API_KEY = configGenerator.CCS_INTERNAL_API_KEY;
CCS_CONTROL_PANEL_SECRET = configGenerator.CCS_CONTROL_PANEL_SECRET;
});
it('CCS_INTERNAL_API_KEY is defined', () => {
assert(CCS_INTERNAL_API_KEY, 'CCS_INTERNAL_API_KEY should be defined');
assert.strictEqual(typeof CCS_INTERNAL_API_KEY, 'string');
});
it('CCS_CONTROL_PANEL_SECRET is defined', () => {
assert(CCS_CONTROL_PANEL_SECRET, 'CCS_CONTROL_PANEL_SECRET should be defined');
assert.strictEqual(typeof CCS_CONTROL_PANEL_SECRET, 'string');
});
it('default API key has expected value', () => {
assert.strictEqual(CCS_INTERNAL_API_KEY, 'ccs-internal-managed');
});
it('default secret has expected value', () => {
assert.strictEqual(CCS_CONTROL_PANEL_SECRET, 'ccs');
});
});
});