139 lines
5.9 KiB
TypeScript
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([])
|
|
})
|
|
})
|