import {afterEach, describe, expect, test} from 'bun:test' import { CloseInstancePublicPortsCommand, CreateInstancesCommand, DeleteInstanceCommand, GetInstanceCommand, GetInstancesCommand, OpenInstancePublicPortsCommand, RebootInstanceCommand, StartInstanceCommand, StopInstanceCommand, type LightsailClient, } from '@aws-sdk/client-lightsail' import {AwsLightsailAdapter} from './AwsLightsailAdapter' const originalEndpoint = process.env.FLOCI_ENDPOINT afterEach(() => { // Assigning undefined would store the string "undefined" for every later test file. if (originalEndpoint === undefined) delete process.env.FLOCI_ENDPOINT else process.env.FLOCI_ENDPOINT = originalEndpoint }) function stubLightsail() { const sent: object[] = [] const client = { async send(command: object) { sent.push(command) if (command instanceof GetInstancesCommand) return {instances: [instance('web')]} if (command instanceof GetInstanceCommand) return {instance: instance(command.input.instanceName ?? 'web')} return {} }, } as unknown as LightsailClient return {client, sent} } function instance(name: string) { return { name, arn: `arn:aws:lightsail:us-east-1:000000000000:Instance/${name}`, createdAt: new Date('2026-10-09T00:00:00Z'), state: {name: 'running'}, blueprintId: 'ubuntu_22_04', blueprintName: 'Ubuntu', bundleId: 'nano_3_0', publicIpAddress: '203.0.113.10', privateIpAddress: '10.0.0.10', networking: {staticIp: '203.0.113.11', ports: [{fromPort: 80, toPort: 80, protocol: 'tcp'}]}, hardware: {disks: [{name: `${name}-root`, sizeInGb: 20, isSystemDisk: true, state: 'in-use'}]}, location: {availabilityZone: 'us-east-1a'}, } } describe('AwsLightsailAdapter', () => { test('maps Lightsail instances to Cloud Explorer resources', async () => { const [resource] = await new AwsLightsailAdapter(stubLightsail().client).list() expect(resource).toMatchObject({id: 'web', service: 'lightsail', status: 'running', region: 'us-east-1a'}) expect(resource?.metadata.publicIpAddress).toBe('203.0.113.10') expect(resource?.metadata).toMatchObject({privateIpAddress: '10.0.0.10', networking: {staticIp: '203.0.113.11'}, hardware: {disks: [{name: 'web-root'}]}}) }) test('merges diagnostic host-port mappings into UI metadata', async () => { process.env.FLOCI_ENDPOINT = 'http://core.test:4566' const fetchMock = globalThis.fetch globalThis.fetch = (async () => new Response(JSON.stringify({mappings: [ {instancePort: 80, host: 'localhost', hostPort: 18080, protocol: 'tcp'}, ]}))) as unknown as typeof fetch try { const resource = await new AwsLightsailAdapter(stubLightsail().client).get('web') expect(resource?.metadata.hostPortMappings).toEqual([ {instancePort: 80, host: 'localhost', hostPort: 18080, protocol: 'tcp'}, ]) } finally { globalThis.fetch = fetchMock } }) test('uses Lightsail CreateInstances request fields', async () => { const stub = stubLightsail() await new AwsLightsailAdapter(stub.client).create({values: { name: 'web', blueprintId: 'ubuntu_22_04', bundleId: 'nano_3_0', availabilityZone: 'us-east-1a', }}) expect(stub.sent.find((command) => command instanceof CreateInstancesCommand)) .toMatchObject({input: { instanceNames: ['web'], blueprintId: 'ubuntu_22_04', bundleId: 'nano_3_0', availabilityZone: 'us-east-1a', }}) }) test('deletes instances by name', async () => { const stub = stubLightsail() await new AwsLightsailAdapter(stub.client).delete('web') expect(stub.sent.find((command) => command instanceof DeleteInstanceCommand)) .toMatchObject({input: {instanceName: 'web'}}) }) test('uses Lightsail lifecycle commands', async () => { const stub = stubLightsail() const adapter = new AwsLightsailAdapter(stub.client) await adapter.start('web') await adapter.stop('web') await adapter.reboot('web') expect(stub.sent.find((command) => command instanceof StartInstanceCommand)).toMatchObject({input: {instanceName: 'web'}}) expect(stub.sent.find((command) => command instanceof StopInstanceCommand)).toMatchObject({input: {instanceName: 'web'}}) expect(stub.sent.find((command) => command instanceof RebootInstanceCommand)).toMatchObject({input: {instanceName: 'web'}}) }) test('opens and closes public ports with the Lightsail port contract', async () => { const stub = stubLightsail() const adapter = new AwsLightsailAdapter(stub.client) await adapter.update('web', {values: {action: 'openPort', fromPort: 8080, toPort: 8080, protocol: 'tcp'}}) await adapter.update('web', {values: {action: 'closePort', fromPort: 8080, toPort: 8080, protocol: 'tcp'}}) expect(stub.sent.find((command) => command instanceof OpenInstancePublicPortsCommand)).toMatchObject({input: {instanceName: 'web', portInfo: {fromPort: 8080, toPort: 8080, protocol: 'tcp'}}}) expect(stub.sent.find((command) => command instanceof CloseInstancePublicPortsCommand)).toMatchObject({input: {instanceName: 'web', portInfo: {fromPort: 8080, toPort: 8080, protocol: 'tcp'}}}) }) })