import {describe, expect, test} from 'bun:test' import type {EC2Client} from '@aws-sdk/client-ec2' import { AllocateAddressCommand, AssociateAddressCommand, AssociateRouteTableCommand, AuthorizeSecurityGroupEgressCommand, AuthorizeSecurityGroupIngressCommand, CreateImageCommand, CreateInternetGatewayCommand, CreateKeyPairCommand, CreateNatGatewayCommand, CreateRouteCommand, CreateRouteTableCommand, CreateSecurityGroupCommand, CreateSubnetCommand, CreateTagsCommand, CreateVpcCommand, DeleteInternetGatewayCommand, DeleteKeyPairCommand, DeleteNatGatewayCommand, DeleteRouteCommand, DeleteSecurityGroupCommand, DeleteSubnetCommand, DeleteTagsCommand, DeleteVpcCommand, DeregisterImageCommand, DescribeAddressesCommand, DescribeAvailabilityZonesCommand, DescribeImagesCommand, DescribeInstanceTypesCommand, DescribeInternetGatewaysCommand, DescribeKeyPairsCommand, DescribeNatGatewaysCommand, DescribeRouteTablesCommand, DescribeSecurityGroupsCommand, DescribeSubnetsCommand, DescribeVpcAttributeCommand, DescribeVpcsCommand, DetachInternetGatewayCommand, DisassociateAddressCommand, DisassociateRouteTableCommand, GetConsoleOutputCommand, RebootInstancesCommand, ModifySubnetAttributeCommand, ModifyVpcAttributeCommand, ReleaseAddressCommand, RevokeSecurityGroupEgressCommand, RevokeSecurityGroupIngressCommand, RunInstancesCommand, StartInstancesCommand, StopInstancesCommand, TerminateInstancesCommand, } from '@aws-sdk/client-ec2' import {createEc2Service} from './ec2' function fakeClient(responses: Record = {}): EC2Client { return { send: async (cmd: {constructor: {name: string}}) => { const name = cmd.constructor.name if (name in responses) return responses[name] return {} }, } as unknown as EC2Client } describe('ec2Service.runInstance', () => { test('sends RunInstancesCommand and returns mapped instance', async () => { const sentCmds: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sentCmds.push(cmd) return { Instances: [{ InstanceId: 'i-new123', Tags: [{Key: 'Name', Value: 'my-instance'}], State: {Name: 'pending'}, }], } }, } as unknown as EC2Client const service = createEc2Service(client) const result = await service.runInstance({ name: 'my-instance', imageId: 'ami-abc', instanceType: 't3.micro', }) expect(sentCmds[0]).toBeInstanceOf(RunInstancesCommand) expect(result.instanceId).toBe('i-new123') expect(result.name).toBe('my-instance') expect(result.state).toBe('pending') }) }) describe('ec2Service.describeInstance', () => { test('maps attached network interfaces and their addresses', async () => { const service = createEc2Service(fakeClient({ DescribeInstancesCommand: { Reservations: [{Instances: [{ InstanceId: 'i-networked', NetworkInterfaces: [{ NetworkInterfaceId: 'eni-123', Attachment: {DeviceIndex: 0}, Status: 'in-use', PrivateIpAddress: '10.0.1.10', Association: {PublicIp: '198.51.100.10'}, Groups: [{GroupId: 'sg-123', GroupName: 'web'}], }], }]}], }, })) const instance = await service.describeInstance('i-networked') expect(instance.networkInterfaces).toEqual([{ networkInterfaceId: 'eni-123', deviceIndex: 0, status: 'in-use', privateIpAddress: '10.0.1.10', publicIpAddress: '198.51.100.10', privateDnsName: undefined, subnetId: undefined, vpcId: undefined, securityGroups: [{id: 'sg-123', name: 'web'}], }]) }) }) describe('ec2Service lifecycle', () => { test('startInstance sends StartInstancesCommand with correct id', async () => { const sentCmds: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sentCmds.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).startInstance('i-abc') expect(sentCmds[0]).toBeInstanceOf(StartInstancesCommand) }) test('stopInstance sends StopInstancesCommand with correct id', async () => { const sentCmds: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sentCmds.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).stopInstance('i-abc') expect(sentCmds[0]).toBeInstanceOf(StopInstancesCommand) }) test('rebootInstance sends RebootInstancesCommand', async () => { const sentCmds: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sentCmds.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).rebootInstance('i-abc') expect(sentCmds[0]).toBeInstanceOf(RebootInstancesCommand) }) test('terminateInstance sends TerminateInstancesCommand', async () => { const sentCmds: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sentCmds.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).terminateInstance('i-abc') expect(sentCmds[0]).toBeInstanceOf(TerminateInstancesCommand) }) }) describe('ec2Service.updateInstanceTags', () => { test('calls CreateTags for additions and DeleteTags for removals', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client const service = createEc2Service(client) await service.updateInstanceTags('i-abc', [{key: 'Env', value: 'prod'}], ['OldKey']) expect(sent[0]).toBeInstanceOf(CreateTagsCommand) expect(sent[1]).toBeInstanceOf(DeleteTagsCommand) }) test('skips CreateTags when nothing to add', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).updateInstanceTags('i-abc', [], ['OldKey']) expect(sent[0]).toBeInstanceOf(DeleteTagsCommand) expect(sent).toHaveLength(1) }) }) describe('ec2Service.createImage', () => { test('sends CreateImageCommand and returns imageId', async () => { const client = fakeClient({CreateImageCommand: {ImageId: 'ami-new'}}) const result = await createEc2Service(client).createImage({instanceId: 'i-abc', name: 'snap-1'}) expect(result.imageId).toBe('ami-new') }) }) describe('ec2Service.deregisterImage', () => { test('sends DeregisterImageCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).deregisterImage('ami-old') expect(sent[0]).toBeInstanceOf(DeregisterImageCommand) }) }) describe('ec2Service.listAmis', () => { test('requests the complete image catalog and maps launchable AMIs', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd) return { Images: [{ ImageId: 'ami-amazonlinux2023', Name: 'Amazon Linux 2023', }], } }, } as unknown as EC2Client const result = await createEc2Service(client).listAmis() expect(sent[0]).toBeInstanceOf(DescribeImagesCommand) expect((sent[0] as DescribeImagesCommand).input).toEqual({}) expect(result).toEqual([ expect.objectContaining({ imageId: 'ami-amazonlinux2023', name: 'Amazon Linux 2023', }), ]) }) }) describe('ec2Service.getConsoleOutput', () => { test('decodes base64 output', async () => { const encoded = Buffer.from('hello console').toString('base64') const client = fakeClient({GetConsoleOutputCommand: {Output: encoded}}) const result = await createEc2Service(client).getConsoleOutput('i-abc') expect(result.output).toBe('hello console') }) test('returns empty output when operation is unsupported', async () => { const client: EC2Client = { send: async () => { throw new Error('GetConsoleOutput is not supported') }, } as unknown as EC2Client const result = await createEc2Service(client).getConsoleOutput('i-abc') expect(result.output).toBe('') }) }) describe('ec2Service key pairs', () => { test('listKeyPairs returns empty array when unsupported', async () => { const client: EC2Client = { send: async () => { throw new Error('DescribeKeyPairs is not supported') }, } as unknown as EC2Client const result = await createEc2Service(client).listKeyPairs() expect(result).toEqual([]) }) test('createKeyPair sends CreateKeyPairCommand with the requested key type and returns material', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd) return {KeyPairId: 'kp-1', KeyName: 'my-key', KeyMaterial: '-----BEGIN RSA-----'} }, } as unknown as EC2Client const result = await createEc2Service(client).createKeyPair('my-key', 'ed25519') expect(sent[0]).toBeInstanceOf(CreateKeyPairCommand) expect((sent[0] as CreateKeyPairCommand).input).toMatchObject({KeyName: 'my-key', KeyType: 'ed25519'}) expect(result.keyName).toBe('my-key') expect(result.keyMaterial).toBe('-----BEGIN RSA-----') }) test('createKeyPair defaults to rsa', async () => { const sent: CreateKeyPairCommand[] = [] const client: EC2Client = { send: async (cmd: CreateKeyPairCommand) => { sent.push(cmd) return {} }, } as unknown as EC2Client await createEc2Service(client).createKeyPair('my-key') expect(sent[0].input.KeyType).toBe('rsa') }) test('deleteKeyPair sends DeleteKeyPairCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).deleteKeyPair('my-key') expect(sent[0]).toBeInstanceOf(DeleteKeyPairCommand) }) }) describe('ec2Service security groups', () => { test('listSecurityGroups returns empty array when unsupported', async () => { const client: EC2Client = { send: async () => { throw new Error('DescribeSecurityGroups is not supported') }, } as unknown as EC2Client expect(await createEc2Service(client).listSecurityGroups()).toEqual([]) }) test('createSecurityGroup sends CreateSecurityGroupCommand and returns groupId', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd); return {GroupId: 'sg-new'} }, } as unknown as EC2Client const result = await createEc2Service(client).createSecurityGroup('web', 'Web SG', 'vpc-1') expect(sent[0]).toBeInstanceOf(CreateSecurityGroupCommand) expect(result.groupId).toBe('sg-new') }) test('deleteSecurityGroup sends DeleteSecurityGroupCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).deleteSecurityGroup('sg-1') expect(sent[0]).toBeInstanceOf(DeleteSecurityGroupCommand) }) test('authorizeSecurityGroupIngress sends correct command', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).authorizeSecurityGroupIngress('sg-1', {protocol: 'tcp', fromPort: 80, toPort: 80, cidr: '0.0.0.0/0'}) expect(sent[0]).toBeInstanceOf(AuthorizeSecurityGroupIngressCommand) }) test('revokeSecurityGroupIngress sends correct command', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).revokeSecurityGroupIngress('sg-1', {protocol: 'tcp', fromPort: 80, toPort: 80, cidr: '0.0.0.0/0'}) expect(sent[0]).toBeInstanceOf(RevokeSecurityGroupIngressCommand) }) test('authorizeSecurityGroupEgress sends correct command', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).authorizeSecurityGroupEgress('sg-1', {protocol: 'tcp', fromPort: 443, toPort: 443, cidr: '0.0.0.0/0'}) expect(sent[0]).toBeInstanceOf(AuthorizeSecurityGroupEgressCommand) }) test('revokeSecurityGroupEgress sends correct command', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).revokeSecurityGroupEgress('sg-1', {protocol: 'tcp', fromPort: 443, toPort: 443, cidr: '0.0.0.0/0'}) expect(sent[0]).toBeInstanceOf(RevokeSecurityGroupEgressCommand) }) }) describe('ec2Service VPCs and subnets', () => { test('listVpcs returns empty array when unsupported', async () => { const client: EC2Client = { send: async () => { throw new Error('DescribeVpcs is not supported') }, } as unknown as EC2Client expect(await createEc2Service(client).listVpcs()).toEqual([]) }) test('createVpc sends CreateVpcCommand and returns mapped vpc', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd); return {Vpc: {VpcId: 'vpc-new', CidrBlock: '10.0.0.0/16', IsDefault: false}} }, } as unknown as EC2Client const result = await createEc2Service(client).createVpc('10.0.0.0/16') expect(sent[0]).toBeInstanceOf(CreateVpcCommand) expect(result.vpcId).toBe('vpc-new') expect(result.cidrBlock).toBe('10.0.0.0/16') }) test('deleteVpc sends DeleteVpcCommand as final command', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).deleteVpc('vpc-1') // deleteVpc first cleans up SGs, subnets, IGWs, RTBs then sends DeleteVpcCommand last expect(sent[sent.length - 1]).toBeInstanceOf(DeleteVpcCommand) }) test('listSubnets returns empty array when unsupported', async () => { const client: EC2Client = { send: async () => { throw new Error('DescribeSubnets is not supported') }, } as unknown as EC2Client expect(await createEc2Service(client).listSubnets()).toEqual([]) }) test('createSubnet sends CreateSubnetCommand and returns mapped subnet', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd) return {Subnet: {SubnetId: 'subnet-new', VpcId: 'vpc-1', CidrBlock: '10.0.1.0/24', AvailabilityZone: 'us-east-1a'}} }, } as unknown as EC2Client const result = await createEc2Service(client).createSubnet('vpc-1', '10.0.1.0/24', 'us-east-1a') expect(sent[0]).toBeInstanceOf(CreateSubnetCommand) expect(result.subnetId).toBe('subnet-new') expect(result.availabilityZone).toBe('us-east-1a') }) test('deleteSubnet sends DeleteSubnetCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).deleteSubnet('subnet-1') expect(sent[0]).toBeInstanceOf(DeleteSubnetCommand) }) test('modifySubnetMapPublicIp sends ModifySubnetAttributeCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).modifySubnetMapPublicIp('subnet-1', true) expect(sent[0]).toBeInstanceOf(ModifySubnetAttributeCommand) }) test('getVpcAttributes returns dns settings from two parallel describe calls', async () => { const client = fakeClient({ DescribeVpcAttributeCommand: {AttributeValue: {Value: true}}, }) const result = await createEc2Service(client).getVpcAttributes('vpc-1') expect(result).toHaveProperty('enableDnsHostnames') expect(result).toHaveProperty('enableDnsSupport') }) test('modifyVpcAttribute sends ModifyVpcAttributeCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).modifyVpcAttribute('vpc-1', 'enableDnsHostnames', true) expect(sent[0]).toBeInstanceOf(ModifyVpcAttributeCommand) }) }) describe('ec2Service Internet Gateways', () => { test('listInternetGateways returns empty array when unsupported', async () => { const client: EC2Client = {send: async () => { throw new Error('DescribeInternetGateways is not supported') }} as unknown as EC2Client expect(await createEc2Service(client).listInternetGateways()).toEqual([]) }) test('createInternetGateway sends CreateInternetGatewayCommand and returns mapped igw', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd) if (cmd instanceof CreateInternetGatewayCommand) return {InternetGateway: {InternetGatewayId: 'igw-0a1b2c3d', Attachments: [], Tags: []}} return {} }, } as unknown as EC2Client const result = await createEc2Service(client).createInternetGateway() expect(sent[0]).toBeInstanceOf(CreateInternetGatewayCommand) expect(result.internetGatewayId).toBe('igw-0a1b2c3d') }) test('deleteInternetGateway detaches all VPCs then deletes', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd) if (cmd instanceof DescribeInternetGatewaysCommand) { return {InternetGateways: [{InternetGatewayId: 'igw-0a1b2c3d', Attachments: [{VpcId: 'vpc-0a1b2c3d', State: 'available'}]}]} } return {} }, } as unknown as EC2Client await createEc2Service(client).deleteInternetGateway('igw-0a1b2c3d') expect(sent[0]).toBeInstanceOf(DescribeInternetGatewaysCommand) expect(sent[1]).toBeInstanceOf(DetachInternetGatewayCommand) expect(sent[2]).toBeInstanceOf(DeleteInternetGatewayCommand) }) }) describe('ec2Service NAT Gateways', () => { test('listNatGateways returns empty array when unsupported', async () => { const client: EC2Client = {send: async () => { throw new Error('DescribeNatGateways is not supported') }} as unknown as EC2Client expect(await createEc2Service(client).listNatGateways()).toEqual([]) }) test('createNatGateway auto-allocates EIP when allocationId is absent', async () => { const sent: unknown[] = [] let attempt = 0 const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd) if (cmd instanceof AllocateAddressCommand) return {AllocationId: 'eipalloc-0a1b2c3d', PublicIp: '1.2.3.4'} if (cmd instanceof CreateNatGatewayCommand) return {NatGateway: {NatGatewayId: 'nat-0a1b2c3d', SubnetId: 'subnet-1', VpcId: 'vpc-1'}} if (cmd instanceof DescribeNatGatewaysCommand) { attempt++ return {NatGateways: [{NatGatewayId: 'nat-0a1b2c3d', SubnetId: 'subnet-1', VpcId: 'vpc-1', State: attempt >= 1 ? 'available' : 'pending'}]} } return {} }, } as unknown as EC2Client const result = await createEc2Service(client).createNatGateway({subnetId: 'subnet-1'}) expect(sent[0]).toBeInstanceOf(AllocateAddressCommand) expect(sent[1]).toBeInstanceOf(CreateNatGatewayCommand) expect(result.natGatewayId).toBe('nat-0a1b2c3d') }) test('deleteNatGateway sends DeleteNatGatewayCommand', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd) if (cmd instanceof DescribeNatGatewaysCommand) return {NatGateways: [{NatGatewayId: 'nat-0a1b2c3d', State: 'deleted'}]} return {} }, } as unknown as EC2Client await createEc2Service(client).deleteNatGateway('nat-0a1b2c3d') expect(sent[0]).toBeInstanceOf(DeleteNatGatewayCommand) }) }) describe('ec2Service Route Tables', () => { test('listRouteTables returns empty array when unsupported', async () => { const client: EC2Client = {send: async () => { throw new Error('DescribeRouteTables is not supported') }} as unknown as EC2Client expect(await createEc2Service(client).listRouteTables()).toEqual([]) }) test('createRoute sends CreateRouteCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).createRoute('rtb-0a1b2c3d', {destinationCidrBlock: '0.0.0.0/0', gatewayId: 'igw-0a1b2c3d'}) expect(sent[0]).toBeInstanceOf(CreateRouteCommand) }) test('deleteRoute sends DeleteRouteCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).deleteRoute('rtb-0a1b2c3d', '0.0.0.0/0') expect(sent[0]).toBeInstanceOf(DeleteRouteCommand) }) test('associateRouteTable sends AssociateRouteTableCommand and returns associationId', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd); return {AssociationId: 'rtbassoc-0a1b2c3d'} }, } as unknown as EC2Client const id = await createEc2Service(client).associateRouteTable('rtb-0a1b2c3d', 'subnet-0a1b2c3d') expect(sent[0]).toBeInstanceOf(AssociateRouteTableCommand) expect(id).toBe('rtbassoc-0a1b2c3d') }) test('disassociateRouteTable sends DisassociateRouteTableCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).disassociateRouteTable('rtbassoc-0a1b2c3d') expect(sent[0]).toBeInstanceOf(DisassociateRouteTableCommand) }) }) describe('ec2Service Elastic IPs', () => { test('listElasticIps returns empty array when unsupported', async () => { const client: EC2Client = {send: async () => { throw new Error('DescribeAddresses is not supported') }} as unknown as EC2Client expect(await createEc2Service(client).listElasticIps()).toEqual([]) }) test('allocateElasticIp sends AllocateAddressCommand and returns eip', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd) if (cmd instanceof AllocateAddressCommand) return {AllocationId: 'eipalloc-0a1b2c3d', PublicIp: '54.0.0.1', Domain: 'vpc'} return {} }, } as unknown as EC2Client const result = await createEc2Service(client).allocateElasticIp() expect(sent[0]).toBeInstanceOf(AllocateAddressCommand) expect(result.allocationId).toBe('eipalloc-0a1b2c3d') expect(result.publicIp).toBe('54.0.0.1') }) test('releaseElasticIp sends ReleaseAddressCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).releaseElasticIp('eipalloc-0a1b2c3d') expect(sent[0]).toBeInstanceOf(ReleaseAddressCommand) }) test('associateElasticIp sends AssociateAddressCommand and returns associationId', async () => { const sent: unknown[] = [] const client: EC2Client = { send: async (cmd: unknown) => { sent.push(cmd) if (cmd instanceof AssociateAddressCommand) return {AssociationId: 'eipassoc-test'} return {} }, } as unknown as EC2Client const result = await createEc2Service(client).associateElasticIp('eipalloc-0a1b2c3d', 'i-test1') expect(sent[0]).toBeInstanceOf(AssociateAddressCommand) expect(result.associationId).toBe('eipassoc-test') }) test('disassociateElasticIp sends DisassociateAddressCommand', async () => { const sent: unknown[] = [] const client: EC2Client = {send: async (cmd: unknown) => { sent.push(cmd); return {} }} as unknown as EC2Client await createEc2Service(client).disassociateElasticIp('eipassoc-test') expect(sent[0]).toBeInstanceOf(DisassociateAddressCommand) }) }) describe('ec2Service metadata', () => { test('listAvailabilityZones returns empty array when unsupported', async () => { const client: EC2Client = {send: async () => { throw new Error('DescribeAvailabilityZones is not supported') }} as unknown as EC2Client expect(await createEc2Service(client).listAvailabilityZones()).toEqual([]) }) test('listAvailabilityZones returns mapped zones', async () => { const client = fakeClient({ DescribeAvailabilityZonesCommand: {AvailabilityZones: [{ZoneName: 'us-east-1a', ZoneId: 'use1-az1', State: 'available'}]}, }) const result = await createEc2Service(client).listAvailabilityZones() expect(result).toHaveLength(1) expect(result[0].zoneName).toBe('us-east-1a') expect(result[0].zoneId).toBe('use1-az1') }) test('listInstanceTypes returns empty array when unsupported', async () => { const client: EC2Client = {send: async () => { throw new Error('DescribeInstanceTypes is not supported') }} as unknown as EC2Client expect(await createEc2Service(client).listInstanceTypes()).toEqual([]) }) test('listInstanceTypes returns mapped types', async () => { const client = fakeClient({ DescribeInstanceTypesCommand: {InstanceTypes: [{InstanceType: 't3.micro', VCpuInfo: {DefaultVCpus: 2}, MemoryInfo: {SizeInMiB: 1024}}]}, }) const result = await createEc2Service(client).listInstanceTypes() expect(result).toHaveLength(1) expect(result[0].instanceType).toBe('t3.micro') expect(result[0].vcpu).toBe(2) expect(result[0].memoryMiB).toBe(1024) }) }) // Suppress unused import warnings void DescribeKeyPairsCommand void DescribeSecurityGroupsCommand void DescribeSubnetsCommand void DescribeVpcAttributeCommand void DescribeVpcsCommand void DescribeInternetGatewaysCommand void DescribeNatGatewaysCommand void DescribeRouteTablesCommand void DescribeAddressesCommand void CreateRouteTableCommand void ReleaseAddressCommand