Files
floci-ui/packages/api/src/adapter-aws/AwsLightsailAdapter.test.ts
T
Zakaria 51a0a60a73
CI / checks (pull_request) Successful in 25m28s
test(api): stop tests leaking module mocks and env into later files
CI failed on a different set of tests each run because bun runs every test
file in one process and the file order differs between machines:

- the secretsmanager route test replaced the whole ../aws module, so files
  loaded after it lost awsClients, lambda and defaultAccountId (API Gateway,
  SES)
- the Lightsail tests restored FLOCI_ENDPOINT by assigning undefined, which
  stores the string "undefined" (runtime diagnostics)
- the auth tests used full-strength argon2 for ~30 sign-ins and timed out on
  slower runners; the OIDC cleanup test cached a discovery document without
  userinfo_endpoint for the shared issuer

Checked with 20 runs under bun test --randomize.
2026-10-24 11:30:46 -04:00

116 lines
5.4 KiB
TypeScript

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'}}})
})
})