Skip to content

Commit f4bc99b

Browse files
Copilot0xrinegade
andcommitted
Complete comprehensive code quality fixes with full test coverage
Co-authored-by: 0xrinegade <[email protected]>
1 parent 6d98df1 commit f4bc99b

File tree

9 files changed

+1809
-158
lines changed

9 files changed

+1809
-158
lines changed

__tests__/cli/commands.test.ts

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
/**
2+
* Tests for CLI functionality
3+
*/
4+
5+
import { execSync } from 'child_process';
6+
import { promises as fs } from 'fs';
7+
import path from 'path';
8+
import os from 'os';
9+
10+
describe('CLI Commands', () => {
11+
let tempConfigDir: string;
12+
let originalHome: string | undefined;
13+
14+
beforeEach(async () => {
15+
// Create temporary directory for test config
16+
tempConfigDir = await fs.mkdtemp(path.join(os.tmpdir(), 'svm-pay-test-'));
17+
originalHome = process.env.HOME;
18+
process.env.HOME = tempConfigDir;
19+
20+
// Mock console methods to reduce test noise
21+
jest.spyOn(console, 'log').mockImplementation();
22+
jest.spyOn(console, 'error').mockImplementation();
23+
jest.spyOn(console, 'warn').mockImplementation();
24+
});
25+
26+
afterEach(async () => {
27+
// Restore environment
28+
if (originalHome) {
29+
process.env.HOME = originalHome;
30+
}
31+
32+
// Clean up temp directory
33+
try {
34+
await fs.rmdir(tempConfigDir, { recursive: true });
35+
} catch (error) {
36+
// Ignore cleanup errors
37+
}
38+
39+
jest.restoreAllMocks();
40+
});
41+
42+
describe('setup command', () => {
43+
it('should show help when no arguments provided', () => {
44+
const result = execSync('node dist/bin/svm-pay.js setup --help', {
45+
encoding: 'utf8',
46+
cwd: process.cwd()
47+
});
48+
49+
expect(result).toContain('Set up your payment configuration');
50+
expect(result).toContain('-k, --private-key');
51+
expect(result).toContain('-a, --api-key');
52+
});
53+
54+
it('should validate private key format', () => {
55+
try {
56+
execSync('node dist/bin/svm-pay.js setup -k invalid-key', {
57+
encoding: 'utf8',
58+
cwd: process.cwd(),
59+
env: { ...process.env, HOME: tempConfigDir }
60+
});
61+
fail('Should have thrown an error for invalid private key');
62+
} catch (error: any) {
63+
expect(error.status).toBe(1);
64+
}
65+
});
66+
67+
// Note: Testing with real private keys in unit tests is not recommended
68+
// In a real test environment, you'd use test keys or mock the validation
69+
});
70+
71+
describe('balance command', () => {
72+
it('should show error when not configured', () => {
73+
try {
74+
execSync('node dist/bin/svm-pay.js balance', {
75+
encoding: 'utf8',
76+
cwd: process.cwd(),
77+
env: { ...process.env, HOME: tempConfigDir }
78+
});
79+
fail('Should have thrown an error for missing configuration');
80+
} catch (error: any) {
81+
expect(error.status).toBe(1);
82+
}
83+
});
84+
85+
it('should show help for balance command', () => {
86+
const result = execSync('node dist/bin/svm-pay.js balance --help', {
87+
encoding: 'utf8',
88+
cwd: process.cwd()
89+
});
90+
91+
expect(result).toContain('Check your current Solana wallet balance');
92+
});
93+
});
94+
95+
describe('pay command', () => {
96+
it('should show help for pay command', () => {
97+
const result = execSync('node dist/bin/svm-pay.js pay --help', {
98+
encoding: 'utf8',
99+
cwd: process.cwd()
100+
});
101+
102+
expect(result).toContain('Process a payment');
103+
expect(result).toContain('-a, --amount');
104+
expect(result).toContain('-t, --to');
105+
expect(result).toContain('-f, --force');
106+
});
107+
108+
it('should show dangerous warning for force flag', () => {
109+
const result = execSync('node dist/bin/svm-pay.js pay --help', {
110+
encoding: 'utf8',
111+
cwd: process.cwd()
112+
});
113+
114+
expect(result).toContain('⚠️ DANGEROUS');
115+
expect(result).toContain('extreme caution');
116+
});
117+
118+
it('should require configuration before payment', () => {
119+
try {
120+
execSync('node dist/bin/svm-pay.js pay -a 1.0', {
121+
encoding: 'utf8',
122+
cwd: process.cwd(),
123+
env: { ...process.env, HOME: tempConfigDir }
124+
});
125+
fail('Should have thrown an error for missing configuration');
126+
} catch (error: any) {
127+
expect(error.status).toBe(1);
128+
}
129+
});
130+
});
131+
132+
describe('history command', () => {
133+
it('should show help for history command', () => {
134+
const result = execSync('node dist/bin/svm-pay.js history --help', {
135+
encoding: 'utf8',
136+
cwd: process.cwd()
137+
});
138+
139+
expect(result).toContain('View your payment history');
140+
expect(result).toContain('--limit');
141+
expect(result).toContain('--all');
142+
});
143+
144+
it('should handle empty history gracefully', () => {
145+
try {
146+
const result = execSync('node dist/bin/svm-pay.js history', {
147+
encoding: 'utf8',
148+
cwd: process.cwd(),
149+
env: { ...process.env, HOME: tempConfigDir }
150+
});
151+
152+
// Should not throw error, just show empty results
153+
expect(result).toContain('No payment history found');
154+
} catch (error: any) {
155+
// If it fails, it should be due to missing config, not empty history
156+
expect(error.status).toBe(1);
157+
}
158+
});
159+
});
160+
161+
describe('usage command', () => {
162+
it('should show help for usage command', () => {
163+
const result = execSync('node dist/bin/svm-pay.js usage --help', {
164+
encoding: 'utf8',
165+
cwd: process.cwd()
166+
});
167+
168+
expect(result).toContain('Check your OpenRouter API usage');
169+
});
170+
171+
it('should require configuration for API usage check', () => {
172+
try {
173+
execSync('node dist/bin/svm-pay.js usage', {
174+
encoding: 'utf8',
175+
cwd: process.cwd(),
176+
env: { ...process.env, HOME: tempConfigDir }
177+
});
178+
fail('Should have thrown an error for missing configuration');
179+
} catch (error: any) {
180+
expect(error.status).toBe(1);
181+
}
182+
});
183+
});
184+
185+
describe('main CLI', () => {
186+
it('should show help when no command provided', () => {
187+
const result = execSync('node dist/bin/svm-pay.js --help', {
188+
encoding: 'utf8',
189+
cwd: process.cwd()
190+
});
191+
192+
expect(result).toContain('CLI tool for managing Solana-based payments');
193+
expect(result).toContain('setup');
194+
expect(result).toContain('balance');
195+
expect(result).toContain('pay');
196+
expect(result).toContain('history');
197+
expect(result).toContain('usage');
198+
});
199+
200+
it('should show version information', () => {
201+
const result = execSync('node dist/bin/svm-pay.js --version', {
202+
encoding: 'utf8',
203+
cwd: process.cwd()
204+
});
205+
206+
expect(result.trim()).toMatch(/^\d+\.\d+\.\d+$/); // Semantic version format
207+
});
208+
209+
it('should handle invalid commands gracefully', () => {
210+
try {
211+
execSync('node dist/bin/svm-pay.js invalid-command', {
212+
encoding: 'utf8',
213+
cwd: process.cwd()
214+
});
215+
fail('Should have thrown an error for invalid command');
216+
} catch (error: any) {
217+
expect(error.status).toBe(1);
218+
}
219+
});
220+
});
221+
222+
describe('command validation', () => {
223+
it('should validate Solana addresses in pay command', () => {
224+
// Test with obviously invalid address
225+
try {
226+
execSync('node dist/bin/svm-pay.js pay -a 1.0 -t invalid-address -f', {
227+
encoding: 'utf8',
228+
cwd: process.cwd(),
229+
env: { ...process.env, HOME: tempConfigDir }
230+
});
231+
fail('Should have thrown an error for invalid address');
232+
} catch (error: any) {
233+
expect(error.status).toBe(1);
234+
}
235+
});
236+
237+
it('should validate amount format in pay command', () => {
238+
try {
239+
execSync('node dist/bin/svm-pay.js pay -a invalid-amount', {
240+
encoding: 'utf8',
241+
cwd: process.cwd(),
242+
env: { ...process.env, HOME: tempConfigDir }
243+
});
244+
fail('Should have thrown an error for invalid amount');
245+
} catch (error: any) {
246+
expect(error.status).toBe(1);
247+
}
248+
});
249+
});
250+
251+
describe('environment variable support', () => {
252+
it('should support SVM_PAY_PRIVATE_KEY environment variable', async () => {
253+
// Create minimal config without private key
254+
const configPath = path.join(tempConfigDir, '.svm-pay', 'config.json');
255+
await fs.mkdir(path.dirname(configPath), { recursive: true });
256+
await fs.writeFile(configPath, JSON.stringify({
257+
openrouterApiKey: 'test-key',
258+
solanaRpcEndpoint: 'https://api.mainnet-beta.solana.com'
259+
}));
260+
261+
// Test that environment variable is recognized
262+
// Note: This would fail in practice without a valid key, but tests the precedence
263+
try {
264+
execSync('node dist/bin/svm-pay.js balance', {
265+
encoding: 'utf8',
266+
cwd: process.cwd(),
267+
env: {
268+
...process.env,
269+
HOME: tempConfigDir,
270+
SVM_PAY_PRIVATE_KEY: 'test-private-key'
271+
}
272+
});
273+
} catch (error: any) {
274+
// Should fail due to invalid key format, not missing key
275+
const stderr = error.stderr?.toString() || '';
276+
expect(stderr).not.toContain('Private key not configured');
277+
}
278+
});
279+
});
280+
});

0 commit comments

Comments
 (0)