mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-15 18:21:09 +00:00
209 lines
7.9 KiB
JavaScript
209 lines
7.9 KiB
JavaScript
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const { execSync } = require('child_process');
|
|
const path = require('path');
|
|
const { createTestEnvironment } = require('../shared/fixtures/test-environment');
|
|
|
|
describe('npm CLI', () => {
|
|
const distCcsPath = path.join(__dirname, '..', '..', 'dist', 'ccs.js');
|
|
const srcCcsPath = path.join(__dirname, '..', '..', 'src', 'ccs.ts');
|
|
let testEnv;
|
|
let testCcsHome;
|
|
|
|
function buildCliCommand(args = '') {
|
|
if (fs.existsSync(distCcsPath)) {
|
|
return `node "${distCcsPath}" ${args}`;
|
|
}
|
|
|
|
// Some test files rebuild or clean dist during the same Bun process.
|
|
return `bun "${srcCcsPath}" ${args}`;
|
|
}
|
|
|
|
beforeAll(() => {
|
|
// Create isolated test environment
|
|
testEnv = createTestEnvironment();
|
|
testCcsHome = testEnv.testHome;
|
|
|
|
// Run postinstall to create config in test environment
|
|
const postinstallScript = path.join(__dirname, '..', '..', 'scripts', 'postinstall.js');
|
|
execSync(`node "${postinstallScript}"`, {
|
|
stdio: 'ignore',
|
|
env: { ...process.env, CCS_HOME: testCcsHome }
|
|
});
|
|
});
|
|
|
|
afterAll(() => {
|
|
// Clean up test environment
|
|
if (testEnv) {
|
|
testEnv.cleanup();
|
|
}
|
|
});
|
|
|
|
// Helper to run CLI with test environment
|
|
function runCli(args, options = {}) {
|
|
return execSync(buildCliCommand(args), {
|
|
...options,
|
|
env: { ...process.env, CCS_HOME: testCcsHome }
|
|
});
|
|
}
|
|
|
|
describe('Argument parsing', () => {
|
|
it('handles flag -c without profile error', function() {
|
|
try {
|
|
runCli('-c', { stdio: 'pipe', timeout: 3000 });
|
|
} catch (e) {
|
|
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
|
// Should NOT show "Profile '-c' not found" error
|
|
assert(!output.includes("Profile '-c' not found"), 'Should not treat -c as profile');
|
|
}
|
|
});
|
|
|
|
it('handles flag --verbose without profile error', function() {
|
|
try {
|
|
runCli('--verbose', { stdio: 'pipe', timeout: 3000 });
|
|
} catch (e) {
|
|
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
|
assert(!output.includes("Profile '--verbose' not found"), 'Should not treat --verbose as profile');
|
|
}
|
|
});
|
|
|
|
it('handles flag -p with value', function() {
|
|
try {
|
|
runCli('-p "test prompt"', { stdio: 'pipe', timeout: 8000 });
|
|
} catch (e) {
|
|
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
|
assert(!output.includes("Profile '-p' not found"), 'Should not treat -p as profile');
|
|
}
|
|
});
|
|
|
|
it('handles multiple flags', function() {
|
|
try {
|
|
runCli('-c --verbose', { stdio: 'pipe', timeout: 3000 });
|
|
} catch (e) {
|
|
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
|
assert(!output.includes("Profile '-c' not found"), 'Should not treat flags as profiles');
|
|
assert(!output.includes("Profile '--verbose' not found"), 'Should not treat flags as profiles');
|
|
}
|
|
});
|
|
|
|
it('routes cursor probe through the cursor command handler', function() {
|
|
let output = '';
|
|
try {
|
|
output = execSync(`bun "${srcCcsPath}" cursor probe`, {
|
|
encoding: 'utf8',
|
|
stdio: 'pipe',
|
|
timeout: 3000,
|
|
env: { ...process.env, CCS_HOME: testCcsHome }
|
|
});
|
|
} catch (e) {
|
|
output = e.stderr?.toString() || e.stdout?.toString() || '';
|
|
}
|
|
assert(!output.includes("Profile 'cursor' not found"), 'Should not fall through to profile lookup');
|
|
assert(
|
|
output.includes('Cursor Live Probe') || output.includes('legacy cursor probe'),
|
|
'Should route through the legacy cursor compatibility handler'
|
|
);
|
|
});
|
|
|
|
it('routes gitlab --help to provider shortcut help instead of starting auth', function() {
|
|
const output = execSync(`bun "${srcCcsPath}" gitlab --help`, {
|
|
encoding: 'utf8',
|
|
timeout: 3000,
|
|
env: { ...process.env, CCS_HOME: testCcsHome }
|
|
});
|
|
|
|
assert(output.includes('CCS gitlab Shortcut Help'), 'Should render provider shortcut help');
|
|
assert(output.includes('--gitlab-token-login'), 'Should document canonical GitLab PAT flag');
|
|
assert(output.includes('--token-login'), 'Should document legacy GitLab PAT alias');
|
|
assert(output.includes('--gitlab-url <url>'), 'Should document self-hosted GitLab URL flag');
|
|
assert(!output.includes('Starting GitLab Duo OAuth'), 'Should not start OAuth when help is requested');
|
|
});
|
|
});
|
|
|
|
describe('Profile handling', () => {
|
|
// Note: GLM/Kimi profiles are no longer auto-created (v6.0).
|
|
// Legacy GLMT files may still exist, but new supported API profiles are created
|
|
// via UI presets or CLI: ccs api create --preset glm
|
|
|
|
it('shows helpful error for non-existent profile', function() {
|
|
try {
|
|
runCli('glm --help', { stdio: 'pipe' });
|
|
// If GLM profile exists from previous setup, this is fine too
|
|
} catch (e) {
|
|
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
|
// Either profile exists and works, or shows helpful "not found" message
|
|
// Both are valid behaviors depending on user's setup
|
|
const isValid = !output.includes("Profile 'glm' not found") ||
|
|
output.includes("not found") ||
|
|
output.includes("ccs api create");
|
|
assert(isValid, 'Should either find profile or show helpful message');
|
|
}
|
|
});
|
|
|
|
it('shows error for invalid profile', function() {
|
|
try {
|
|
runCli('invalid-profile-name', { stdio: 'pipe' });
|
|
assert(false, 'Should have thrown an error for invalid profile');
|
|
} catch (e) {
|
|
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
|
assert(output.includes("not found") || output.includes("invalid"), 'Should show profile not found error');
|
|
}
|
|
});
|
|
|
|
it('handles profile with flags correctly', function() {
|
|
try {
|
|
// Use a known command instead of profile that may not exist
|
|
runCli('api --help', { stdio: 'pipe', timeout: 3000 });
|
|
} catch (e) {
|
|
const output = e.stderr?.toString() || '';
|
|
assert(!output.includes("Profile '-c' not found"), 'Should not treat flags as profiles');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Version and help', () => {
|
|
it('shows version with --version flag', function() {
|
|
const output = runCli('--version', { encoding: 'utf8' });
|
|
assert(/\d+\.\d+\.\d+/.test(output), 'Should show version number');
|
|
});
|
|
|
|
it('shows version with -v flag', function() {
|
|
const output = runCli('-v', { encoding: 'utf8' });
|
|
assert(/\d+\.\d+\.\d+/.test(output), 'Should show version number');
|
|
});
|
|
|
|
it('shows help with --help flag', function() {
|
|
const output = runCli('--help', { encoding: 'utf8' });
|
|
assert(/usage|help|options/i.test(output), 'Should show help information');
|
|
});
|
|
|
|
it('shows help with -h flag', function() {
|
|
const output = runCli('-h', { encoding: 'utf8' });
|
|
assert(/usage|help|options/i.test(output), 'Should show help information');
|
|
});
|
|
});
|
|
|
|
describe('Error handling', () => {
|
|
it('handles empty arguments gracefully', function() {
|
|
try {
|
|
runCli('', { stdio: 'pipe', timeout: 3000 });
|
|
} catch (e) {
|
|
// Should either succeed or fail gracefully with a helpful error
|
|
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
|
assert(!output.includes('TypeError') && !output.includes('Cannot read'), 'Should not crash with TypeError');
|
|
}
|
|
});
|
|
|
|
it('handles very long argument', function() {
|
|
const longArg = 'a'.repeat(1000);
|
|
try {
|
|
runCli(`"${longArg}"`, { stdio: 'pipe', timeout: 3000 });
|
|
} catch (e) {
|
|
// Should handle gracefully, not crash
|
|
const output = e.stderr?.toString() || e.stdout?.toString() || '';
|
|
assert(!output.includes('TypeError') && !output.includes('Cannot read'), 'Should not crash with TypeError');
|
|
}
|
|
});
|
|
});
|
|
});
|