Skip to content

Commit 438fe07

Browse files
authored
Feat: better error handling
Cli updates
2 parents 5462658 + 2320e03 commit 438fe07

12 files changed

Lines changed: 974 additions & 639 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# [1.1.0](https://github.com/niledatabase/cli/compare/v1.0.11...v1.1.0) (2025-03-24)
2+
3+
4+
### Features
5+
6+
* new features for local dev ([f542d3d](https://github.com/niledatabase/cli/commit/f542d3dfeccfaef051f2f9652ef8c986794c8356))
7+
* new features for local dev ([5758a5f](https://github.com/niledatabase/cli/commit/5758a5f2fcbbd49dcfd9bd3a0778ce223729d0bf))
8+
19
# [1.1.0-alpha.1](https://github.com/niledatabase/cli/compare/v1.0.11-alpha.1...v1.1.0-alpha.1) (2025-02-22)
210

311

src/__tests__/commands/db.test.ts

Lines changed: 42 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,10 @@ describe('DB Command', () => {
118118
it('should handle API errors', async () => {
119119
const error = new Error('API Error');
120120
mockNileAPI.listDatabases.mockRejectedValueOnce(error);
121-
try {
122-
await program.parseAsync(['node', 'test', 'db', 'list']);
123-
fail('Should have thrown an error');
124-
} catch (e) {
125-
expect(e).toBeInstanceOf(ProcessExitError);
126-
expect(console.error).toHaveBeenCalledWith(
127-
theme.error('Failed to list databases:'),
128-
'API Error'
129-
);
130-
}
121+
122+
await expect(
123+
program.parseAsync(['node', 'test', 'db', 'list'])
124+
).rejects.toThrow('API Error');
131125
});
132126

133127
it('should output in JSON format', async () => {
@@ -171,7 +165,9 @@ describe('DB Command', () => {
171165
expect(mockNileAPI.createDatabase).toHaveBeenCalledWith('test-workspace', 'test-db', 'AWS_US_WEST_2');
172166
const calls = (console.log as jest.Mock).mock.calls;
173167
const output = calls.map(call => call[0]).join('\n');
174-
expect(output).toContain(`Database '${theme.bold('test-db')}' created successfully`);
168+
expect(output).toContain('test-db');
169+
expect(output).toContain('AWS_US_WEST_2');
170+
expect(output).toContain('CREATING');
175171
});
176172

177173
it('should require name option', async () => {
@@ -183,7 +179,7 @@ describe('DB Command', () => {
183179

184180
const stderrCalls = mockStderrWrite.mock.calls;
185181
const stderrOutput = stderrCalls.map(call => call[0]).join('');
186-
expect(stderrOutput).toContain("error: required option '--name <n>' not specified");
182+
expect(stderrOutput).toContain("error: required option '--name <name>' not specified");
187183
});
188184

189185
it('should require region option', async () => {
@@ -195,19 +191,15 @@ describe('DB Command', () => {
195191

196192
const stderrCalls = mockStderrWrite.mock.calls;
197193
const stderrOutput = stderrCalls.map(call => call[0]).join('');
198-
expect(stderrOutput).toContain("error: required option '--region <r>' not specified");
194+
expect(stderrOutput).toContain("error: required option '--region <region>' not specified");
199195
});
200196

201197
it('should handle API errors', async () => {
202198
mockNileAPI.createDatabase.mockRejectedValue(new Error('API error'));
203199

204-
try {
205-
await program.parseAsync(['node', 'test', 'db', 'create', '--name', 'test-db', '--region', 'AWS_US_WEST_2']);
206-
} catch (error) {
207-
expectProcessExit(error);
208-
}
209-
210-
expect(console.error).toHaveBeenCalledWith(theme.error('Failed to create database:'), 'API error');
200+
await expect(
201+
program.parseAsync(['node', 'test', 'db', 'create', '--name', 'test-db', '--region', 'AWS_US_WEST_2'])
202+
).rejects.toThrow('API error');
211203
});
212204
});
213205

@@ -222,7 +214,11 @@ describe('DB Command', () => {
222214
await program.parseAsync(['node', 'test', 'db', 'show', 'test-db']);
223215

224216
expect(mockNileAPI.getDatabase).toHaveBeenCalledWith('test-workspace', 'test-db');
225-
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Database details:'));
217+
const calls = (console.log as jest.Mock).mock.calls;
218+
const output = calls.map(call => call[0]).join('\n');
219+
expect(output).toContain('test-db');
220+
expect(output).toContain('AWS_US_WEST_2');
221+
expect(output).toContain('ACTIVE');
226222
});
227223

228224
it('should output in JSON format', async () => {
@@ -261,39 +257,24 @@ describe('DB Command', () => {
261257
it('should delete database with provided name', async () => {
262258
mockNileAPI.deleteDatabase.mockResolvedValue(undefined);
263259

264-
await program.parseAsync(['node', 'test', 'db', 'delete', 'test-db', '--force']);
260+
await program.parseAsync(['node', 'test', 'db', 'delete', 'test-db']);
265261

266262
expect(mockNileAPI.deleteDatabase).toHaveBeenCalledWith('test-workspace', 'test-db');
267263
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Database deleted successfully'));
268264
});
269265

270266
it('should require database name', async () => {
271-
mockConfigManager.getDatabase.mockReturnValue(undefined);
272-
273-
try {
274-
await program.parseAsync(['node', 'test', 'db', 'delete']);
275-
} catch (error) {
276-
expectProcessExit(error);
277-
}
278-
279-
const actualError = (console.error as jest.Mock).mock.calls[0][1];
280-
expect(console.error).toHaveBeenCalledWith(
281-
theme.error('Failed to delete database:'),
282-
expect.stringContaining('No database specified')
283-
);
284-
expect(actualError).toContain('No database specified');
267+
await expect(
268+
program.parseAsync(['node', 'test', 'db', 'delete'])
269+
).rejects.toThrow();
285270
});
286271

287272
it('should handle API errors', async () => {
288273
mockNileAPI.deleteDatabase.mockRejectedValue(new Error('API error'));
289274

290-
try {
291-
await program.parseAsync(['node', 'test', 'db', 'delete', 'test-db', '--force']);
292-
} catch (error) {
293-
expectProcessExit(error);
294-
}
295-
296-
expect(console.error).toHaveBeenCalledWith(theme.error('Failed to delete database:'), 'API error');
275+
await expect(
276+
program.parseAsync(['node', 'test', 'db', 'delete', 'test-db'])
277+
).rejects.toThrow('API error');
297278
});
298279
});
299280

@@ -304,9 +285,11 @@ describe('DB Command', () => {
304285
await program.parseAsync(['node', 'test', 'db', 'regions']);
305286

306287
expect(mockNileAPI.listRegions).toHaveBeenCalledWith('test-workspace');
307-
expect(console.log).toHaveBeenCalledWith(theme.primary('\nAvailable regions:'));
308-
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('AWS_US_WEST_2'));
309-
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('AWS_US_EAST_1'));
288+
const calls = (console.log as jest.Mock).mock.calls;
289+
const output = calls.map(call => call[0]).join('\n');
290+
expect(output).toContain('NAME');
291+
expect(output).toContain('AWS_US_WEST_2');
292+
expect(output).toContain('AWS_US_EAST_1');
310293
});
311294

312295
it('should output in JSON format', async () => {
@@ -324,21 +307,17 @@ describe('DB Command', () => {
324307

325308
await program.parseAsync(['node', 'test', 'db', 'regions']);
326309

327-
expect(console.log).toHaveBeenCalledWith('REGION');
310+
expect(console.log).toHaveBeenCalledWith('NAME');
328311
expect(console.log).toHaveBeenCalledWith('AWS_US_WEST_2');
329312
expect(console.log).toHaveBeenCalledWith('AWS_US_EAST_1');
330313
});
331314

332315
it('should handle API errors', async () => {
333316
mockNileAPI.listRegions.mockRejectedValue(new Error('API error'));
334317

335-
try {
336-
await program.parseAsync(['node', 'test', 'db', 'regions']);
337-
} catch (error) {
338-
expectProcessExit(error);
339-
}
340-
341-
expect(console.error).toHaveBeenCalledWith(theme.error('Failed to list regions:'), 'API error');
318+
await expect(
319+
program.parseAsync(['node', 'test', 'db', 'regions'])
320+
).rejects.toThrow('API error');
342321
});
343322
});
344323

@@ -381,67 +360,25 @@ describe('DB Command', () => {
381360
});
382361

383362
it('should require database name', async () => {
384-
mockConfigManager.getDatabase.mockReturnValue(undefined);
385-
mockNileAPI.getDatabaseConnection.mockImplementation(() => {
386-
throw new Error('No database specified. Use one of:\n1. --db flag\n2. nile config --db <n>\n3. NILE_DB environment variable');
387-
});
388-
389-
try {
390-
await program.parseAsync(['node', 'test', 'db', 'connectionstring', '--psql']);
391-
} catch (error) {
392-
expectProcessExit(error);
393-
}
394-
395-
const actualError = (console.error as jest.Mock).mock.calls[0][1];
396-
expect(console.error).toHaveBeenCalledWith(
397-
theme.error('Failed to get connection string:'),
398-
expect.stringContaining('No database specified')
399-
);
400-
expect(actualError).toContain('No database specified');
401-
});
402-
403-
it('should require --psql flag', async () => {
404-
const stderrWrite = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
405-
406-
try {
407-
await program.parseAsync(['node', 'test', 'db', 'connectionstring', '--name', 'test-db']);
408-
} catch (error) {
409-
expectProcessExit(error);
410-
}
411-
412-
const stderrOutput = stderrWrite.mock.calls.map(call => call[0]).join('');
413-
expect(stderrOutput).toContain("error: required option '--psql' not specified");
414-
stderrWrite.mockRestore();
363+
await expect(
364+
program.parseAsync(['node', 'test', 'db', 'connectionstring'])
365+
).rejects.toThrow();
415366
});
416367

417368
it('should require workspace', async () => {
418369
mockConfigManager.getWorkspace.mockReturnValue(undefined);
419-
mockConfigManager.getDatabase.mockReturnValue('test-db');
420370

421-
try {
422-
await program.parseAsync(['node', 'test', 'db', 'connectionstring', '--name', 'test-db', '--psql']);
423-
} catch (error) {
424-
expectProcessExit(error);
425-
}
426-
427-
const actualError = (console.error as jest.Mock).mock.calls[0][1];
428-
expect(console.error).toHaveBeenCalledWith(
429-
theme.error('Failed to get connection string:'),
430-
expect.stringContaining('No workspace specified')
431-
);
432-
expect(actualError).toContain('No workspace specified');
371+
await expect(
372+
program.parseAsync(['node', 'test', 'db', 'connectionstring', 'test-db'])
373+
).rejects.toThrow();
433374
});
434375

435376
it('should handle API errors', async () => {
436377
mockNileAPI.getDatabaseConnection.mockRejectedValue(new Error('API error'));
437378

438-
try {
439-
await program.parseAsync(['node', 'test', 'db', 'connectionstring', '--name', 'test-db', '--psql']);
440-
} catch (error) {
441-
expectProcessExit(error);
442-
}
443-
444-
expect(console.error).toHaveBeenCalledWith(theme.error('Failed to get connection string:'), 'API error');
379+
await expect(
380+
program.parseAsync(['node', 'test', 'db', 'connectionstring', 'test-db', '--psql'])
381+
).rejects.toThrow('API error');
445382
});
446383
});
447384
});

src/__tests__/commands/tenants.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ describe('Tenants Command', () => {
170170

171171
await expect(
172172
program.parseAsync(['node', 'test', 'tenants', 'create', '--name', 'New Tenant'])
173-
).rejects.toThrow('Process.exit called with code: 1');
173+
).rejects.toThrow('Database error');
174174

175175
expect(mockClient.end).toHaveBeenCalled();
176176
});
@@ -203,7 +203,7 @@ describe('Tenants Command', () => {
203203
'--id', 'non-existent',
204204
'--new_name', 'New Name'
205205
])
206-
).rejects.toThrow('Process.exit called with code: 1');
206+
).rejects.toThrow(`Tenant with ID 'non-existent' not found`);
207207

208208
expect(mockClient.end).toHaveBeenCalled();
209209
});
@@ -241,7 +241,7 @@ describe('Tenants Command', () => {
241241
'node', 'test', 'tenants', 'delete',
242242
'--id', 'non-existent'
243243
])
244-
).rejects.toThrow('Process.exit called with code: 1');
244+
).rejects.toThrow(`Tenant with ID 'non-existent' not found`);
245245

246246
expect(mockClient.end).toHaveBeenCalled();
247247
});
@@ -253,7 +253,7 @@ describe('Tenants Command', () => {
253253

254254
await expect(
255255
program.parseAsync(['node', 'test', 'tenants', 'create', '--name', 'New Tenant'])
256-
).rejects.toThrow('Process.exit called with code: 1');
256+
).rejects.toThrow('Database error');
257257

258258
expect(mockClient.end).toHaveBeenCalled();
259259
});

0 commit comments

Comments
 (0)