@tyvm/knowhow 0.0.35 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agents/tools/aiClient.ts +36 -0
- package/src/agents/tools/lintFile.ts +1 -1
- package/src/agents/tools/list.ts +34 -0
- package/src/agents/tools/ycmd/tools/diagnostics.ts +2 -0
- package/src/ai.ts +5 -4
- package/src/auth/browserLogin.ts +283 -0
- package/src/auth/errors.ts +6 -0
- package/src/auth/spinner.ts +23 -0
- package/src/chat/CliChatService.ts +25 -6
- package/src/chat/modules/AgentModule.ts +1 -2
- package/src/chat/modules/AskModule.ts +1 -2
- package/src/chat/types.ts +14 -4
- package/src/chat-old.ts +446 -0
- package/src/chat.ts +48 -433
- package/src/cli.ts +5 -12
- package/src/embeddings.ts +1 -1
- package/src/index.ts +0 -8
- package/src/login.ts +14 -1
- package/src/microphone.ts +0 -1
- package/src/plugins/downloader/downloader.ts +34 -122
- package/src/services/KnowhowClient.ts +3 -0
- package/src/services/index.ts +1 -2
- package/tests/manual/browser-login/README.md +189 -0
- package/tests/manual/browser-login/test_browser_login_basic.ts +115 -0
- package/tests/manual/browser-login/test_cli_integration.ts +169 -0
- package/tests/manual/browser-login/test_cross_platform_browser.ts +186 -0
- package/tests/manual/browser-login/test_error_scenarios.ts +223 -0
- package/tests/manual/cli/no-env.sh +256 -0
- package/ts_build/src/agents/tools/aiClient.d.ts +2 -0
- package/ts_build/src/agents/tools/aiClient.js +21 -1
- package/ts_build/src/agents/tools/aiClient.js.map +1 -1
- package/ts_build/src/agents/tools/lintFile.js +1 -1
- package/ts_build/src/agents/tools/lintFile.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +32 -0
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/ycmd/tools/diagnostics.js +1 -0
- package/ts_build/src/agents/tools/ycmd/tools/diagnostics.js.map +1 -1
- package/ts_build/src/ai.d.ts +1 -1
- package/ts_build/src/ai.js +2 -1
- package/ts_build/src/ai.js.map +1 -1
- package/ts_build/src/auth/browserLogin.d.ts +11 -0
- package/ts_build/src/auth/browserLogin.js +197 -0
- package/ts_build/src/auth/browserLogin.js.map +1 -0
- package/ts_build/src/auth/errors.d.ts +4 -0
- package/ts_build/src/auth/errors.js +13 -0
- package/ts_build/src/auth/errors.js.map +1 -0
- package/ts_build/src/auth/spinner.d.ts +7 -0
- package/ts_build/src/auth/spinner.js +23 -0
- package/ts_build/src/auth/spinner.js.map +1 -0
- package/ts_build/src/chat/CliChatService.d.ts +4 -3
- package/ts_build/src/chat/CliChatService.js +18 -4
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +1 -1
- package/ts_build/src/chat/modules/AgentModule.js +1 -2
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/AskModule.js +1 -2
- package/ts_build/src/chat/modules/AskModule.js.map +1 -1
- package/ts_build/src/chat/types.d.ts +5 -3
- package/ts_build/src/chat-old.d.ts +13 -0
- package/ts_build/src/chat-old.js +340 -0
- package/ts_build/src/chat-old.js.map +1 -0
- package/ts_build/src/chat.d.ts +3 -13
- package/ts_build/src/chat.js +38 -331
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/chat2.d.ts +1 -1
- package/ts_build/src/chat2.js +2 -2
- package/ts_build/src/chat2.js.map +1 -1
- package/ts_build/src/cli.js +3 -9
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/embeddings.js.map +1 -1
- package/ts_build/src/index.d.ts +0 -2
- package/ts_build/src/index.js +1 -9
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/login.d.ts +1 -1
- package/ts_build/src/login.js +14 -0
- package/ts_build/src/login.js.map +1 -1
- package/ts_build/src/microphone.js.map +1 -1
- package/ts_build/src/plugins/downloader/downloader.d.ts +1 -6
- package/ts_build/src/plugins/downloader/downloader.js +26 -97
- package/ts_build/src/plugins/downloader/downloader.js.map +1 -1
- package/ts_build/src/services/KnowhowClient.js +3 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/index.js +1 -2
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/tests/manual/browser-login/test_browser_login_basic.d.ts +2 -0
- package/ts_build/tests/manual/browser-login/test_browser_login_basic.js +108 -0
- package/ts_build/tests/manual/browser-login/test_browser_login_basic.js.map +1 -0
- package/ts_build/tests/manual/browser-login/test_cli_integration.d.ts +2 -0
- package/ts_build/tests/manual/browser-login/test_cli_integration.js +153 -0
- package/ts_build/tests/manual/browser-login/test_cli_integration.js.map +1 -0
- package/ts_build/tests/manual/browser-login/test_cross_platform_browser.d.ts +2 -0
- package/ts_build/tests/manual/browser-login/test_cross_platform_browser.js +159 -0
- package/ts_build/tests/manual/browser-login/test_cross_platform_browser.js.map +1 -0
- package/ts_build/tests/manual/browser-login/test_error_scenarios.d.ts +2 -0
- package/ts_build/tests/manual/browser-login/test_error_scenarios.js +197 -0
- package/ts_build/tests/manual/browser-login/test_error_scenarios.js.map +1 -0
- package/src/agents/vim/vim.ts +0 -152
- package/src/chat2.ts +0 -62
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Manual Test: CLI Integration Test
|
|
5
|
+
*
|
|
6
|
+
* This test validates the integration of browser login with the CLI command system:
|
|
7
|
+
* 1. Tests `knowhow login` (browser login as default)
|
|
8
|
+
* 2. Tests `knowhow login --jwt` (legacy JWT input)
|
|
9
|
+
* 3. Verifies backwards compatibility
|
|
10
|
+
* 4. Tests login status verification
|
|
11
|
+
*
|
|
12
|
+
* Prerequisites:
|
|
13
|
+
* - Built CLI application
|
|
14
|
+
* - Valid KNOWHOW_API_URL environment variable
|
|
15
|
+
* - Network connection to Knowhow API
|
|
16
|
+
*
|
|
17
|
+
* Usage: npx tsx ./tests/manual/browser-login/test_cli_integration.ts
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { exec } from 'child_process';
|
|
21
|
+
import { promisify } from 'util';
|
|
22
|
+
import * as fs from 'fs';
|
|
23
|
+
import * as path from 'path';
|
|
24
|
+
|
|
25
|
+
const execAsync = promisify(exec);
|
|
26
|
+
|
|
27
|
+
async function testCliIntegration(): Promise<void> {
|
|
28
|
+
console.log('\n=== CLI Integration Test ===\n');
|
|
29
|
+
|
|
30
|
+
const configDir = path.join(process.cwd(), '.knowhow');
|
|
31
|
+
const jwtFile = path.join(configDir, '.jwt');
|
|
32
|
+
let testResults: string[] = [];
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// Clean up any existing JWT file
|
|
36
|
+
if (fs.existsSync(jwtFile)) {
|
|
37
|
+
fs.unlinkSync(jwtFile);
|
|
38
|
+
console.log('🧹 Cleaned up existing JWT file\n');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('=== Test 1: Browser Login as Default ===');
|
|
42
|
+
console.log('Testing: knowhow login (should use browser login)');
|
|
43
|
+
console.log('Note: This will open your browser - please complete authentication\n');
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// This should use browser login by default
|
|
47
|
+
const { stdout, stderr } = await execAsync('npm run build && node dist/cli.js login', {
|
|
48
|
+
timeout: 120000 // 2 minutes timeout
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log('Command output:', stdout);
|
|
52
|
+
if (stderr) console.log('Command stderr:', stderr);
|
|
53
|
+
|
|
54
|
+
// Check if JWT file was created
|
|
55
|
+
if (fs.existsSync(jwtFile)) {
|
|
56
|
+
const jwtContent = fs.readFileSync(jwtFile, 'utf8');
|
|
57
|
+
if (jwtContent.split('.').length === 3) {
|
|
58
|
+
console.log('✅ Test 1 PASSED: Browser login created valid JWT');
|
|
59
|
+
testResults.push('✅ Browser login as default: PASSED');
|
|
60
|
+
} else {
|
|
61
|
+
console.log('❌ Test 1 FAILED: Invalid JWT created');
|
|
62
|
+
testResults.push('❌ Browser login as default: FAILED (invalid JWT)');
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
console.log('❌ Test 1 FAILED: No JWT file created');
|
|
66
|
+
testResults.push('❌ Browser login as default: FAILED (no JWT file)');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (error.killed && error.signal === 'SIGTERM') {
|
|
71
|
+
console.log('⚠️ Test 1 TIMEOUT: User may not have completed authentication');
|
|
72
|
+
testResults.push('⚠️ Browser login as default: TIMEOUT');
|
|
73
|
+
} else {
|
|
74
|
+
console.log('❌ Test 1 ERROR:', error.message);
|
|
75
|
+
testResults.push('❌ Browser login as default: ERROR');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Clean up for next test
|
|
80
|
+
if (fs.existsSync(jwtFile)) {
|
|
81
|
+
fs.unlinkSync(jwtFile);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log('\n=== Test 2: Legacy JWT Input ===');
|
|
85
|
+
console.log('Testing: knowhow login --jwt (should prompt for manual JWT input)');
|
|
86
|
+
|
|
87
|
+
// Create a mock interaction that immediately exits
|
|
88
|
+
try {
|
|
89
|
+
const { stdout, stderr } = await execAsync('echo "" | npm run build && echo "" | node dist/cli.js login --jwt', {
|
|
90
|
+
timeout: 10000
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
console.log('Command output:', stdout);
|
|
94
|
+
if (stderr) console.log('Command stderr:', stderr);
|
|
95
|
+
|
|
96
|
+
// The command should have prompted for JWT input
|
|
97
|
+
if (stdout.includes('JWT') || stdout.includes('token') || stderr.includes('JWT') || stderr.includes('token')) {
|
|
98
|
+
console.log('✅ Test 2 PASSED: --jwt flag triggers manual JWT input');
|
|
99
|
+
testResults.push('✅ Legacy JWT input: PASSED');
|
|
100
|
+
} else {
|
|
101
|
+
console.log('❌ Test 2 FAILED: --jwt flag not working correctly');
|
|
102
|
+
testResults.push('❌ Legacy JWT input: FAILED');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// This is expected since we're not providing valid JWT input
|
|
107
|
+
if (error.message.includes('JWT') || error.message.includes('token')) {
|
|
108
|
+
console.log('✅ Test 2 PASSED: --jwt flag triggered JWT input prompt');
|
|
109
|
+
testResults.push('✅ Legacy JWT input: PASSED');
|
|
110
|
+
} else {
|
|
111
|
+
console.log('❌ Test 2 FAILED:', error.message);
|
|
112
|
+
testResults.push('❌ Legacy JWT input: FAILED');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log('\n=== Test 3: Command Help ===');
|
|
117
|
+
console.log('Testing: knowhow login --help');
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const { stdout, stderr } = await execAsync('npm run build && node dist/cli.js login --help');
|
|
121
|
+
|
|
122
|
+
console.log('Help output:', stdout);
|
|
123
|
+
|
|
124
|
+
if (stdout.includes('--jwt') && stdout.includes('browser')) {
|
|
125
|
+
console.log('✅ Test 3 PASSED: Help shows both browser and JWT options');
|
|
126
|
+
testResults.push('✅ Command help: PASSED');
|
|
127
|
+
} else {
|
|
128
|
+
console.log('❌ Test 3 FAILED: Help missing expected options');
|
|
129
|
+
testResults.push('❌ Command help: FAILED');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.log('❌ Test 3 ERROR:', error.message);
|
|
134
|
+
testResults.push('❌ Command help: ERROR');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('❌ Test suite failed:', error.message);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Print summary
|
|
142
|
+
console.log('\n=== Test Summary ===');
|
|
143
|
+
testResults.forEach(result => console.log(result));
|
|
144
|
+
|
|
145
|
+
const passedTests = testResults.filter(r => r.includes('PASSED')).length;
|
|
146
|
+
const totalTests = testResults.length;
|
|
147
|
+
|
|
148
|
+
console.log(`\nResults: ${passedTests}/${totalTests} tests passed`);
|
|
149
|
+
|
|
150
|
+
if (passedTests === totalTests) {
|
|
151
|
+
console.log('🎉 All CLI integration tests PASSED!');
|
|
152
|
+
process.exit(0);
|
|
153
|
+
} else {
|
|
154
|
+
console.log('❌ Some CLI integration tests FAILED');
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Handle graceful shutdown
|
|
160
|
+
process.on('SIGINT', () => {
|
|
161
|
+
console.log('\n\n🛑 Test interrupted by user (Ctrl+C)');
|
|
162
|
+
process.exit(0);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Run the test
|
|
166
|
+
testCliIntegration().catch((error) => {
|
|
167
|
+
console.error('Unhandled error:', error);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Manual Test: Cross-Platform Browser Opening
|
|
5
|
+
*
|
|
6
|
+
* This test validates that the browser opening functionality works across different platforms:
|
|
7
|
+
* 1. Detects the current platform
|
|
8
|
+
* 2. Tests browser opening with a test URL
|
|
9
|
+
* 3. Validates the correct command is used for each platform
|
|
10
|
+
* 4. Tests fallback behavior when browser opening fails
|
|
11
|
+
*
|
|
12
|
+
* Prerequisites:
|
|
13
|
+
* - Default browser installed on the system
|
|
14
|
+
* - Network connection (for test URL)
|
|
15
|
+
*
|
|
16
|
+
* Usage: npx tsx ./tests/manual/browser-login/test_cross_platform_browser.ts
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { openBrowser } from '../../../src/auth/browserLogin';
|
|
20
|
+
import * as os from 'os';
|
|
21
|
+
import { exec } from 'child_process';
|
|
22
|
+
import { promisify } from 'util';
|
|
23
|
+
|
|
24
|
+
const execAsync = promisify(exec);
|
|
25
|
+
|
|
26
|
+
async function testCrossPlatformBrowser(): Promise<void> {
|
|
27
|
+
console.log('\n=== Cross-Platform Browser Opening Test ===\n');
|
|
28
|
+
|
|
29
|
+
const platform = os.platform();
|
|
30
|
+
console.log(`Detected platform: ${platform}`);
|
|
31
|
+
|
|
32
|
+
let testResults: string[] = [];
|
|
33
|
+
|
|
34
|
+
// Test 1: Platform Detection
|
|
35
|
+
console.log('\n=== Test 1: Platform Detection ===');
|
|
36
|
+
|
|
37
|
+
let expectedCommand: string;
|
|
38
|
+
switch (platform) {
|
|
39
|
+
case 'darwin':
|
|
40
|
+
expectedCommand = 'open';
|
|
41
|
+
console.log('✅ Platform: macOS - should use "open" command');
|
|
42
|
+
break;
|
|
43
|
+
case 'win32':
|
|
44
|
+
expectedCommand = 'start';
|
|
45
|
+
console.log('✅ Platform: Windows - should use "start" command');
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
expectedCommand = 'xdg-open';
|
|
49
|
+
console.log('✅ Platform: Linux/Unix - should use "xdg-open" command');
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
testResults.push('✅ Platform detection: PASSED');
|
|
53
|
+
|
|
54
|
+
// Test 2: Command Availability
|
|
55
|
+
console.log('\n=== Test 2: Browser Command Availability ===');
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
// Test if the expected command exists
|
|
59
|
+
await execAsync(`which ${expectedCommand} || where ${expectedCommand}`);
|
|
60
|
+
console.log(`✅ Browser command "${expectedCommand}" is available`);
|
|
61
|
+
testResults.push('✅ Browser command availability: PASSED');
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.log(`⚠️ Browser command "${expectedCommand}" may not be available`);
|
|
64
|
+
console.log(' This could cause browser opening to fail gracefully');
|
|
65
|
+
testResults.push('⚠️ Browser command availability: WARNING');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Test 3: Browser Opening with Test URL
|
|
69
|
+
console.log('\n=== Test 3: Browser Opening Test ===');
|
|
70
|
+
console.log('Testing browser opening with a test URL...');
|
|
71
|
+
console.log('Note: This should open https://example.com in your default browser');
|
|
72
|
+
|
|
73
|
+
const testUrl = 'https://example.com';
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
await openBrowser(testUrl);
|
|
77
|
+
console.log('✅ Browser opening completed without errors');
|
|
78
|
+
|
|
79
|
+
// Give user time to confirm
|
|
80
|
+
console.log('\nDid your browser open to https://example.com? (This test requires manual verification)');
|
|
81
|
+
console.log('The test will continue in 10 seconds...');
|
|
82
|
+
|
|
83
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
84
|
+
|
|
85
|
+
testResults.push('✅ Browser opening: PASSED (manual verification required)');
|
|
86
|
+
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.log(`⚠️ Browser opening failed gracefully: ${error.message}`);
|
|
89
|
+
console.log(' This is expected behavior - the application should continue working');
|
|
90
|
+
testResults.push('✅ Browser opening fallback: PASSED');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Test 4: Invalid URL Handling
|
|
94
|
+
console.log('\n=== Test 4: Invalid URL Handling ===');
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await openBrowser('not-a-valid-url');
|
|
98
|
+
console.log('✅ Invalid URL handled gracefully');
|
|
99
|
+
testResults.push('✅ Invalid URL handling: PASSED');
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.log(`⚠️ Invalid URL caused error: ${error.message}`);
|
|
102
|
+
console.log(' This is acceptable as long as it doesn\'t crash the application');
|
|
103
|
+
testResults.push('✅ Invalid URL handling: PASSED (graceful failure)');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Test 5: URL with Special Characters
|
|
107
|
+
console.log('\n=== Test 5: Special Characters in URL ===');
|
|
108
|
+
|
|
109
|
+
const specialUrl = 'https://example.com/path?param=value&other=test#fragment';
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
await openBrowser(specialUrl);
|
|
113
|
+
console.log('✅ URL with special characters handled correctly');
|
|
114
|
+
testResults.push('✅ Special characters handling: PASSED');
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.log(`⚠️ Special characters in URL caused issues: ${error.message}`);
|
|
117
|
+
testResults.push('⚠️ Special characters handling: WARNING');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Platform-specific tests
|
|
121
|
+
console.log('\n=== Test 6: Platform-Specific Command Testing ===');
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const testCommand = getTestCommand(platform);
|
|
125
|
+
if (testCommand) {
|
|
126
|
+
console.log(`Testing platform-specific command: ${testCommand}`);
|
|
127
|
+
await execAsync(testCommand);
|
|
128
|
+
console.log('✅ Platform-specific command executed successfully');
|
|
129
|
+
testResults.push('✅ Platform-specific command: PASSED');
|
|
130
|
+
} else {
|
|
131
|
+
console.log('⚠️ No platform-specific test available');
|
|
132
|
+
testResults.push('⚠️ Platform-specific command: SKIPPED');
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.log(`❌ Platform-specific command failed: ${error.message}`);
|
|
136
|
+
testResults.push('❌ Platform-specific command: FAILED');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Print summary
|
|
140
|
+
console.log('\n=== Test Summary ===');
|
|
141
|
+
testResults.forEach(result => console.log(result));
|
|
142
|
+
|
|
143
|
+
const passedTests = testResults.filter(r => r.includes('PASSED')).length;
|
|
144
|
+
const warningTests = testResults.filter(r => r.includes('WARNING')).length;
|
|
145
|
+
const failedTests = testResults.filter(r => r.includes('FAILED')).length;
|
|
146
|
+
const totalTests = testResults.length;
|
|
147
|
+
|
|
148
|
+
console.log(`\nResults: ${passedTests}/${totalTests} passed, ${warningTests} warnings, ${failedTests} failed`);
|
|
149
|
+
|
|
150
|
+
if (failedTests === 0) {
|
|
151
|
+
console.log('🎉 Cross-platform browser tests completed successfully!');
|
|
152
|
+
if (warningTests > 0) {
|
|
153
|
+
console.log(' Some warnings detected - check platform-specific behavior');
|
|
154
|
+
}
|
|
155
|
+
process.exit(0);
|
|
156
|
+
} else {
|
|
157
|
+
console.log('❌ Some cross-platform browser tests failed');
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getTestCommand(platform: string): string | null {
|
|
163
|
+
switch (platform) {
|
|
164
|
+
case 'darwin':
|
|
165
|
+
// Test opening a simple file/app that should exist
|
|
166
|
+
return 'open -a "System Preferences" || echo "Could not open System Preferences"';
|
|
167
|
+
case 'win32':
|
|
168
|
+
// Test opening notepad which should be available on all Windows systems
|
|
169
|
+
return 'start notepad && timeout 2 && taskkill /f /im notepad.exe 2>nul || echo "Notepad test completed"';
|
|
170
|
+
default:
|
|
171
|
+
// Test xdg-open with a simple command
|
|
172
|
+
return 'xdg-open --version || echo "xdg-open version check completed"';
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Handle graceful shutdown
|
|
177
|
+
process.on('SIGINT', () => {
|
|
178
|
+
console.log('\n\n🛑 Test interrupted by user (Ctrl+C)');
|
|
179
|
+
process.exit(0);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Run the test
|
|
183
|
+
testCrossPlatformBrowser().catch((error) => {
|
|
184
|
+
console.error('Unhandled error:', error);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Manual Test: Error Scenarios and Edge Cases
|
|
5
|
+
*
|
|
6
|
+
* This test validates error handling and edge cases in the browser login flow:
|
|
7
|
+
* 1. Network connectivity issues
|
|
8
|
+
* 2. Invalid API responses
|
|
9
|
+
* 3. Timeout scenarios
|
|
10
|
+
* 4. User cancellation
|
|
11
|
+
* 5. Session expiration
|
|
12
|
+
* 6. Invalid JWT handling
|
|
13
|
+
*
|
|
14
|
+
* Prerequisites:
|
|
15
|
+
* - Network connection (some tests will simulate disconnection)
|
|
16
|
+
* - Valid KNOWHOW_API_URL environment variable
|
|
17
|
+
*
|
|
18
|
+
* Usage: npx tsx ./tests/manual/browser-login/test_error_scenarios.ts
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { BrowserLoginService, validateJwt } from '../../../src/auth/browserLogin';
|
|
22
|
+
import { BrowserLoginError } from '../../../src/auth/errors';
|
|
23
|
+
import * as fs from 'fs';
|
|
24
|
+
import * as path from 'path';
|
|
25
|
+
|
|
26
|
+
async function testErrorScenarios(): Promise<void> {
|
|
27
|
+
console.log('\n=== Error Scenarios and Edge Cases Test ===\n');
|
|
28
|
+
|
|
29
|
+
let testResults: string[] = [];
|
|
30
|
+
const configDir = path.join(process.cwd(), '.knowhow');
|
|
31
|
+
const jwtFile = path.join(configDir, '.jwt');
|
|
32
|
+
|
|
33
|
+
// Test 1: Invalid API URL
|
|
34
|
+
console.log('=== Test 1: Invalid API URL ===');
|
|
35
|
+
try {
|
|
36
|
+
const browserLogin = new BrowserLoginService('https://invalid-api-url-that-does-not-exist.com');
|
|
37
|
+
await browserLogin.login();
|
|
38
|
+
console.log('❌ Test 1 FAILED: Should have thrown error for invalid API URL');
|
|
39
|
+
testResults.push('❌ Invalid API URL: FAILED');
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (error instanceof BrowserLoginError && error.code === 'NETWORK_ERROR') {
|
|
42
|
+
console.log('✅ Test 1 PASSED: Invalid API URL properly handled');
|
|
43
|
+
testResults.push('✅ Invalid API URL: PASSED');
|
|
44
|
+
} else if (error.message.includes('network') || error.message.includes('ENOTFOUND') || error.message.includes('connect')) {
|
|
45
|
+
console.log('✅ Test 1 PASSED: Network error properly handled');
|
|
46
|
+
testResults.push('✅ Invalid API URL: PASSED');
|
|
47
|
+
} else {
|
|
48
|
+
console.log(`⚠️ Test 1 WARNING: Unexpected error type: ${error.message}`);
|
|
49
|
+
testResults.push('⚠️ Invalid API URL: WARNING');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Test 2: Missing API URL
|
|
54
|
+
console.log('\n=== Test 2: Missing API URL ===');
|
|
55
|
+
try {
|
|
56
|
+
const originalUrl = process.env.KNOWHOW_API_URL;
|
|
57
|
+
delete process.env.KNOWHOW_API_URL;
|
|
58
|
+
|
|
59
|
+
const browserLogin = new BrowserLoginService('');
|
|
60
|
+
await browserLogin.login();
|
|
61
|
+
|
|
62
|
+
// Restore environment variable
|
|
63
|
+
process.env.KNOWHOW_API_URL = originalUrl;
|
|
64
|
+
|
|
65
|
+
console.log('❌ Test 2 FAILED: Should have thrown error for missing API URL');
|
|
66
|
+
testResults.push('❌ Missing API URL: FAILED');
|
|
67
|
+
} catch (error) {
|
|
68
|
+
process.env.KNOWHOW_API_URL = process.env.KNOWHOW_API_URL || 'https://app.knowhow.run';
|
|
69
|
+
|
|
70
|
+
if (error.message.includes('not set') || error.message.includes('API_URL')) {
|
|
71
|
+
console.log('✅ Test 2 PASSED: Missing API URL properly handled');
|
|
72
|
+
testResults.push('✅ Missing API URL: PASSED');
|
|
73
|
+
} else {
|
|
74
|
+
console.log(`⚠️ Test 2 WARNING: Unexpected error: ${error.message}`);
|
|
75
|
+
testResults.push('⚠️ Missing API URL: WARNING');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Test 3: JWT Validation
|
|
80
|
+
console.log('\n=== Test 3: JWT Validation ===');
|
|
81
|
+
|
|
82
|
+
const jwtTests = [
|
|
83
|
+
{ jwt: '', expected: false, name: 'empty string' },
|
|
84
|
+
{ jwt: 'invalid', expected: false, name: 'single part' },
|
|
85
|
+
{ jwt: 'part1.part2', expected: false, name: 'two parts' },
|
|
86
|
+
{ jwt: 'part1.part2.part3', expected: true, name: 'three parts' },
|
|
87
|
+
{ jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.eoaDVGTClRdfxUZXiPs3f8FmJDkDE_VCQBNn120LSg', expected: false, name: 'incomplete JWT' },
|
|
88
|
+
{ jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.eoaDVGTClRdfxUZXiPs3f8FmJDkDE_VCQBNn120LSug', expected: true, name: 'valid JWT format' },
|
|
89
|
+
{ jwt: null as any, expected: false, name: 'null value' },
|
|
90
|
+
{ jwt: undefined as any, expected: false, name: 'undefined value' },
|
|
91
|
+
{ jwt: 123 as any, expected: false, name: 'number value' },
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
let jwtValidationPassed = 0;
|
|
95
|
+
for (const test of jwtTests) {
|
|
96
|
+
const result = validateJwt(test.jwt);
|
|
97
|
+
if (result === test.expected) {
|
|
98
|
+
console.log(` ✅ JWT validation (${test.name}): PASSED`);
|
|
99
|
+
jwtValidationPassed++;
|
|
100
|
+
} else {
|
|
101
|
+
console.log(` ❌ JWT validation (${test.name}): FAILED - expected ${test.expected}, got ${result}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (jwtValidationPassed === jwtTests.length) {
|
|
106
|
+
console.log('✅ Test 3 PASSED: All JWT validation tests passed');
|
|
107
|
+
testResults.push('✅ JWT validation: PASSED');
|
|
108
|
+
} else {
|
|
109
|
+
console.log(`❌ Test 3 FAILED: ${jwtValidationPassed}/${jwtTests.length} JWT validation tests passed`);
|
|
110
|
+
testResults.push('❌ JWT validation: FAILED');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Test 4: File Permission Handling
|
|
114
|
+
console.log('\n=== Test 4: File Permission Handling ===');
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
// Clean up any existing JWT file
|
|
118
|
+
if (fs.existsSync(jwtFile)) {
|
|
119
|
+
fs.unlinkSync(jwtFile);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Create directory if it doesn't exist
|
|
123
|
+
if (!fs.existsSync(configDir)) {
|
|
124
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Test writing to a read-only directory (simulate permission issue)
|
|
128
|
+
const testJwt = 'test.jwt.token';
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
fs.writeFileSync(jwtFile, testJwt, { mode: 0o600 });
|
|
132
|
+
const stats = fs.statSync(jwtFile);
|
|
133
|
+
const permissions = stats.mode & parseInt('777', 8);
|
|
134
|
+
|
|
135
|
+
if (permissions === parseInt('600', 8)) {
|
|
136
|
+
console.log('✅ Test 4 PASSED: JWT file permissions set correctly');
|
|
137
|
+
testResults.push('✅ File permissions: PASSED');
|
|
138
|
+
} else {
|
|
139
|
+
console.log(`⚠️ Test 4 WARNING: File permissions are ${permissions.toString(8)}, expected 600`);
|
|
140
|
+
testResults.push('⚠️ File permissions: WARNING');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Clean up test file
|
|
144
|
+
fs.unlinkSync(jwtFile);
|
|
145
|
+
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.log(`❌ Test 4 FAILED: Could not create JWT file: ${error.message}`);
|
|
148
|
+
testResults.push('❌ File permissions: FAILED');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.log(`❌ Test 4 FAILED: File permission test error: ${error.message}`);
|
|
153
|
+
testResults.push('❌ File permissions: FAILED');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Test 5: Error Code Handling
|
|
157
|
+
console.log('\n=== Test 5: Error Code Handling ===');
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const error1 = new BrowserLoginError('Test error', 'USER_CANCELLED');
|
|
161
|
+
const error2 = new BrowserLoginError('Test error without code');
|
|
162
|
+
|
|
163
|
+
if (error1.code === 'USER_CANCELLED' && !error2.code) {
|
|
164
|
+
console.log('✅ Test 5 PASSED: Error codes handled correctly');
|
|
165
|
+
testResults.push('✅ Error codes: PASSED');
|
|
166
|
+
} else {
|
|
167
|
+
console.log('❌ Test 5 FAILED: Error codes not working correctly');
|
|
168
|
+
testResults.push('❌ Error codes: FAILED');
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.log(`❌ Test 5 FAILED: Error code test failed: ${error.message}`);
|
|
172
|
+
testResults.push('❌ Error codes: FAILED');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Test 6: Graceful Cancellation Simulation
|
|
176
|
+
console.log('\n=== Test 6: Graceful Cancellation Simulation ===');
|
|
177
|
+
console.log('This test simulates user cancellation (Ctrl+C) behavior...');
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
// This test would be interactive in a real scenario
|
|
181
|
+
console.log('✅ Test 6 PASSED: Cancellation mechanisms are in place');
|
|
182
|
+
console.log(' (Full cancellation test requires manual Ctrl+C during login)');
|
|
183
|
+
testResults.push('✅ Cancellation simulation: PASSED');
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.log(`❌ Test 6 FAILED: ${error.message}`);
|
|
186
|
+
testResults.push('❌ Cancellation simulation: FAILED');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Print summary
|
|
190
|
+
console.log('\n=== Test Summary ===');
|
|
191
|
+
testResults.forEach(result => console.log(result));
|
|
192
|
+
|
|
193
|
+
const passedTests = testResults.filter(r => r.includes('PASSED')).length;
|
|
194
|
+
const warningTests = testResults.filter(r => r.includes('WARNING')).length;
|
|
195
|
+
const failedTests = testResults.filter(r => r.includes('FAILED')).length;
|
|
196
|
+
const totalTests = testResults.length;
|
|
197
|
+
|
|
198
|
+
console.log(`\nResults: ${passedTests}/${totalTests} passed, ${warningTests} warnings, ${failedTests} failed`);
|
|
199
|
+
|
|
200
|
+
if (failedTests === 0) {
|
|
201
|
+
console.log('🎉 Error scenario tests completed successfully!');
|
|
202
|
+
if (warningTests > 0) {
|
|
203
|
+
console.log(' Some warnings detected - review edge case handling');
|
|
204
|
+
}
|
|
205
|
+
process.exit(0);
|
|
206
|
+
} else {
|
|
207
|
+
console.log('❌ Some error scenario tests failed');
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Handle graceful shutdown
|
|
213
|
+
process.on('SIGINT', () => {
|
|
214
|
+
console.log('\n\n🛑 Test interrupted by user (Ctrl+C)');
|
|
215
|
+
console.log(' This demonstrates the graceful cancellation feature');
|
|
216
|
+
process.exit(0);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Run the test
|
|
220
|
+
testErrorScenarios().catch((error) => {
|
|
221
|
+
console.error('Unhandled error:', error);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
});
|