Files
floci-ui/packages/api/src/adapter-aws/AwsSnsAdapter.test.ts
T
2026-09-25 18:18:33 -04:00

139 lines
5.9 KiB
TypeScript

import {describe, expect, test} from 'bun:test'
import {
CreateTopicCommand,
DeleteTopicCommand,
GetSubscriptionAttributesCommand,
GetTopicAttributesCommand,
ListSubscriptionsByTopicCommand,
ListTopicsCommand,
PublishCommand,
SubscribeCommand,
type SNSClient,
UnsubscribeCommand,
} from '@aws-sdk/client-sns'
import {AwsSnsAdapter} from './AwsSnsAdapter'
import {ValidationError} from '../cloud-spi/errors'
const ORDERS_ARN = 'arn:aws:sns:us-east-1:000000000000:orders'
const ALERTS_ARN = 'arn:aws:sns:us-east-1:000000000000:alerts'
function stubSns() {
const sent: object[] = []
const client = {
async send(command: object) {
sent.push(command)
if (command instanceof ListTopicsCommand) return {Topics: [{TopicArn: ORDERS_ARN}, {TopicArn: ALERTS_ARN}]}
if (command instanceof GetTopicAttributesCommand) {
return {Attributes: {DisplayName: 'Order notifications', FifoTopic: 'false'}}
}
if (command instanceof CreateTopicCommand) {
return {TopicArn: `arn:aws:sns:us-east-1:000000000000:${command.input.Name}`}
}
if (command instanceof PublishCommand) return {MessageId: 'e5a98c5b-cd94-4c38-a9ac-7260ec3e575f'}
if (command instanceof ListSubscriptionsByTopicCommand) return {Subscriptions: [{
SubscriptionArn: 'arn:aws:sns:us-east-1:000000000000:orders:sub-1',
Protocol: 'sqs',
Endpoint: 'arn:aws:sqs:us-east-1:000000000000:orders',
}]}
if (command instanceof GetSubscriptionAttributesCommand) return {Attributes: {FilterPolicy: '{"eventType":["created"]}'}}
if (command instanceof SubscribeCommand) return {SubscriptionArn: 'arn:aws:sns:us-east-1:000000000000:orders:sub-2'}
return {}
},
} as unknown as SNSClient
return {client, sent}
}
describe('AwsSnsAdapter', () => {
test('lists SNS topic ARNs as topic resources', async () => {
const {client} = stubSns()
const [topic] = await new AwsSnsAdapter(client).list()
expect(topic).toMatchObject({id: ORDERS_ARN, name: 'orders', service: 'notifications', type: 'topic'})
expect(topic?.metadata.arn).toBe(ORDERS_ARN)
})
test('gets a topic with its SNS attributes', async () => {
const {client, sent} = stubSns()
const topic = await new AwsSnsAdapter(client).get(ORDERS_ARN)
expect(sent[0]).toBeInstanceOf(GetTopicAttributesCommand)
expect(topic?.metadata.displayName).toBe('Order notifications')
})
test('creates and reads back an SNS topic', async () => {
const {client, sent} = stubSns()
const topic = await new AwsSnsAdapter(client).create({values: {name: 'orders'}})
expect(sent[0]).toBeInstanceOf(CreateTopicCommand)
expect((sent[0] as CreateTopicCommand).input.Name).toBe('orders')
expect(sent[1]).toBeInstanceOf(GetTopicAttributesCommand)
expect(topic.id).toBe(ORDERS_ARN)
})
test('deletes a topic by ARN', async () => {
const {client, sent} = stubSns()
await new AwsSnsAdapter(client).delete(ORDERS_ARN)
expect((sent[0] as DeleteTopicCommand).input.TopicArn).toBe(ORDERS_ARN)
})
test('publishes a message with optional subject and string message attributes', async () => {
const {client, sent} = stubSns()
const result = await new AwsSnsAdapter(client).publish(ORDERS_ARN, {
message: 'Order placed',
subject: 'Order update',
messageAttributes: {orderId: '1234'},
})
expect(result.messageId).toBe('e5a98c5b-cd94-4c38-a9ac-7260ec3e575f')
expect((sent[0] as PublishCommand).input).toEqual({
TopicArn: ORDERS_ARN,
Message: 'Order placed',
Subject: 'Order update',
MessageAttributes: {orderId: {DataType: 'String', StringValue: '1234'}},
})
})
test('manages SNS subscriptions with direct SDK commands and exposes their attributes', async () => {
const {client, sent} = stubSns()
const adapter = new AwsSnsAdapter(client)
const created = await adapter.createSnsSubscription(ORDERS_ARN, {
protocol: 'sqs',
endpoint: 'arn:aws:sqs:us-east-1:000000000000:orders',
})
const subscriptions = await adapter.listSnsSubscriptions(ORDERS_ARN)
await adapter.deleteSnsSubscription('arn:aws:sns:us-east-1:000000000000:orders:sub-1')
expect((sent[0] as SubscribeCommand).input).toEqual({
TopicArn: ORDERS_ARN,
Protocol: 'sqs',
Endpoint: 'arn:aws:sqs:us-east-1:000000000000:orders',
})
expect((sent[1] as ListSubscriptionsByTopicCommand).input).toEqual({TopicArn: ORDERS_ARN})
expect((sent[2] as GetSubscriptionAttributesCommand).input).toEqual({
SubscriptionArn: 'arn:aws:sns:us-east-1:000000000000:orders:sub-1',
})
expect((sent[3] as UnsubscribeCommand).input).toEqual({
SubscriptionArn: 'arn:aws:sns:us-east-1:000000000000:orders:sub-1',
})
expect(created).toMatchObject({arn: 'arn:aws:sns:us-east-1:000000000000:orders:sub-2', protocol: 'sqs', status: 'Confirmed'})
expect(subscriptions).toEqual([{
arn: 'arn:aws:sns:us-east-1:000000000000:orders:sub-1',
protocol: 'sqs',
endpoint: 'arn:aws:sqs:us-east-1:000000000000:orders',
status: 'Confirmed',
filterPolicy: '{"eventType":["created"]}',
}])
})
test('rejects invalid topic names and blank messages before calling SNS', async () => {
const {client, sent} = stubSns()
const adapter = new AwsSnsAdapter(client)
await expect(adapter.create({values: {name: 'has space'}})).rejects.toBeInstanceOf(ValidationError)
await expect(adapter.publish(ORDERS_ARN, {message: ''})).rejects.toBeInstanceOf(ValidationError)
expect(sent).toEqual([])
})
})