Files
ccs/tests/unit/cliproxy/remote-token-uploader.test.ts
T
kaitranntt aaa6feb8db fix(tests): remove undefined MockRoute export and unused imports
- Remove MockRoute from index.ts (type was removed in previous commit)
- Remove unused UPLOAD_SUCCESS, UNAUTHORIZED imports from test file
2026-01-23 23:47:09 -05:00

317 lines
11 KiB
TypeScript

/**
* Remote Token Uploader Tests
*
* Tests for uploadTokenToRemote and related functions.
* Uses mock fetch to verify API interactions.
*/
import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { mockFetch, restoreFetch, getCapturedFetchRequests } from '../../mocks';
describe('remote-token-uploader', () => {
let tempDir: string;
let tempTokenFile: string;
beforeEach(() => {
// Create temp directory and token file
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-test-'));
tempTokenFile = path.join(tempDir, 'test-token.json');
fs.writeFileSync(
tempTokenFile,
JSON.stringify({
type: 'gemini',
email: 'test@example.com',
access_token: 'test-access-token',
refresh_token: 'test-refresh-token',
})
);
});
afterEach(() => {
restoreFetch();
// Clean up temp files
try {
fs.unlinkSync(tempTokenFile);
fs.rmdirSync(tempDir);
} catch {
// Ignore cleanup errors
}
});
describe('uploadTokenToRemote', () => {
it('should upload token file successfully', async () => {
mockFetch([
{
url: /\/v0\/management\/auth-files/,
method: 'POST',
response: { status: 'ok', id: 'uploaded-123' },
},
]);
const url = 'http://127.0.0.1:8317/v0/management/auth-files';
const tokenContent = fs.readFileSync(tempTokenFile, 'utf-8');
const fileName = path.basename(tempTokenFile);
const formData = new FormData();
const blob = new Blob([tokenContent], { type: 'application/json' });
formData.append('file', blob, fileName);
const response = await fetch(url, {
method: 'POST',
headers: {
Authorization: 'Bearer test-mgmt-key',
},
body: formData,
});
expect(response.ok).toBe(true);
const result = await response.json();
expect(result).toHaveProperty('status', 'ok');
expect(result).toHaveProperty('id', 'uploaded-123');
// Verify request was captured correctly
const captured = getCapturedFetchRequests();
expect(captured.length).toBe(1);
expect(captured[0].url).toContain('/v0/management/auth-files');
expect(captured[0].method).toBe('POST');
// Headers should be captured (case-preserved from plain object)
expect(captured[0].headers['Authorization']).toBe('Bearer test-mgmt-key');
});
it('should handle upload failure gracefully', async () => {
mockFetch([
{
url: /\/v0\/management\/auth-files/,
method: 'POST',
response: { error: 'Unauthorized' },
status: 401,
},
]);
const url = 'http://127.0.0.1:8317/v0/management/auth-files';
const tokenContent = fs.readFileSync(tempTokenFile, 'utf-8');
const formData = new FormData();
formData.append('file', new Blob([tokenContent], { type: 'application/json' }), 'test.json');
const response = await fetch(url, {
method: 'POST',
body: formData,
});
expect(response.ok).toBe(false);
expect(response.status).toBe(401);
});
it('should handle connection timeout', async () => {
// Test that delay mechanism works (simulates network latency)
mockFetch([
{
url: /\/v0\/management\/auth-files/,
method: 'POST',
delay: 100, // Simulate slow response
response: { status: 'ok' },
},
]);
const url = 'http://127.0.0.1:8317/v0/management/auth-files';
const startTime = Date.now();
const response = await fetch(url, {
method: 'POST',
body: new FormData(),
});
const elapsedTime = Date.now() - startTime;
// Verify delay was applied (at least 100ms)
expect(elapsedTime).toBeGreaterThanOrEqual(100);
expect(response.ok).toBe(true);
});
it('should handle connection refused', async () => {
// Try to connect to a port with nothing listening
const url = 'http://127.0.0.1:59999/v0/management/auth-files';
try {
await fetch(url, {
method: 'POST',
body: new FormData(),
});
// Should not reach here in most cases
} catch (error) {
// Connection refused is expected
expect(error).toBeDefined();
}
});
});
describe('isRemoteUploadEnabled', () => {
it('should return false when not in remote mode', () => {
// Test the logic directly
const target = { isRemote: false, authToken: 'token' };
const result = target.isRemote && Boolean(target.authToken);
expect(result).toBe(false);
});
it('should return false when remote but no auth', () => {
const target = { isRemote: true, authToken: undefined, managementKey: undefined };
const result = target.isRemote && Boolean(target.managementKey ?? target.authToken);
expect(result).toBe(false);
});
it('should return true when remote with authToken', () => {
const target = { isRemote: true, authToken: 'token', managementKey: undefined };
const result = target.isRemote && Boolean(target.managementKey ?? target.authToken);
expect(result).toBe(true);
});
it('should return true when remote with managementKey', () => {
const target = { isRemote: true, authToken: undefined, managementKey: 'mgmt-key' };
const result = target.isRemote && Boolean(target.managementKey ?? target.authToken);
expect(result).toBe(true);
});
it('should prefer managementKey over authToken', () => {
const target = { isRemote: true, authToken: 'auth', managementKey: 'mgmt' };
const key = target.managementKey ?? target.authToken;
expect(key).toBe('mgmt');
});
});
describe('token file validation', () => {
it('should reject invalid JSON', async () => {
// Create invalid token file
const invalidTokenFile = path.join(tempDir, 'invalid.json');
fs.writeFileSync(invalidTokenFile, 'not valid json {{{');
try {
const content = fs.readFileSync(invalidTokenFile, 'utf-8');
JSON.parse(content);
expect(true).toBe(false); // Should not reach
} catch (error) {
expect((error as Error).message).toContain('JSON');
} finally {
fs.unlinkSync(invalidTokenFile);
}
});
it('should handle missing file', () => {
try {
fs.readFileSync('/nonexistent/path/token.json', 'utf-8');
expect(true).toBe(false); // Should not reach
} catch (error) {
expect((error as Error).message).toContain('ENOENT');
}
});
});
describe('multipart/form-data construction', () => {
it('should construct valid FormData with file field', () => {
const tokenContent = JSON.stringify({ type: 'test', token: 'abc' });
const fileName = 'oauth-token.json';
const formData = new FormData();
const blob = new Blob([tokenContent], { type: 'application/json' });
formData.append('file', blob, fileName);
// FormData should have the file
expect(formData.has('file')).toBe(true);
const file = formData.get('file') as File;
expect(file).toBeDefined();
expect(file.name).toBe(fileName);
expect(file.type).toContain('application/json');
});
});
describe('Authorization header', () => {
it('should use Bearer token format', async () => {
mockFetch([{ url: /\/test/, method: 'POST', response: { status: 'ok' } }]);
const authToken = 'my-secret-token-123';
await fetch('http://127.0.0.1:8317/test', {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`,
},
body: new FormData(),
});
const captured = getCapturedFetchRequests();
expect(captured.length).toBe(1);
// Headers case-preserved from plain object
expect(captured[0].headers['Authorization']).toBe(`Bearer ${authToken}`);
});
it('should not send Authorization when no token', async () => {
mockFetch([{ url: /\/test/, method: 'POST', response: { status: 'ok' } }]);
await fetch('http://127.0.0.1:8317/test', {
method: 'POST',
body: new FormData(),
});
const captured = getCapturedFetchRequests();
expect(captured.length).toBe(1);
// No authorization header should be present (check both cases)
expect(captured[0].headers['Authorization']).toBeUndefined();
expect(captured[0].headers['authorization']).toBeUndefined();
});
});
describe('response parsing', () => {
it('should accept status: ok response', async () => {
mockFetch([{ url: /\/test/, method: 'POST', response: { status: 'ok' } }]);
const response = await fetch('http://127.0.0.1:8317/test', { method: 'POST' });
const result = (await response.json()) as { status?: string; success?: boolean; id?: string };
const isSuccess = result.status === 'ok' || result.success || result.id;
expect(isSuccess).toBe(true);
});
it('should accept success: true response', async () => {
mockFetch([{ url: /\/test/, method: 'POST', response: { success: true } }]);
const response = await fetch('http://127.0.0.1:8317/test', { method: 'POST' });
const result = (await response.json()) as { status?: string; success?: boolean; id?: string };
const isSuccess = result.status === 'ok' || result.success || result.id;
expect(isSuccess).toBe(true);
});
it('should accept id in response', async () => {
mockFetch([{ url: /\/test/, method: 'POST', response: { id: 'file-abc123' } }]);
const response = await fetch('http://127.0.0.1:8317/test', { method: 'POST' });
const result = (await response.json()) as { status?: string; success?: boolean; id?: string };
// result.id is truthy when present
const isSuccess = result.status === 'ok' || result.success === true || Boolean(result.id);
expect(isSuccess).toBe(true);
});
it('should detect error response', async () => {
mockFetch([{ url: /\/test/, method: 'POST', response: { error: 'Invalid token format' } }]);
const response = await fetch('http://127.0.0.1:8317/test', { method: 'POST' });
const result = (await response.json()) as {
status?: string;
success?: boolean;
id?: string;
error?: string;
};
// Error response: none of the success indicators are present
const isSuccess = result.status === 'ok' || result.success === true || Boolean(result.id);
expect(isSuccess).toBe(false);
expect(result.error).toBe('Invalid token format');
});
});
});