Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# [1.1.0](https://github.com/niledatabase/cli/compare/v1.0.11...v1.1.0) (2025-03-24)


### Features

* new features for local dev ([f542d3d](https://github.com/niledatabase/cli/commit/f542d3dfeccfaef051f2f9652ef8c986794c8356))
* new features for local dev ([5758a5f](https://github.com/niledatabase/cli/commit/5758a5f2fcbbd49dcfd9bd3a0778ce223729d0bf))

# [1.1.0-alpha.1](https://github.com/niledatabase/cli/compare/v1.0.11-alpha.1...v1.1.0-alpha.1) (2025-02-22)


Expand Down
147 changes: 42 additions & 105 deletions src/__tests__/commands/db.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,10 @@ describe('DB Command', () => {
it('should handle API errors', async () => {
const error = new Error('API Error');
mockNileAPI.listDatabases.mockRejectedValueOnce(error);
try {
await program.parseAsync(['node', 'test', 'db', 'list']);
fail('Should have thrown an error');
} catch (e) {
expect(e).toBeInstanceOf(ProcessExitError);
expect(console.error).toHaveBeenCalledWith(
theme.error('Failed to list databases:'),
'API Error'
);
}

await expect(
program.parseAsync(['node', 'test', 'db', 'list'])
).rejects.toThrow('API Error');
});

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

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

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

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

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

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

try {
await program.parseAsync(['node', 'test', 'db', 'create', '--name', 'test-db', '--region', 'AWS_US_WEST_2']);
} catch (error) {
expectProcessExit(error);
}

expect(console.error).toHaveBeenCalledWith(theme.error('Failed to create database:'), 'API error');
await expect(
program.parseAsync(['node', 'test', 'db', 'create', '--name', 'test-db', '--region', 'AWS_US_WEST_2'])
).rejects.toThrow('API error');
});
});

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

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

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

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

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

it('should require database name', async () => {
mockConfigManager.getDatabase.mockReturnValue(undefined);

try {
await program.parseAsync(['node', 'test', 'db', 'delete']);
} catch (error) {
expectProcessExit(error);
}

const actualError = (console.error as jest.Mock).mock.calls[0][1];
expect(console.error).toHaveBeenCalledWith(
theme.error('Failed to delete database:'),
expect.stringContaining('No database specified')
);
expect(actualError).toContain('No database specified');
await expect(
program.parseAsync(['node', 'test', 'db', 'delete'])
).rejects.toThrow();
});

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

try {
await program.parseAsync(['node', 'test', 'db', 'delete', 'test-db', '--force']);
} catch (error) {
expectProcessExit(error);
}

expect(console.error).toHaveBeenCalledWith(theme.error('Failed to delete database:'), 'API error');
await expect(
program.parseAsync(['node', 'test', 'db', 'delete', 'test-db'])
).rejects.toThrow('API error');
});
});

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

expect(mockNileAPI.listRegions).toHaveBeenCalledWith('test-workspace');
expect(console.log).toHaveBeenCalledWith(theme.primary('\nAvailable regions:'));
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('AWS_US_WEST_2'));
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('AWS_US_EAST_1'));
const calls = (console.log as jest.Mock).mock.calls;
const output = calls.map(call => call[0]).join('\n');
expect(output).toContain('NAME');
expect(output).toContain('AWS_US_WEST_2');
expect(output).toContain('AWS_US_EAST_1');
});

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

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

expect(console.log).toHaveBeenCalledWith('REGION');
expect(console.log).toHaveBeenCalledWith('NAME');
expect(console.log).toHaveBeenCalledWith('AWS_US_WEST_2');
expect(console.log).toHaveBeenCalledWith('AWS_US_EAST_1');
});

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

try {
await program.parseAsync(['node', 'test', 'db', 'regions']);
} catch (error) {
expectProcessExit(error);
}

expect(console.error).toHaveBeenCalledWith(theme.error('Failed to list regions:'), 'API error');
await expect(
program.parseAsync(['node', 'test', 'db', 'regions'])
).rejects.toThrow('API error');
});
});

Expand Down Expand Up @@ -381,67 +360,25 @@ describe('DB Command', () => {
});

it('should require database name', async () => {
mockConfigManager.getDatabase.mockReturnValue(undefined);
mockNileAPI.getDatabaseConnection.mockImplementation(() => {
throw new Error('No database specified. Use one of:\n1. --db flag\n2. nile config --db <n>\n3. NILE_DB environment variable');
});

try {
await program.parseAsync(['node', 'test', 'db', 'connectionstring', '--psql']);
} catch (error) {
expectProcessExit(error);
}

const actualError = (console.error as jest.Mock).mock.calls[0][1];
expect(console.error).toHaveBeenCalledWith(
theme.error('Failed to get connection string:'),
expect.stringContaining('No database specified')
);
expect(actualError).toContain('No database specified');
});

it('should require --psql flag', async () => {
const stderrWrite = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);

try {
await program.parseAsync(['node', 'test', 'db', 'connectionstring', '--name', 'test-db']);
} catch (error) {
expectProcessExit(error);
}

const stderrOutput = stderrWrite.mock.calls.map(call => call[0]).join('');
expect(stderrOutput).toContain("error: required option '--psql' not specified");
stderrWrite.mockRestore();
await expect(
program.parseAsync(['node', 'test', 'db', 'connectionstring'])
).rejects.toThrow();
});

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

try {
await program.parseAsync(['node', 'test', 'db', 'connectionstring', '--name', 'test-db', '--psql']);
} catch (error) {
expectProcessExit(error);
}

const actualError = (console.error as jest.Mock).mock.calls[0][1];
expect(console.error).toHaveBeenCalledWith(
theme.error('Failed to get connection string:'),
expect.stringContaining('No workspace specified')
);
expect(actualError).toContain('No workspace specified');
await expect(
program.parseAsync(['node', 'test', 'db', 'connectionstring', 'test-db'])
).rejects.toThrow();
});

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

try {
await program.parseAsync(['node', 'test', 'db', 'connectionstring', '--name', 'test-db', '--psql']);
} catch (error) {
expectProcessExit(error);
}

expect(console.error).toHaveBeenCalledWith(theme.error('Failed to get connection string:'), 'API error');
await expect(
program.parseAsync(['node', 'test', 'db', 'connectionstring', 'test-db', '--psql'])
).rejects.toThrow('API error');
});
});
});
8 changes: 4 additions & 4 deletions src/__tests__/commands/tenants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ describe('Tenants Command', () => {

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

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

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

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

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

expect(mockClient.end).toHaveBeenCalled();
});
Expand Down
Loading