Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
deac816
chore(ui-deps): upgrade frontend dependencies, gated green
Jul 5, 2026
f959827
chore(deps): full go get -u ./... upgrade, gated green
Jul 5, 2026
6528c97
chore: sync beads tracker, drop obsolete spa/.keep placeholder
Jul 5, 2026
a41e91c
chore: untrack .claude/settings.local.json, add to gitignore
Jul 5, 2026
8aad822
chore: gitignore transient checkpoint.md
Jul 5, 2026
ae10a86
chore(ci): add go fix modernize gate, apply remaining modernization
Jul 5, 2026
708d196
fix(s3): AWS-parity fixes — SSE persistence, ACL/replication/presign …
Jul 5, 2026
c18fa9b
fix(ec2): tag every taggable resource type; real instance-attribute/l…
Jul 5, 2026
f459c9f
fix(dynamodb): close Select/COUNT, batch-write duplicate-key, and tra…
Jul 5, 2026
5e8140e
chore: restore dashboard/static/spa/.keep placeholder
Jul 5, 2026
8baa462
fix(sns): wire SNS->Lambda and SNS->Firehose delivery in composition …
Jul 5, 2026
c3b5d46
fix(lambda): RemovePermission wire-shape, ESM qualifier ARN parsing, …
Jul 5, 2026
71cd544
fix(iam): disguised stubs, HTTP status codes, policy-doc encoding, pe…
Jul 5, 2026
58e50f3
docs(parity): add per-service PARITY.md manifests + schema template
Jul 5, 2026
647a8ce
fix(sns): PublishBatch attribute wire-shape, signed-envelope, replay …
Jul 5, 2026
17a215e
fix(sqs): duplicate XML decl, FIFO requeue ordering, RedriveAllowPoli…
Jul 5, 2026
58eec06
fix(cloudwatchlogs): drop deprecated seq-token validation, metric-fil…
Jul 5, 2026
ede7169
fix(cloudwatch): PutMetricData all-or-nothing, Values/Counts arrays, …
Jul 5, 2026
6548cf8
fix(kms): GrantTokens on 8 grant-valid ops, error class, grant-index …
Jul 5, 2026
86c2f9a
fix(cloudformation): idempotent DeleteStack, export-in-use enforcemen…
Jul 5, 2026
f2750d4
fix(ecs): rebuild serviceIndex on restore, persist resourceTags, real…
Jul 5, 2026
fba3c78
fix(cli): match cwBk.PutMetricData new single-return signature
Jul 5, 2026
23de3aa
fix(ecr): layer-upload FK/sequencing validation, PutImage digest veri…
Jul 5, 2026
f093a92
fix(rds): delete final-snapshot contract, DescribeDBInstances filters…
Jul 5, 2026
647d201
fix(secretsmanager): data race on lazy store, wire-field names, versi…
Jul 5, 2026
0b1194b
fix(ssm): parameter tier/version/hierarchy rules, document wire-shape…
Jul 5, 2026
e069888
fix(apigatewayv2): per-protocol integration timeout, stage-tag 404, m…
Jul 5, 2026
f222f37
fix(apigateway): rewrite PATCH semantics — multi-segment paths, value…
Jul 5, 2026
e5a9ac6
fix(kinesis): tag persistence data-loss, PutRecords not-found, ON_DEM…
Jul 5, 2026
9f33680
fix(stepfunctions): Map failure swallowing, empty history events, EXP…
Jul 5, 2026
d0ebe97
fix(eventbridge): target-type param structs, cron day-of-week, PutEve…
Jul 5, 2026
d118e0d
fix(autoscaling): revive dead lifecycle-hook timers, LaunchInstances …
Jul 5, 2026
017fc20
fix(elbv2): trust-store revocation wire shape, 400 error status, ALPN…
Jul 5, 2026
e5205db
fix(route53): tag routing/persistence, CallerReference idempotency, e…
Jul 5, 2026
e783037
fix(cloudfront): InconsistentQuantities validation, per-resource Alre…
Jul 5, 2026
7d0baf7
fix(elasticache): systematic error code/status, unreached sentinel, S…
Jul 5, 2026
a40e7cc
fix(cognitoidp): real TOTP MFA (was accept-any-code stub), user-exist…
Jul 5, 2026
0407b38
fix(ses): void-result XML wire shape, sending-paused/quota enforcemen…
Jul 5, 2026
704d7cd
fix(sts): TradeInToken expiry validation, signing algorithms, GetWebI…
Jul 5, 2026
7d16709
fix(glue): BatchGetPartition stub, error codes, orphan partitions, dr…
Jul 5, 2026
fb339b3
feat(store): generic typed datalayer store (Phase 3.1)
Jul 5, 2026
0f09d77
refactor(sqs): convert backend maps to pkgs/store (Phase 3.2 pilot)
Jul 5, 2026
12e611a
refactor(ec2): convert 147 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
f2e87b4
refactor(sagemaker): convert 70 region-nested backend maps to pkgs/st…
Jul 6, 2026
1ffc93d
refactor(cloudformation): convert backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
2472bb7
refactor(dynamodb): convert 7 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
496c52c
refactor(s3): convert backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
031a438
refactor(lambda): convert 9 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
91c888d
refactor(iam): convert 14 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
f5b6ec3
refactor(glue): convert 37 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
6db6e2a
refactor(ecs): convert 15 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
4179a2f
refactor(rds): convert 26 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
4c68677
refactor(ssm): convert 18 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
58b7152
refactor(iot): convert 35 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
e7dbfa8
refactor(quicksight): convert 27 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
6da0334
refactor(apigateway): convert 18 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
be6963d
refactor(cognitoidp): convert 12 backend maps to pkgs/store + elimina…
Jul 6, 2026
490d797
refactor(cloudfront): convert 23 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
82f8d5c
refactor(redshift): convert 29 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
e12611d
refactor(cloudwatch): convert 8 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
3506632
refactor(kms): convert 4 backend maps to pkgs/store + collapse grant …
Jul 6, 2026
3297f38
refactor(cloudwatchlogs): convert 26 backend maps to pkgs/store (Phas…
Jul 6, 2026
9e5d75b
refactor(bedrock): convert 34 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
7a7bc74
refactor(pinpoint): convert 15 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
b1a4815
refactor(eventbridge): convert 13 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
0680631
refactor(elasticache): convert 12 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
58a799d
refactor(sns): convert 5 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
9c8d209
refactor(elbv2): convert 5 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
870feb9
refactor(appsync): convert 12 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
3a8aa26
refactor(apigatewayv2): convert 17 backend maps to pkgs/store (Phase …
Jul 6, 2026
6b78072
refactor(opensearch): convert 17 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
848e055
refactor(memorydb): convert 10 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
7cc61d2
refactor(ecr): convert 10 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
df4bc6c
refactor(route53): convert 8 backend maps to pkgs/store (Phase 3.3)
Jul 6, 2026
328c34f
refactor(kinesis): convert streams map to pkgs/store (Phase 3.3)
Jul 6, 2026
de91ba9
refactor(secretsmanager): convert secrets map to pkgs/store (Phase 3.3)
Jul 6, 2026
76c4915
refactor(autoscaling): convert 7 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
e9af4cc
refactor(ses): convert 9 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
9b93862
refactor(organizations): convert 7 backend maps to pkgs/store (Phase …
Jul 9, 2026
6424eb0
refactor(stepfunctions): convert 6 backend maps to pkgs/store (Phase …
Jul 9, 2026
34e114e
refactor(neptune): convert 9 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
bf42810
refactor(dms): convert 16 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
96d883b
refactor(backup): convert 20 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
9cc7220
refactor(athena): convert 12 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
417908d
refactor(eks): convert 10 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
c98e953
refactor(transfer): convert 15 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
a8dd759
refactor(sts): convert sessions map to pkgs/store (Phase 3.3)
Jul 9, 2026
41c81ab
refactor(kafka): convert 6 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
b9236bd
refactor(s3control): convert 12 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
5897b82
refactor(wafv2): convert 6 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
6488386
refactor(securityhub): convert 16 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
a734f65
refactor(xray): convert 11 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
6a90d7a
refactor(pipes): convert 3 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
f5f0b01
refactor(elb): convert 2 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
fa4b730
refactor(docdb): convert 8 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
ba6258a
refactor(iotwireless): convert 14 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
35cb872
refactor(glacier): convert 4 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
7996d85
fix(docdb): complete the pkgs/store conversion (backend/persistence w…
Jul 9, 2026
0e196c5
refactor(fis): convert 3 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
ab1be83
refactor(route53resolver): convert 13 backend maps to pkgs/store (Pha…
Jul 9, 2026
b3b1da6
refactor(ssoadmin): convert 9 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
621b95c
refactor(sesv2): convert 14 backend maps to pkgs/store (Phase 3.3)
Jul 9, 2026
e45329a
refactor(ram): convert 3 backend maps to pkgs/store (Phase 3.3)
Jul 10, 2026
32ca766
refactor(medialive): convert 15 backend maps to pkgs/store (Phase 3.3)
Jul 10, 2026
3611783
refactor(directoryservice): convert 16 region-nested maps to pkgs/sto…
Jul 10, 2026
9505aa3
refactor(lakeformation): convert 7 backend maps to pkgs/store (Phase …
Jul 10, 2026
c2f0664
refactor(codecommit): convert 8 maps to pkgs/store + add persistence …
Jul 10, 2026
6f3979f
refactor(timestreamwrite): convert 3 backend maps to pkgs/store (Phas…
Jul 10, 2026
e15fbc9
refactor(firehose): convert region-nested streams map to pkgs/store (…
Jul 10, 2026
1e9d187
refactor(mwaa): convert region-nested environments map to pkgs/store …
Jul 10, 2026
1ecba47
refactor(awsconfig): convert 14 backend maps to pkgs/store (Phase 3.3)
Jul 10, 2026
3d0a615
refactor(codepipeline): convert 5 backend maps to pkgs/store (Phase 3.3)
Jul 10, 2026
bc6bbb7
refactor(scheduler): convert region-nested maps to pkgs/store (Phase …
Jul 10, 2026
43fce84
refactor(emr): convert 8 region-nested maps to pkgs/store (Phase 3.3)
Jul 10, 2026
9b20684
refactor(codebuild): convert 21 backend maps to pkgs/store (Phase 3.3)
Jul 10, 2026
f79892e
refactor(efs): convert 4 region-nested maps to pkgs/store (Phase 3.3)
Jul 10, 2026
4dcf99f
refactor(workspaces): convert 12 backend maps to pkgs/store (Phase 3.3)
Jul 10, 2026
798a164
refactor(batch): convert 8 region-nested maps to pkgs/store (Phase 3.3)
Jul 10, 2026
8b971ef
refactor(mq): convert 2 backend maps to pkgs/store (Phase 3.3)
Jul 10, 2026
0379814
refactor(inspector2): convert 16 backend maps to pkgs/store (Phase 3.3)
Jul 10, 2026
08a3b21
refactor(workmail): convert 18 org-nested maps to pkgs/store + add pe…
Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
160 changes: 159 additions & 1 deletion .beads/issues.jsonl

Large diffs are not rendered by default.

54 changes: 0 additions & 54 deletions .claude/settings.local.json

This file was deleted.

18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v9

modernize:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
token: ${{ env.GH_CI_TOKEN }}
ref: ${{ github.event.workflow_run.head_sha || github.sha }}

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
cache: true

- name: go fix (modernize)
run: go fix -diff ./...

govulncheck:
runs-on: ubuntu-latest
steps:
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,9 @@ lambda.zip
.terraform.lock.hcl
.terraform.lock.hcl
/releaser

# personal local Claude Code settings (never sync bypass perms to origin)
.claude/settings.local.json

# session resume checkpoint (transient)
checkpoint.md
56 changes: 54 additions & 2 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -2584,6 +2584,10 @@ func initializeServices(appCtx *service.AppContext) ([]service.Registerable, err
// Wire SNS→SQS delivery: when SNS publishes a message, deliver it to SQS queues.
wireSNSToSQS(byName["SNS"], byName["SQS"])

// Wire SNS→Lambda and SNS→Firehose subscription delivery, plus the SQS sender
// used for Lambda/Firehose subscription DLQ redelivery.
wireSNSToLambdaFirehose(byName["SNS"], byName["Lambda"], byName["Firehose"], byName["SQS"])

// Wire SQS → CloudWatch metric emission for NumberOfMessagesSent/Received/Deleted.
wireSQSMetrics(byName["SQS"], byName["CloudWatch"])

Expand Down Expand Up @@ -3145,6 +3149,54 @@ func wireSNSToSQS(snsReg, sqsReg service.Registerable) {
sqsBk.SubscribeToSNS(emitter)
}

// wireSNSToLambdaFirehose connects the SNS backend to Lambda and Firehose so that
// Lambda- and Firehose-protocol subscriptions actually receive published messages,
// and wires an SQS sender so failed Lambda/Firehose deliveries with a RedrivePolicy
// land in the subscription's dead-letter queue. This is independent of wireSNSToSQS:
// that function wires the SNS→SQS *subscription* delivery path via a publish emitter,
// while the SQS sender wired here only serves DLQ redelivery on failed Lambda/Firehose
// invocations (SNS backend's sqsSender field), so there is no overlap or double-wiring.
func wireSNSToLambdaFirehose(snsReg, lambdaReg, firehoseReg, sqsReg service.Registerable) {
snsH, ok := snsReg.(*snsbackend.Handler)
if !ok {
return
}

snsBk, ok := snsH.Backend.(*snsbackend.InMemoryBackend)
if !ok {
return
}

if lambdaH, lambdaOk := lambdaReg.(*lambdabackend.Handler); lambdaOk {
if lambdaBk, bkOk := lambdaH.Backend.(*lambdabackend.InMemoryBackend); bkOk {
snsBk.SetLambdaBackend(lambdaBk)
}
}

if firehoseH, firehoseOk := firehoseReg.(*firehosebackend.Handler); firehoseOk {
if firehoseBk, bkOk := firehoseH.Backend.(*firehosebackend.InMemoryBackend); bkOk {
snsBk.SetFirehoseBackend(&snsFirehosePutterAdapter{backend: firehoseBk})
}
}

if sqsH, sqsOk := sqsReg.(*sqsbackend.Handler); sqsOk {
if sqsBk, bkOk := sqsH.Backend.(*sqsbackend.InMemoryBackend); bkOk {
snsBk.SetSQSSender(&sqsSenderAdapter{backend: sqsBk})
}
}
}

// snsFirehosePutterAdapter adapts the Firehose backend to the sns.FirehosePutter
// interface, which omits the context parameter that the Firehose backend's
// PutRecordBatch requires.
type snsFirehosePutterAdapter struct {
backend *firehosebackend.InMemoryBackend
}

func (a *snsFirehosePutterAdapter) PutRecordBatch(streamName string, records [][]byte) (int, error) {
return a.backend.PutRecordBatch(context.Background(), streamName, records)
}

// wireSQSMetrics wires the CloudWatch metric emitter into the SQS backend so that
// SendMessage, ReceiveMessage, and DeleteMessage operations emit CloudWatch metrics.
func wireSQSMetrics(sqsReg, cwReg service.Registerable) {
Expand All @@ -3165,7 +3217,7 @@ func wireSQSMetrics(sqsReg, cwReg service.Registerable) {
sqsBk.SetMetricEmitter(
sqsbackend.MetricEmitterFunc(
func(namespace, name string, value float64, unit string) error {
_, err := cwBk.PutMetricData(namespace, []cwbackend.MetricDatum{
err := cwBk.PutMetricData(namespace, []cwbackend.MetricDatum{
{
MetricName: name,
Value: value,
Expand Down Expand Up @@ -4204,7 +4256,7 @@ func wireCWLogsMetricEmitter(cwlogsReg, cwReg service.Registerable) {
cwlogsBk.SetMetricEmitter(
cwlogsbackend.MetricEmitterFunc(
func(namespace, name string, value float64, unit string) error {
_, err := cwBk.PutMetricData(namespace, []cwbackend.MetricDatum{
err := cwBk.PutMetricData(namespace, []cwbackend.MetricDatum{
{
MetricName: name,
Namespace: namespace,
Expand Down
143 changes: 143 additions & 0 deletions cli_sns_wiring_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package main

import (
"context"
"encoding/json"
"io"
"testing"

"github.com/aws/aws-sdk-go-v2/aws"
s3sdk "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/config"
firehosebackend "github.com/blackbirdworks/gopherstack/services/firehose"
lambdabackend "github.com/blackbirdworks/gopherstack/services/lambda"
snsbackend "github.com/blackbirdworks/gopherstack/services/sns"
sqsbackend "github.com/blackbirdworks/gopherstack/services/sqs"
)

// wiringMockS3Storer implements firehose.S3Storer so the test can observe that a
// record delivered through the SNS→Firehose path actually reached S3.
type wiringMockS3Storer struct {
calls []wiringS3PutCall
}

type wiringS3PutCall struct {
bucket string
key string
body []byte
}

func (m *wiringMockS3Storer) PutObject(
_ context.Context,
input *s3sdk.PutObjectInput,
) (*s3sdk.PutObjectOutput, error) {
body, _ := io.ReadAll(input.Body)
m.calls = append(m.calls, wiringS3PutCall{
bucket: aws.ToString(input.Bucket),
key: aws.ToString(input.Key),
body: body,
})

return &s3sdk.PutObjectOutput{}, nil
}

// TestWireSNSToLambdaFirehose_EndToEndDelivery exercises the exact composition-root
// wiring used by cli.go's setup (wireSNSToLambdaFirehose) against REAL Lambda, Firehose,
// and SQS backends — not test doubles — to prove that subscribing a Lambda function or
// Firehose delivery stream to an SNS topic and publishing actually delivers, instead of
// silently no-opping as it did before SetLambdaBackend/SetFirehoseBackend/SetSQSSender
// were wired in the running binary.
func TestWireSNSToLambdaFirehose_EndToEndDelivery(t *testing.T) {
t.Parallel()

snsBk := snsbackend.NewInMemoryBackend()
snsH := snsbackend.NewHandler(snsBk)

lambdaBk := lambdabackend.NewInMemoryBackend(
nil, nil, lambdabackend.DefaultSettings(), config.DefaultAccountID, config.DefaultRegion,
)
lambdaH := lambdabackend.NewHandler(lambdaBk)

firehoseBk := firehosebackend.NewInMemoryBackend(config.DefaultAccountID, config.DefaultRegion)
firehoseH := firehosebackend.NewHandler(firehoseBk)

sqsBk := sqsbackend.NewInMemoryBackend()
sqsH := sqsbackend.NewHandler(sqsBk)

// This is the exact call cli.go's setupServices makes.
wireSNSToLambdaFirehose(snsH, lambdaH, firehoseH, sqsH)

ctx := context.Background()

// --- SNS -> Firehose: publish must reach the real Firehose backend and flush to S3. ---
s3mock := &wiringMockS3Storer{}
firehoseBk.SetS3Backend(s3mock)

_, err := firehoseBk.CreateDeliveryStream(ctx, firehosebackend.CreateDeliveryStreamInput{
Name: "wiring-test-stream",
S3Destination: &firehosebackend.S3DestinationDescription{
BucketARN: "arn:aws:s3:::wiring-test-bucket",
BufferingHints: &firehosebackend.BufferingHints{
SizeInMBs: 128,
IntervalInSeconds: 900,
},
},
})
require.NoError(t, err)

firehoseTopic, err := snsBk.CreateTopic("firehose-topic", nil)
require.NoError(t, err)

firehoseStreamARN := arn.Build(
"firehose", config.DefaultRegion, config.DefaultAccountID, "deliverystream/wiring-test-stream",
)
_, err = snsBk.Subscribe(firehoseTopic.TopicArn, "firehose", firehoseStreamARN, "")
require.NoError(t, err)

_, err = snsBk.Publish(firehoseTopic.TopicArn, "hello-firehose", "", "", nil)
require.NoError(t, err)

firehoseBk.FlushAll(ctx)

require.Len(t, s3mock.calls, 1,
"SNS publish must have been delivered to the real Firehose backend via cli.go's wireSNSToLambdaFirehose")
assert.Contains(t, string(s3mock.calls[0].body), "hello-firehose")

// --- SNS -> Lambda with DLQ: publish must reach the real Lambda backend. There is
// no Docker runtime available in this test, so the invocation fails deterministically
// (a real failure mode, not a stub) — proving delivery requires the failed message to
// be redirected to the real SQS DLQ via the wired SQS sender. ---
dlqOut, err := sqsBk.CreateQueue(&sqsbackend.CreateQueueInput{QueueName: "lambda-dlq"})
require.NoError(t, err)

lambdaTopic, err := snsBk.CreateTopic("lambda-topic", nil)
require.NoError(t, err)

functionARN := arn.Build(
"lambda", config.DefaultRegion, config.DefaultAccountID, "function:wiring-test-fn",
)
sub, err := snsBk.Subscribe(lambdaTopic.TopicArn, "lambda", functionARN, "")
require.NoError(t, err)

dlqARN := arn.Build("sqs", config.DefaultRegion, config.DefaultAccountID, "lambda-dlq")
redrivePolicy, err := json.Marshal(map[string]string{"deadLetterTargetArn": dlqARN})
require.NoError(t, err)
require.NoError(t, snsBk.SetSubscriptionAttributes(sub.SubscriptionArn, "RedrivePolicy", string(redrivePolicy)))

_, err = snsBk.Publish(lambdaTopic.TopicArn, "hello-lambda", "", "", nil)
require.NoError(t, err)

recvOut, err := sqsBk.ReceiveMessage(&sqsbackend.ReceiveMessageInput{
QueueURL: dlqOut.QueueURL,
MaxNumberOfMessages: 1,
})
require.NoError(t, err)
require.Len(t, recvOut.Messages, 1,
"failed Lambda delivery must have been redirected to the real SQS DLQ via cli.go's wireSNSToLambdaFirehose "+
"(proves both SetLambdaBackend and SetSQSSender were wired)")
assert.Equal(t, "hello-lambda", recvOut.Messages[0].Body)
}
Loading