first commit

This commit is contained in:
Zakaria
2026-09-22 16:13:21 -04:00
commit 7e49be27f1
297 changed files with 63162 additions and 0 deletions
@@ -0,0 +1,544 @@
import {describe, expect, test} from 'bun:test'
import {
CreateDBInstanceCommand,
CreateDBSnapshotCommand,
DeleteDBInstanceCommand,
DescribeDBInstancesCommand,
DescribeDBSnapshotsCommand,
DescribeOrderableDBInstanceOptionsCommand,
ListTagsForResourceCommand,
ModifyDBInstanceCommand,
type RDSClient,
} from '@aws-sdk/client-rds'
import {ConflictError, ValidationError} from '../cloud-spi/errors'
import {createRdsService} from '../services/rds'
import {mapAwsSdkError} from './awsErrors'
import {AwsDatabaseAdapter} from './AwsDatabaseAdapter'
type RdsCommand =
| CreateDBInstanceCommand
| CreateDBSnapshotCommand
| DeleteDBInstanceCommand
| DescribeDBInstancesCommand
| DescribeDBSnapshotsCommand
| DescribeOrderableDBInstanceOptionsCommand
| ListTagsForResourceCommand
| ModifyDBInstanceCommand
type Responder = (command: RdsCommand) => unknown | Promise<unknown>
function recordingRds(responder: Responder) {
const commands: RdsCommand[] = []
const client = {
async send(command: RdsCommand) {
commands.push(command)
return responder(command)
},
} as unknown as RDSClient
return {client, commands}
}
function adapterWith(responder: Responder) {
const recording = recordingRds(responder)
return {
...recording,
adapter: new AwsDatabaseAdapter(createRdsService(recording.client), recording.client),
}
}
function instanceResponse(command: CreateDBInstanceCommand) {
return {
DBInstance: {
DBInstanceIdentifier: command.input.DBInstanceIdentifier,
Engine: command.input.Engine,
EngineVersion: command.input.EngineVersion,
DBInstanceClass: command.input.DBInstanceClass,
AllocatedStorage: command.input.AllocatedStorage,
MasterUsername: command.input.MasterUsername,
DBName: command.input.DBName,
DBInstanceStatus: 'creating',
},
}
}
const validCreateValues = {
dbInstanceIdentifier: 'orders-db',
engine: 'postgres',
masterUserPassword: 'secret-pass',
}
describe('AwsDatabaseAdapter', () => {
test('keeps list search behavior intact', async () => {
const {adapter} = adapterWith(async (command) => {
expect(command).toBeInstanceOf(DescribeDBInstancesCommand)
return {
DBInstances: [
{DBInstanceIdentifier: 'orders-db', Engine: 'postgres', VpcSecurityGroups: []},
{DBInstanceIdentifier: 'users-db', Engine: 'mysql', VpcSecurityGroups: []},
],
}
})
const resources = await adapter.list({search: ' ORDERS '})
expect(resources.map(({id}) => id)).toEqual(['orders-db'])
})
test('keeps get 404 behavior intact', async () => {
const {adapter} = adapterWith(async () => {
throw Object.assign(new Error('not found'), {$metadata: {httpStatusCode: 404}})
})
await expect(adapter.get('missing-db')).resolves.toBeNull()
})
test('creates an instance with required values and provider defaults', async () => {
const {adapter, commands} = adapterWith((command) => {
expect(command).toBeInstanceOf(CreateDBInstanceCommand)
return instanceResponse(command as CreateDBInstanceCommand)
})
const resource = await adapter.create({values: {
dbInstanceIdentifier: ' orders-db ',
engine: 'postgres',
masterUserPassword: 'secret-pass',
}})
const createCommand = commands[0] as CreateDBInstanceCommand
const {MasterUserPassword, ...nonSensitiveInput} = createCommand.input
expect(nonSensitiveInput).toEqual({
DBInstanceIdentifier: 'orders-db',
Engine: 'postgres',
DBInstanceClass: 'db.t3.micro',
AllocatedStorage: 20,
MasterUsername: 'root',
})
expect(MasterUserPassword === validCreateValues.masterUserPassword).toBe(true)
expect(JSON.stringify(resource).includes(validCreateValues.masterUserPassword)).toBe(false)
expect(resource).toMatchObject({
id: 'orders-db',
name: 'orders-db',
type: 'db-instance',
status: 'creating',
engine: 'postgres',
instanceClass: 'db.t3.micro',
})
})
test('maps all nine create fields including security groups', async () => {
const {adapter, commands} = adapterWith((command) => instanceResponse(command as CreateDBInstanceCommand))
await adapter.create({values: {
dbInstanceIdentifier: 'orders-db',
engine: 'mariadb',
masterUserPassword: 'secret-pass',
dbInstanceClass: ' db.m6g.large ',
allocatedStorage: '100',
masterUsername: ' app_user ',
dbName: ' orders ',
engineVersion: ' 10.11 ',
securityGroupIds: 'sg-12345678, sg-87654321',
}})
const createCommand = commands[0] as CreateDBInstanceCommand
const {MasterUserPassword, ...nonSensitiveInput} = createCommand.input
expect(nonSensitiveInput).toEqual({
DBInstanceIdentifier: 'orders-db',
Engine: 'mariadb',
DBInstanceClass: 'db.m6g.large',
AllocatedStorage: 100,
MasterUsername: 'app_user',
DBName: 'orders',
EngineVersion: '10.11',
VpcSecurityGroupIds: ['sg-12345678', 'sg-87654321'],
})
expect(MasterUserPassword === validCreateValues.masterUserPassword).toBe(true)
})
test('accepts array of security group ids', async () => {
const {adapter, commands} = adapterWith((command) => instanceResponse(command as CreateDBInstanceCommand))
await adapter.create({values: {
...validCreateValues,
securityGroupIds: ['sg-11111111', 'sg-22222222'],
}})
const createCommand = commands[0] as CreateDBInstanceCommand
expect(createCommand.input.VpcSecurityGroupIds).toEqual(['sg-11111111', 'sg-22222222'])
})
test('uses defaults for blank values and omits blank database name and engine version', async () => {
const {adapter, commands} = adapterWith((command) => instanceResponse(command as CreateDBInstanceCommand))
await adapter.create({values: {
...validCreateValues,
dbInstanceClass: ' ',
allocatedStorage: '',
masterUsername: ' ',
dbName: ' ',
}})
const input = (commands[0] as CreateDBInstanceCommand).input
expect(input.DBInstanceClass).toBe('db.t3.micro')
expect(input.AllocatedStorage).toBe(20)
expect(input.MasterUsername).toBe('root')
expect(input.DBName).toBeUndefined()
expect(input.EngineVersion).toBeUndefined()
})
test('rejects invalid instance identifiers before calling RDS', async () => {
const {adapter, commands} = adapterWith(async () => ({}))
for (const dbInstanceIdentifier of ['', 'Orders-db', '1-orders', 'orders--db', 'orders-', 'a'.repeat(64)]) {
await expect(adapter.create({values: {
...validCreateValues,
dbInstanceIdentifier,
}})).rejects.toBeInstanceOf(ValidationError)
}
expect(commands).toHaveLength(0)
})
test('rejects unsupported engines before calling RDS', async () => {
const {adapter, commands} = adapterWith(async () => ({}))
for (const engine of ['', 'oracle', 'POSTGRES', 42]) {
await expect(adapter.create({values: {...validCreateValues, engine}})).rejects.toBeInstanceOf(ValidationError)
}
expect(commands).toHaveLength(0)
})
test('rejects invalid passwords without exposing them or calling RDS', async () => {
const {adapter, commands} = adapterWith(async () => ({}))
for (const masterUserPassword of ['', 'short', 'x'.repeat(129), 42]) {
let caught: unknown
try {
await adapter.create({values: {...validCreateValues, masterUserPassword}})
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(ValidationError)
const exposesPassword = caught instanceof Error
&& typeof masterUserPassword === 'string'
&& masterUserPassword.length > 0
&& caught.message.includes(masterUserPassword)
expect(exposesPassword).toBe(false)
}
expect(commands).toHaveLength(0)
})
test('rejects non-positive, fractional, and non-numeric storage before calling RDS', async () => {
const {adapter, commands} = adapterWith(async () => ({}))
for (const allocatedStorage of [0, -1, 1.5, '0', '-1', '1.5', 'twenty', true]) {
await expect(adapter.create({values: {
...validCreateValues,
allocatedStorage,
}})).rejects.toBeInstanceOf(ValidationError)
}
expect(commands).toHaveLength(0)
})
test('rejects storage that exceeds the safe integer range before calling RDS', async () => {
const {adapter, commands} = adapterWith(async () => ({}))
await expect(adapter.create({values: {
...validCreateValues,
allocatedStorage: '9'.repeat(400),
}})).rejects.toBeInstanceOf(ValidationError)
expect(commands).toHaveLength(0)
})
test('leaves provider failures for the shared AWS error mapper', async () => {
const providerError = Object.assign(new Error('instance already exists'), {
name: 'DBInstanceAlreadyExistsFault',
$fault: 'client',
$metadata: {httpStatusCode: 400},
})
const {adapter} = adapterWith(async () => {
throw providerError
})
let caught: unknown
try {
await adapter.create({values: validCreateValues})
} catch (error) {
caught = error
}
expect(caught).toBe(providerError)
expect(mapAwsSdkError(caught)).toBeInstanceOf(ConflictError)
expect(caught instanceof Error && caught.message.includes(validCreateValues.masterUserPassword)).toBe(false)
})
test('deletes without a final snapshot', async () => {
const {adapter, commands} = adapterWith(async () => ({}))
await adapter.delete('orders-db')
const deleteCommand = commands[0] as DeleteDBInstanceCommand
expect(deleteCommand).toBeInstanceOf(DeleteDBInstanceCommand)
expect(deleteCommand.input).toEqual({
DBInstanceIdentifier: 'orders-db',
SkipFinalSnapshot: true,
})
})
test('lists and normalizes database snapshots', async () => {
const {adapter, commands} = adapterWith(async (command) => {
expect(command).toBeInstanceOf(DescribeDBSnapshotsCommand)
return {
DBSnapshots: [{
DBSnapshotIdentifier: 'orders-db-snapshot-1',
DBInstanceIdentifier: 'orders-db',
DBSnapshotArn: 'arn:aws:rds:eu-west-2:000000000000:snapshot:orders-db-snapshot-1',
Status: 'available',
Engine: 'postgres',
EngineVersion: '16.4',
AllocatedStorage: 100,
SnapshotType: 'manual',
SnapshotCreateTime: new Date('2026-08-14T09:30:00.000Z'),
Port: 5432,
AvailabilityZone: 'eu-west-2a',
VpcId: 'vpc-123',
}],
}
})
const snapshots = await adapter.listDatabaseSnapshots('orders-db')
expect((commands[0] as DescribeDBSnapshotsCommand).input).toEqual({
DBInstanceIdentifier: 'orders-db',
Marker: undefined,
})
expect(snapshots).toEqual([{
id: 'orders-db-snapshot-1',
name: 'orders-db-snapshot-1',
instanceIdentifier: 'orders-db',
status: 'available',
engine: 'postgres',
version: '16.4',
createdAt: '2026-08-14T09:30:00.000Z',
metadata: {
arn: 'arn:aws:rds:eu-west-2:000000000000:snapshot:orders-db-snapshot-1',
allocatedStorage: 100,
snapshotType: 'manual',
port: 5432,
availabilityZone: 'eu-west-2a',
vpcId: 'vpc-123',
},
}])
})
test('does not swallow snapshot provider failures', async () => {
const providerError = Object.assign(new Error('operation unavailable'), {
name: 'UnsupportedOperation',
$fault: 'client',
$metadata: {httpStatusCode: 501},
})
const {adapter} = adapterWith(async () => {
throw providerError
})
await expect(adapter.listDatabaseSnapshots()).rejects.toBe(providerError)
})
test('creates and normalizes a database snapshot', async () => {
const {adapter, commands} = adapterWith(async (command) => {
expect(command).toBeInstanceOf(CreateDBSnapshotCommand)
return {
DBSnapshot: {
DBSnapshotIdentifier: 'orders-db-snapshot-1',
DBInstanceIdentifier: 'orders-db',
Status: 'creating',
Engine: 'postgres',
SnapshotType: 'manual',
},
}
})
const snapshot = await adapter.createDatabaseSnapshot({
instanceIdentifier: ' orders-db ',
snapshotIdentifier: ' orders-db-snapshot-1 ',
})
const createCommand = commands[0] as CreateDBSnapshotCommand
expect(createCommand.input).toEqual({
DBInstanceIdentifier: 'orders-db',
DBSnapshotIdentifier: 'orders-db-snapshot-1',
})
expect(snapshot).toMatchObject({
id: 'orders-db-snapshot-1',
name: 'orders-db-snapshot-1',
instanceIdentifier: 'orders-db',
status: 'creating',
engine: 'postgres',
metadata: {snapshotType: 'manual'},
})
})
test('rejects missing and invalid snapshot identifiers before calling RDS', async () => {
const {adapter, commands} = adapterWith(async () => ({}))
const invalidInputs = [
{instanceIdentifier: '', snapshotIdentifier: 'orders-db-snapshot-1'},
{instanceIdentifier: 'orders-db', snapshotIdentifier: ''},
{instanceIdentifier: 'orders db', snapshotIdentifier: 'orders-db-snapshot-1'},
{instanceIdentifier: 'orders-db', snapshotIdentifier: '1-orders-snapshot'},
{instanceIdentifier: 'orders-db', snapshotIdentifier: 'orders--snapshot'},
{instanceIdentifier: 'orders-db', snapshotIdentifier: 'orders-snapshot-'},
{instanceIdentifier: 'orders-db', snapshotIdentifier: 'a'.repeat(256)},
]
for (const input of invalidInputs) {
await expect(adapter.createDatabaseSnapshot(input)).rejects.toBeInstanceOf(ValidationError)
}
expect(commands).toHaveLength(0)
})
test('lists and deduplicates orderable instance classes', async () => {
const {adapter, commands} = adapterWith(async (command) => {
expect(command).toBeInstanceOf(DescribeOrderableDBInstanceOptionsCommand)
return {
OrderableDBInstanceOptions: [
{DBInstanceClass: 'db.t3.micro', Engine: 'postgres'},
{DBInstanceClass: 'db.m8g.large', Engine: 'postgres'},
{DBInstanceClass: 'db.t3.micro', Engine: 'postgres'},
],
}
})
const classes = await adapter.listDatabaseOrderableInstanceClasses('postgres')
expect((commands[0] as DescribeOrderableDBInstanceOptionsCommand).input).toEqual({
Engine: 'postgres',
Marker: undefined,
})
expect(classes).toEqual(['db.t3.micro', 'db.m8g.large'])
})
test('updates an instance with supported fields and excludes unsupported fields', async () => {
const {adapter, commands} = adapterWith(async (command) => {
expect(command).toBeInstanceOf(ModifyDBInstanceCommand)
return {
DBInstance: {
DBInstanceIdentifier: 'orders-db',
Engine: 'postgres',
DBInstanceStatus: 'available',
IAMDatabaseAuthenticationEnabled: false,
DBSubnetGroup: {DBSubnetGroupName: 'private-db'},
VpcSecurityGroups: [{VpcSecurityGroupId: 'sg-1', Status: 'active'}, {VpcSecurityGroupId: 'sg-2', Status: 'active'}],
OptionGroupMemberships: [{OptionGroupName: 'default-postgres', Status: 'in-sync'}],
AutoMinorVersionUpgrade: true,
},
}
})
const resource = await adapter.update('orders-db', {values: {
masterUserPassword: 'new-secret',
enableIamDatabaseAuthentication: 'false',
dbSubnetGroupName: 'private-db',
vpcSecurityGroupIds: 'sg-1, sg-2',
optionGroupName: 'default-postgres',
autoMinorVersionUpgrade: 'true',
}})
const modifyCommand = commands[0] as ModifyDBInstanceCommand
expect(modifyCommand.input).toEqual({
DBInstanceIdentifier: 'orders-db',
MasterUserPassword: 'new-secret',
EnableIAMDatabaseAuthentication: false,
DBSubnetGroupName: 'private-db',
VpcSecurityGroupIds: ['sg-1', 'sg-2'],
OptionGroupName: 'default-postgres',
AutoMinorVersionUpgrade: true,
})
expect(modifyCommand.input).not.toHaveProperty('DBInstanceClass')
expect(modifyCommand.input).not.toHaveProperty('AllocatedStorage')
expect(modifyCommand.input).not.toHaveProperty('Engine')
expect(modifyCommand.input).not.toHaveProperty('EngineVersion')
expect(modifyCommand.input).not.toHaveProperty('ApplyImmediately')
expect(JSON.stringify(resource)).not.toContain('new-secret')
expect(resource.metadata.optionGroupName).toBe('default-postgres')
expect(resource.metadata.autoMinorVersionUpgrade).toBe(true)
expect(resource.metadata.vpcSecurityGroupIds).toBe('sg-1, sg-2')
})
test('accepts boolean literals for boolean update fields', async () => {
const {adapter, commands} = adapterWith(async (command) => ({
DBInstance: {
DBInstanceIdentifier: 'orders-db',
Engine: 'postgres',
DBInstanceStatus: 'available',
},
}))
await adapter.update('orders-db', {values: {
enableIamDatabaseAuthentication: true,
autoMinorVersionUpgrade: false,
}})
const modifyCommand = commands[0] as ModifyDBInstanceCommand
expect(modifyCommand.input.EnableIAMDatabaseAuthentication).toBe(true)
expect(modifyCommand.input.AutoMinorVersionUpgrade).toBe(false)
})
test('omits blank strings from update and trims CSV', async () => {
const {adapter, commands} = adapterWith(async () => ({
DBInstance: {
DBInstanceIdentifier: 'orders-db',
Engine: 'postgres',
DBInstanceStatus: 'available',
},
}))
await adapter.update('orders-db', {values: {
masterUserPassword: '',
enableIamDatabaseAuthentication: '',
dbSubnetGroupName: ' ',
vpcSecurityGroupIds: ' sg-100 , sg-200 ',
optionGroupName: '',
autoMinorVersionUpgrade: '',
}})
const modifyCommand = commands[0] as ModifyDBInstanceCommand
expect(modifyCommand.input).toEqual({
DBInstanceIdentifier: 'orders-db',
VpcSecurityGroupIds: ['sg-100', 'sg-200'],
})
})
test('rejects malformed booleans and invalid passwords without echoing password', async () => {
const {adapter, commands} = adapterWith(async () => ({}))
await expect(adapter.update('orders-db', {values: {
enableIamDatabaseAuthentication: 'maybe',
}})).rejects.toBeInstanceOf(ValidationError)
await expect(adapter.update('orders-db', {values: {
masterUserPassword: 'short',
}})).rejects.toThrow('8 and 128')
try {
await adapter.update('orders-db', {values: {masterUserPassword: 'short'}})
} catch (err) {
expect(String(err)).not.toContain('short')
}
expect(commands).toHaveLength(0)
})
test('rejects update requests with zero effective supported changes', async () => {
const {adapter, commands} = adapterWith(async () => ({}))
await expect(adapter.update('orders-db', {values: {}})).rejects.toBeInstanceOf(ValidationError)
await expect(adapter.update('orders-db', {values: {
masterUserPassword: '',
dbSubnetGroupName: '',
unsupportedField: 'value',
}})).rejects.toBeInstanceOf(ValidationError)
expect(commands).toHaveLength(0)
})
})