Skip to content

Comments

Add fauxqs AWS engine#400

Merged
kibertoad merged 28 commits intomainfrom
feat/fauxsq
Feb 22, 2026
Merged

Add fauxqs AWS engine#400
kibertoad merged 28 commits intomainfrom
feat/fauxsq

Conversation

@kibertoad
Copy link
Owner

@kibertoad kibertoad commented Feb 19, 2026

Summary by CodeRabbit

  • New Features

    • Added support for configurable queue backend selection (Localstack or in-memory mock service) via environment variables during testing.
    • Introduced simplified test resource management for AWS services (queues, topics, buckets).
  • Chores

    • Updated CI/CD workflows to support testing with multiple queue backend configurations.
    • Improved test infrastructure for more reliable and flexible testing across different deployment scenarios.

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR refactors the testing infrastructure to support dual-mode AWS service mocking: LocalStack and Fauxqs. It introduces a new TestAwsResourceAdmin abstraction for test resource management, adds Fauxqs as a dependency, updates CI workflows to conditionally start LocalStack based on QUEUE_BACKEND, and refactors numerous test files to use the new abstraction instead of direct AWS SDK calls.

Changes

Cohort / File(s) Summary
Docker & Infrastructure Configuration
docker-compose.yml, turbo.json
Simplified LocalStack configuration by removing verbose environment variables; added QUEUE_BACKEND to Turbo global environment; modified test:coverage task dependency wiring.
Root Dependencies
package.json
Updated Turbo from ^2.6.2 to ^2.8.10.
S3 Payload Store Package
packages/s3-payload-store/package.json, packages/s3-payload-store/test/utils/globalSetup.ts, packages/s3-payload-store/test/utils/testS3Config.ts, packages/s3-payload-store/vitest.config.ts
Added fauxqs dependency; introduced global Vitest setup script to initialize mock server; updated S3 config endpoint and added requestHandler; constrained tests to single worker.
SNS Package Configuration
packages/sns/package.json, packages/sns/vitest.config.ts
Updated docker:start script to conditionally start LocalStack; added fauxqs dependency; added Vitest setupFiles configuration.
SNS Test Infrastructure
packages/sns/test/utils/fauxqsInstance.ts, packages/sns/test/utils/testAdmin.ts, packages/sns/test/utils/testSnsConfig.ts, packages/sns/test/utils/vitest.setup.ts, packages/sns/test/utils/s3Utils.ts
Introduced TestAwsResourceAdmin class for dual-mode (FauxQS/AWS SDK) resource management; added FauxQS server lifecycle utilities; refactored S3 utilities to support multiple buckets; added conditional S3 configuration based on QUEUE_BACKEND.
SNS Test Files
packages/sns/test/consumers/..., packages/sns/test/publishers/...*, packages/sns/test/utils/snsSubscriber.spec.ts, packages/gcp-pubsub/test/consumers/PubSubPermissionConsumer.payloadOffloading.spec.ts, packages/gcp-pubsub/test/utils/gcsUtils.ts
Replaced direct AWS SDK calls and legacy helpers with TestAwsResourceAdmin abstraction across multiple consumer and publisher tests; updated bucket utility signatures to support variadic operations.
SQS Package Configuration
packages/sqs/package.json, packages/sqs/vitest.config.ts
Updated docker:start script to conditionally start LocalStack; added fauxqs dependency; replaced rimraf in dependencies with fauxqs; added Vitest setupFiles.
SQS Test Infrastructure
packages/sqs/test/utils/fauxqsInstance.ts, packages/sqs/test/utils/testAdmin.ts, packages/sqs/test/utils/testAwsConfig.ts, packages/sqs/test/utils/testContext.ts, packages/sqs/test/utils/s3Utils.ts, packages/sqs/test/utils/vitest.setup.ts
Introduced TestAwsResourceAdmin class for resource lifecycle management; refactored S3 utilities for multiple buckets; added conditional S3 configuration; updated test context to wire TestAwsResourceAdmin into DI container.
SQS Test Files
packages/sqs/test/consumers/..., packages/sqs/test/publishers/..., packages/sqs/lib/fakes/TestSqsPublisher.spec.ts
Systematically replaced direct SQS/S3 utility calls with TestAwsResourceAdmin methods across consumer and publisher tests; updated queue and bucket lifecycle management.
CI Workflows
.github/workflows/ci.common.yml, .github/workflows/ci.yml
Added QUEUE_BACKEND workflow input with conditional logic for LocalStack startup; introduced AWS package detection and parallel job execution; updated automerge job dependency handling for dual-mode testing.

Sequence Diagram(s)

sequenceDiagram
    actor Test
    participant Setup as Vitest Setup
    participant FauxQS as FauxQS Server
    participant TestAdmin as TestAwsResourceAdmin
    participant AwsSDK as AWS SDK Clients
    
    Test->>Setup: Initialize test environment
    Setup->>Setup: Check QUEUE_BACKEND
    alt QUEUE_BACKEND = "fauxqs" (default)
        Setup->>FauxQS: ensureFauxqsServer()
        FauxQS->>FauxQS: Start server on port 4566
    else QUEUE_BACKEND = "localstack"
        Setup->>Setup: Skip FauxQS (use LocalStack)
    end
    
    Test->>TestAdmin: Create test resource admin
    TestAdmin->>TestAdmin: Inject FauxQS server & AWS clients
    
    Test->>TestAdmin: createQueue(name, opts)
    alt Server available
        TestAdmin->>FauxQS: server.createQueue(region, name, ...)
        FauxQS-->>TestAdmin: Queue reference
    else Using AWS SDK
        TestAdmin->>AwsSDK: assertQueue(sqsClient, ...)
        AwsSDK-->>TestAdmin: Queue created in LocalStack
    end
    TestAdmin-->>Test: Queue ready
    
    Test->>Test: Execute test logic
    
    Test->>TestAdmin: deleteQueues(...names)
    alt Server available
        TestAdmin->>FauxQS: server.deleteQueue(region, name)
    else Using AWS SDK
        TestAdmin->>AwsSDK: deleteQueue(sqsClient, ...)
    end
    TestAdmin-->>Test: Cleanup complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • chore: Vitest 4 #394: Modifies Vitest configuration patterns across multiple packages, introducing test configuration enhancements similar to the maxWorkers and setupFiles changes.
  • feat: test pubsub publisher #365: Updates CI workflow and docker-compose configuration files, sharing infrastructure configuration changes with this PR.
  • Implement test publisher #357: Introduces TestSqsPublisher which is directly refactored in this PR to use the new TestAwsResourceAdmin abstraction for test resource management.

Suggested labels

minor

Suggested reviewers

  • CarlosGamero

Poem

🐰 The mock server hops with faux AWS delight,
TestAdmin orchestrates resources day and night,
No more SDK chaos, the tests now shine bright—
LocalStack or Fauxqs, both paths feel just right! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add fauxqs AWS engine' accurately and concisely describes the main objective of the pull request, which is to introduce a new fauxqs AWS engine alongside existing localstack support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/fauxsq

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@kibertoad kibertoad marked this pull request as ready for review February 19, 2026 15:52
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
packages/s3-payload-store/test/utils/globalSetup.ts (1)

9-11: Same teardown() null-safety issue as in packages/sqs/test/utils/globalSetup.ts.

server will be undefined if startFauxqs() throws, causing a masked TypeError in teardown().

🛡️ Proposed fix
 export async function teardown() {
-  await server.stop()
+  await server?.stop()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/s3-payload-store/test/utils/globalSetup.ts` around lines 9 - 11, The
teardown() function calls server.stop() without guarding against server being
undefined, which will throw if start failed; update teardown() (the teardown
function in this file) to check that the server variable (the server instance
created by startFauxqs/startServer) is defined before calling server.stop(), and
only invoke stop() when server is non-null/undefined (or wrap the shutdown in a
safe try/catch that no-ops if server is missing) so teardown() no longer throws
a masked TypeError.
packages/sqs/test/utils/globalSetup.ts (2)

9-11: teardown() will throw if setup() fails.

server is uninitialized at module load. If startFauxqs() rejects (e.g., port 4566 already bound), server remains undefined. Vitest guarantees teardown() runs even when setup() throws, so server.stop() will then throw a TypeError, hiding the original error.

🛡️ Proposed fix
 export async function teardown() {
-  await server.stop()
+  await server?.stop()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/globalSetup.ts` around lines 9 - 11, teardown()
currently calls server.stop() unconditionally which throws if setup() failed and
server wasn't initialized; update teardown to only attempt stopping when server
is defined and has a stop method (e.g., if (server && typeof server.stop ===
'function') await server.stop()), so that teardown won't mask the original setup
error thrown by startFauxqs()/setup().

3-11: Consolidate duplicate globalSetup.ts files and extract hardcoded port to environment configuration.

Three packages have byte-for-byte identical globalSetup.ts files (packages/sqs, packages/s3-payload-store, and packages/sns) that hardcode port 4566. While current CI configuration runs package tests serially with per-package filtering, this duplication creates maintenance burden and brittleness: changes must be propagated across three files, and the hardcoded port makes the setup fragile if test execution patterns change or tests are run concurrently locally.

Extract the setup to a shared utility and parameterize the port to improve maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/globalSetup.ts` around lines 3 - 11, The three
identical globalSetup.ts files duplicate test setup and hardcode port 4566;
consolidate by moving the start/stop logic into a shared test utility (e.g.,
create a single helper that wraps startFauxqs) and update the package
globalSetup.ts files (functions setup, teardown and the server variable) to call
that shared helper; change the helper to accept the port via environment/config
(process.env.TEST_SQS_PORT or a test-config module) instead of using the literal
4566 so tests can run concurrently or be configured per environment.
packages/sns/test/utils/globalSetup.ts (1)

1-11: globalSetup.ts is copy-pasted verbatim across three packages.

packages/sns/test/utils/globalSetup.ts, packages/sqs/test/utils/globalSetup.ts, and packages/s3-payload-store/test/utils/globalSetup.ts are byte-for-byte identical. Extracting this into a shared helper (e.g., a @message-queue-toolkit/test-utils package or a top-level test/ workspace utility) would eliminate three-way drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/utils/globalSetup.ts` around lines 1 - 11, The three
identical test setup files duplicate logic for starting/stopping fauxqs; extract
that into a single shared test utility and import it from each package to avoid
drift. Create a new module exporting startFauxqs-based helpers (e.g.,
startTestFauxqs/stopTestFauxqs or a default exported setup/teardown) that
encapsulate the current server variable and calls to startFauxqs({ port: 4566,
logger: false, host: 'localstack' }) and server.stop(), then replace the
package-local functions (setup, teardown) and the local server variable with
imports of the new helpers (referencing startFauxqs, setup, teardown, and server
in your refactor) from the shared test-utils package or top-level test workspace
and update tests to use the centralized helpers.
packages/sqs/test/utils/testAwsConfig.ts (1)

14-22: TEST_S3_CONFIG is duplicated verbatim across three packages.

packages/sqs/test/utils/testAwsConfig.ts, packages/sns/test/utils/testSnsConfig.ts, and packages/s3-payload-store/test/utils/testS3Config.ts all declare an identical TEST_S3_CONFIG. A shared test-utilities package (or a single file exported from a common location) would eliminate the need to keep these in sync.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/testAwsConfig.ts` around lines 14 - 22, The
TEST_S3_CONFIG constant (typed as S3ClientConfig and using
createLocalhostHandler) is duplicated across the three test modules; extract
this constant into a single shared test-utilities export (e.g., export
TEST_S3_CONFIG from a common test-utils module), re-export or import any needed
types/helpers (S3ClientConfig, createLocalhostHandler) from existing packages,
then replace the local TEST_S3_CONFIG definitions in the three modules with
imports from the new shared module and delete the duplicate declarations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/sns/test/utils/globalSetup.ts`:
- Around line 9-11: The teardown() function calls server.stop() but server may
be undefined if setup() never assigned it; add a null guard so teardown only
calls stop when server is set (e.g., check server !== undefined or if (server)
before awaiting stop) to avoid TypeError. Update the teardown() implementation
(referencing teardown(), setup(), and the server variable) to safely handle an
uninitialized server.
- Around line 5-11: The setup() uses a hardcoded port when calling startFauxqs({
port: 4566, ... }) which causes EADDRINUSE in concurrent test runs; change
setup() to pick a non-conflicting port (either pass port: 0 to auto-assign if
startFauxqs supports it, or read a package-specific env var like
process.env.FAUXQS_PORT with a fallback unique port for this package) and ensure
teardown() still stops the same server instance; update references in setup,
teardown, and the startFauxqs call to use the chosen dynamic/env-driven port.

In `@packages/sqs/test/utils/globalSetup.ts`:
- Line 6: The TEST_AWS_CONFIG for SQS and SNS is missing requestHandler
configuration so requests to localhost:4566 aren't intercepted; update the
TEST_AWS_CONFIG object in both places to include requestHandler:
createLocalhostHandler() (use the same pattern as the S3 config) so the SQS/SNS
clients use createLocalhostHandler() when instantiated.

---

Duplicate comments:
In `@packages/sns/test/utils/testSnsConfig.ts`:
- Around line 15-23: TEST_S3_CONFIG is duplicated across test packages;
consolidate it into a single shared test utility and update callers to import
it: extract the TEST_S3_CONFIG constant (including its S3ClientConfig type,
endpoint, region, credentials, and requestHandler: createLocalhostHandler())
into a common test helper module (e.g., a shared-test-utils file/library),
export it, and then replace the local definitions in the packages that currently
define TEST_S3_CONFIG (the file that contains the current TEST_S3_CONFIG
constant and the other test files that duplicate it) with imports from the new
shared module so all tests use the single exported TEST_S3_CONFIG. Ensure
createLocalhostHandler() is available or re-exported from the shared module and
preserve the S3ClientConfig typing when exporting.

---

Nitpick comments:
In `@packages/s3-payload-store/test/utils/globalSetup.ts`:
- Around line 9-11: The teardown() function calls server.stop() without guarding
against server being undefined, which will throw if start failed; update
teardown() (the teardown function in this file) to check that the server
variable (the server instance created by startFauxqs/startServer) is defined
before calling server.stop(), and only invoke stop() when server is
non-null/undefined (or wrap the shutdown in a safe try/catch that no-ops if
server is missing) so teardown() no longer throws a masked TypeError.

In `@packages/sns/test/utils/globalSetup.ts`:
- Around line 1-11: The three identical test setup files duplicate logic for
starting/stopping fauxqs; extract that into a single shared test utility and
import it from each package to avoid drift. Create a new module exporting
startFauxqs-based helpers (e.g., startTestFauxqs/stopTestFauxqs or a default
exported setup/teardown) that encapsulate the current server variable and calls
to startFauxqs({ port: 4566, logger: false, host: 'localstack' }) and
server.stop(), then replace the package-local functions (setup, teardown) and
the local server variable with imports of the new helpers (referencing
startFauxqs, setup, teardown, and server in your refactor) from the shared
test-utils package or top-level test workspace and update tests to use the
centralized helpers.

In `@packages/sqs/test/utils/globalSetup.ts`:
- Around line 9-11: teardown() currently calls server.stop() unconditionally
which throws if setup() failed and server wasn't initialized; update teardown to
only attempt stopping when server is defined and has a stop method (e.g., if
(server && typeof server.stop === 'function') await server.stop()), so that
teardown won't mask the original setup error thrown by startFauxqs()/setup().
- Around line 3-11: The three identical globalSetup.ts files duplicate test
setup and hardcode port 4566; consolidate by moving the start/stop logic into a
shared test utility (e.g., create a single helper that wraps startFauxqs) and
update the package globalSetup.ts files (functions setup, teardown and the
server variable) to call that shared helper; change the helper to accept the
port via environment/config (process.env.TEST_SQS_PORT or a test-config module)
instead of using the literal 4566 so tests can run concurrently or be configured
per environment.

In `@packages/sqs/test/utils/testAwsConfig.ts`:
- Around line 14-22: The TEST_S3_CONFIG constant (typed as S3ClientConfig and
using createLocalhostHandler) is duplicated across the three test modules;
extract this constant into a single shared test-utilities export (e.g., export
TEST_S3_CONFIG from a common test-utils module), re-export or import any needed
types/helpers (S3ClientConfig, createLocalhostHandler) from existing packages,
then replace the local TEST_S3_CONFIG definitions in the three modules with
imports from the new shared module and delete the duplicate declarations.

Comment on lines 5 to 11
export async function setup() {
server = await startFauxqs({ port: 4566, logger: false, host: 'localstack' })
}

export async function teardown() {
await server.stop()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find all globalSetup.ts files and check their port configurations
fd 'globalSetup.ts' --exec cat {} +

Repository: kibertoad/message-queue-toolkit

Length of output: 988


🏁 Script executed:

# Check for turbo.json and CI configuration
fd 'turbo.json' --type f --exec cat {} \;
fd '.github' --type d --exec find {} -name '*.yml' -o -name '*.yaml' \; 2>/dev/null | head -20

Repository: kibertoad/message-queue-toolkit

Length of output: 588


🏁 Script executed:

# Check if there are any other test configuration files that might define ports
fd 'jest.config|vitest.config|test.*setup' --type f | head -20

Repository: kibertoad/message-queue-toolkit

Length of output: 553


🏁 Script executed:

# Find and read GitHub Actions workflows
fd '.github/workflows' -type d --exec find {} -name '*.yml' -o -name '*.yaml' \;

Repository: kibertoad/message-queue-toolkit

Length of output: 245


🏁 Script executed:

# Check root package.json for test script
cat package.json | head -50

Repository: kibertoad/message-queue-toolkit

Length of output: 755


🏁 Script executed:

# Check the vitest configs for the three packages
cat packages/sns/vitest.config.ts
cat packages/sqs/vitest.config.ts
cat packages/s3-payload-store/vitest.config.ts

Repository: kibertoad/message-queue-toolkit

Length of output: 1763


🏁 Script executed:

# Check if test task is defined in package.json scripts for each package
fd 'package.json' -path '*/packages/sns/*' -o -path '*/packages/sqs/*' -o -path '*/packages/s3-payload-store/*' --exec cat {} \;

Repository: kibertoad/message-queue-toolkit

Length of output: 243


🏁 Script executed:

# Find all GitHub Actions workflow files
find .github/workflows -name '*.yml' -o -name '*.yaml' 2>/dev/null

Repository: kibertoad/message-queue-toolkit

Length of output: 197


🏁 Script executed:

# Read specific workflow files
find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \) -exec cat {} \;

Repository: kibertoad/message-queue-toolkit

Length of output: 18704


🏁 Script executed:

# Check individual package.json files for test scripts
cat packages/sns/package.json | grep -A 20 '"scripts"'
cat packages/sqs/package.json | grep -A 20 '"scripts"'
cat packages/s3-payload-store/package.json | grep -A 20 '"scripts"'

Repository: kibertoad/message-queue-toolkit

Length of output: 2394


Hardcoded FauxQS port 4566 is vulnerable in concurrent test scenarios.

While the current GitHub Actions CI isolates each package test in a separate job (and thus separate runner), the code has a latent vulnerability: if tests from packages/sns, packages/sqs, and packages/s3-payload-store run concurrently on the same machine—whether via npm run test:ci locally without filtering, a future CI refactor, or a monorepo test runner consolidation—they will race to bind port 4566 and cause EADDRINUSE failures.

All three packages' globalSetup.ts files hardcode startFauxqs({ port: 4566, ... }).

Recommend assigning distinct ports per package (e.g., 4566/4567/4568) or using a dynamic/free port assignment (e.g., port: 0 for auto-assignment if the library supports it).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/utils/globalSetup.ts` around lines 5 - 11, The setup() uses
a hardcoded port when calling startFauxqs({ port: 4566, ... }) which causes
EADDRINUSE in concurrent test runs; change setup() to pick a non-conflicting
port (either pass port: 0 to auto-assign if startFauxqs supports it, or read a
package-specific env var like process.env.FAUXQS_PORT with a fallback unique
port for this package) and ensure teardown() still stops the same server
instance; update references in setup, teardown, and the startFauxqs call to use
the chosen dynamic/env-driven port.

Comment on lines 9 to 11
export async function teardown() {
await server.stop()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

teardown() will throw if setup() never assigned server.

server is declared but uninitialized; if setup() rejects before the await resolves, server remains undefined and teardown() will throw TypeError: Cannot read properties of undefined (reading 'stop'). While Vitest does not call teardown when setup throws, adding a null guard is defensive and aligns with the same risk present in all three identical globalSetup files.

🛡️ Proposed fix
 export async function teardown() {
-  await server.stop()
+  await server?.stop()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function teardown() {
await server.stop()
}
export async function teardown() {
await server?.stop()
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/utils/globalSetup.ts` around lines 9 - 11, The teardown()
function calls server.stop() but server may be undefined if setup() never
assigned it; add a null guard so teardown only calls stop when server is set
(e.g., check server !== undefined or if (server) before awaiting stop) to avoid
TypeError. Update the teardown() implementation (referencing teardown(),
setup(), and the server variable) to safely handle an uninitialized server.

let server: Awaited<ReturnType<typeof startFauxqs>>

export async function setup() {
server = await startFauxqs({ port: 4566, logger: false, host: 'localstack' })
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the SQS test globalSetup.ts file
cat -n packages/sqs/test/utils/globalSetup.ts

Repository: kibertoad/message-queue-toolkit

Length of output: 416


🏁 Script executed:

# Look for S3 test setup to compare the handler approach
find . -path "*/packages/s3*" -name "*globalSetup*" -o -path "*/packages/s3*" -name "*setup*" | head -10

Repository: kibertoad/message-queue-toolkit

Length of output: 128


🏁 Script executed:

# Search for createLocalhostHandler usage
rg "createLocalhostHandler" --type ts --type js -A 3 -B 3

Repository: kibertoad/message-queue-toolkit

Length of output: 2665


🏁 Script executed:

# Check for FauxQS configuration in package.json or dependencies
rg "fauxqs" -i --type json

Repository: kibertoad/message-queue-toolkit

Length of output: 249


🏁 Script executed:

# Look for any hosts or DNS configuration files
find . -name "hosts" -o -name ".hosts" -o -name "docker-compose*" -o -name ".env*" | grep -v node_modules | head -20

Repository: kibertoad/message-queue-toolkit

Length of output: 133


🏁 Script executed:

# View the full SQS test config
cat -n packages/sqs/test/utils/testAwsConfig.ts

Repository: kibertoad/message-queue-toolkit

Length of output: 823


🏁 Script executed:

# Check docker-compose.yml for any localstack DNS setup
cat -n docker-compose.yml | head -50

Repository: kibertoad/message-queue-toolkit

Length of output: 1945


🏁 Script executed:

# Check SNS test config as well
cat -n packages/sns/test/utils/testSnsConfig.ts

Repository: kibertoad/message-queue-toolkit

Length of output: 907


Add missing requestHandler: createLocalhostHandler() to SQS and SNS client configs.

The SQS and SNS test configs currently omit requestHandler: createLocalhostHandler(), while the S3 config correctly includes it. Without this handler, requests to localhost:4566 won't be properly intercepted and routed. Add requestHandler: createLocalhostHandler() to TEST_AWS_CONFIG in both packages/sqs/test/utils/testAwsConfig.ts (after line 11) and packages/sns/test/utils/testSnsConfig.ts (after line 12), matching the pattern used in the S3 configs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/globalSetup.ts` at line 6, The TEST_AWS_CONFIG for
SQS and SNS is missing requestHandler configuration so requests to
localhost:4566 aren't intercepted; update the TEST_AWS_CONFIG object in both
places to include requestHandler: createLocalhostHandler() (use the same pattern
as the S3 config) so the SQS/SNS clients use createLocalhostHandler() when
instantiated.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/sns/package.json`:
- Line 58: The dependency "fauxqs" is invalid and must be removed or corrected
in the package.json dependency lists for the affected packages (the SNS, SQS and
S3-payload-store packages); locate the "fauxqs": "^1.7.0" dependency entries in
each package.json, either delete the dependency if it's unused or replace it
with the correct npm package name/version if this was a typo, then run a fresh
install (npm ci / npm install) to verify the lockfile and build, and commit the
updated package.json (and package-lock.json/yarn.lock) changes.

"awilix": "^12.0.5",
"awilix": "^12.1.1",
"awilix-manager": "^6.1.0",
"fauxqs": "^1.7.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if fauxqs is defined as a workspace package
echo "=== Searching for fauxqs in package.json files ==="
find . -name "package.json" -type f ! -path "*/node_modules/*" -exec grep -l "fauxqs" {} \;

echo ""
echo "=== Checking for fauxqs package name declaration ==="
rg '"name"\s*:\s*"[^"]*fauxqs' --type json

echo ""
echo "=== Checking root-level workspaces configuration ==="
head -n 200 package.json | grep -A 20 "workspaces"

echo ""
echo "=== Listing all packages in the repo ==="
fd "package.json" --max-depth 3 --exclude "node_modules" -exec grep -H '"name"' {} \; | head -20

Repository: kibertoad/message-queue-toolkit

Length of output: 1241


🌐 Web query:

fauxqs npm package

💡 Result:

I couldn’t find any public npm package named fauxqs (no npm package page or repo shows up in search results), so it may be:

  • a typo (e.g., you meant something else like fauxauth (npmjs.com)), or
  • a private/internal package (not publicly indexed), or
  • unpublished / deleted / renamed.

To confirm from your machine:

npm view fauxqs
npm search fauxqs

If you tell me what it’s supposed to do (or paste the dependency line from package.json / an install error), I can help identify the intended package.

Citations:


🏁 Script executed:

#!/bin/bash
echo "=== packages/sns/package.json - fauxqs reference ==="
grep -n "fauxqs" packages/sns/package.json

echo ""
echo "=== packages/sqs/package.json - fauxqs reference ==="
grep -n "fauxqs" packages/sqs/package.json

echo ""
echo "=== packages/s3-payload-store/package.json - fauxqs reference ==="
grep -n "fauxqs" packages/s3-payload-store/package.json

echo ""
echo "=== Checking if fauxqs might be defined under different name ==="
find packages -name "package.json" ! -path "*/node_modules/*" -exec grep -H "querystring\|query-string\|qs" {} \; | grep -v node_modules | head -5

Repository: kibertoad/message-queue-toolkit

Length of output: 666


🏁 Script executed:

#!/bin/bash
echo "=== Checking for lock files with fauxqs reference ==="
if [ -f "package-lock.json" ]; then
  grep -c "fauxqs" package-lock.json 2>/dev/null || echo "No package-lock.json entries"
fi

if [ -f "pnpm-lock.yaml" ]; then
  grep -c "fauxqs" pnpm-lock.yaml 2>/dev/null || echo "No pnpm-lock.yaml entries"
fi

echo ""
echo "=== Checking git log for fauxqs ==="
git log --all --oneline --grep="fauxqs" 2>/dev/null | head -5 || echo "No git commits mentioning fauxqs"

echo ""
echo "=== Checking if fauxqs could be a typo for 'qs' ==="
rg "\"qs\"" packages/*/package.json --type json | head -10

Repository: kibertoad/message-queue-toolkit

Length of output: 218


Remove or replace the invalid fauxqs dependency across all affected packages.

The fauxqs package does not exist on the npm registry and is not defined as a workspace package in this repository. It's currently referenced as "fauxqs": "^1.7.0" in:

  • packages/sns/package.json (line 58)
  • packages/sqs/package.json (line 53)
  • packages/s3-payload-store/package.json (line 44)

This will cause npm install to fail. Either remove the dependency if it's unused, or correct it to the intended package name if it's a typo.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/package.json` at line 58, The dependency "fauxqs" is invalid and
must be removed or corrected in the package.json dependency lists for the
affected packages (the SNS, SQS and S3-payload-store packages); locate the
"fauxqs": "^1.7.0" dependency entries in each package.json, either delete the
dependency if it's unused or replace it with the correct npm package
name/version if this was a typo, then run a fresh install (npm ci / npm install)
to verify the lockfile and build, and commit the updated package.json (and
package-lock.json/yarn.lock) changes.

@kibertoad kibertoad changed the title Switch to fauxqs Add fauxqs AWS engine Feb 21, 2026
@kibertoad
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 21, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

opts?: { attributes?: Record<string, string>; tags?: Record<string, string> },
) {
if (this.server) {
return this.server.createQueue(name, {
Copy link
Owner Author

@kibertoad kibertoad Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all these methods would also work if we just used SDK client same way we do for localstack part, but I wanted to ensure we have feature parity on the programmatic API side (spoilers: there were gaps which needed to be filled)

it is also somewhat faster, but not to an extent that would make recommend converting any existing repos that make SDK calls to use programmatic API instead.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
packages/sqs/lib/fakes/TestSqsPublisher.spec.ts (1)

16-18: ⚠️ Potential issue | 🟠 Major

Hardcoded LocalStack URLs break all queueUrl-consuming tests under Fauxqs

The URL constants bake in the LocalStack hostname and AWS account ID:

http://sqs.eu-west-1.localstack:4566/000000000000/<queue>

Even though queue creation is now routed through testAdmin.createQueue, every test that uses these constants for publishing or for the verification consumer (all "publish with queueUrl" cases, and the verification consumers in the "queueName", "consumer", "publisher", and FIFO suites) will fail when QUEUE_BACKEND=fauxqs because Fauxqs exposes queues at a different base URL.

The URL should be resolved dynamically — e.g., from the return value of testAdmin.createQueue or via GetQueueUrlCommand — rather than constructed from a hard-coded LocalStack address.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/lib/fakes/TestSqsPublisher.spec.ts` around lines 16 - 18, The
tests currently hardcode queueUrl and fifoQueueUrl strings which embed a
LocalStack hostname/account and break when QUEUE_BACKEND=fauxqs; update
TestSqsPublisher.spec.ts to resolve queue URLs dynamically instead of
constructing them: after creating queues via testAdmin.createQueue (or by
calling GetQueueUrlCommand) capture and use the returned queueUrl for publishing
and verification, and replace all usages of the queueUrl and fifoQueueUrl
constants with those dynamic values so tests work for both LocalStack and Fauxqs
backends.
packages/sqs/test/publishers/SqsPermissionPublisher.fifo.spec.ts (1)

275-332: ⚠️ Potential issue | 🟡 Minor

Missing S3 bucket cleanup after the offloading test.

'fifo-payload-offloading-test-bucket' is created here but never cleaned up. The offloaded payload object uploaded to S3 accumulates across repeated LocalStack runs (no-op for Fauxqs since it's in-memory per run). Add an emptyBuckets call to the suite's afterEach to keep the test self-contained.

🧹 Proposed fix — add bucket cleanup to afterEach
+  const s3BucketName = 'fifo-payload-offloading-test-bucket'
+
   const queueName = 'test-fifo-queue.fifo'

   let diContainer: AwilixContainer<Dependencies>
   let sqsClient: SQSClient
   let testAdmin: TestAwsResourceAdmin
   beforeEach(async () => {
     diContainer = await registerDependencies()
     sqsClient = diContainer.cradle.sqsClient
     testAdmin = diContainer.cradle.testAdmin
     await testAdmin.deleteQueues(queueName)
   })

   afterEach(async () => {
     await testAdmin.deleteQueues(queueName)
+    await testAdmin.emptyBuckets(s3BucketName)
     await diContainer.cradle.awilixManager.executeDispose()
     await diContainer.dispose()
   })

And remove the local const s3BucketName declaration from inside the test body.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/publishers/SqsPermissionPublisher.fifo.spec.ts` around
lines 275 - 332, The test creates an S3 bucket
('fifo-payload-offloading-test-bucket') via testAdmin.createBucket but never
cleans it up; remove the local const s3BucketName from the test and add a
cleanup call (testAdmin.emptyBuckets or equivalent) in the suite's afterEach so
the offloaded objects uploaded by the S3PayloadStore (used in payloadStoreConfig
and SqsPermissionPublisherFifo) are removed between runs; ensure the afterEach
references the same bucket name used when creating the bucket or empties all
test buckets.
packages/sqs/test/consumers/SqsPermissionConsumer.startupResourcePolling.spec.ts (1)

59-59: ⚠️ Potential issue | 🟡 Minor

Clarify queue URL resolution mechanism with fauxqs—currently working but undocumented dependency.

The queueUrl is hardcoded to http://sqs.eu-west-1.localstack:4566/… while the SQS client is configured with endpoint: 'http://localhost:4566'. The tests pass because fauxqs resolves queues by the path component (queue name) rather than requiring exact URL matching. However, this behavior is implicit and not documented. This pattern is repeated across 25+ test files (packages/sqs and packages/sns), making it a widespread undocumented assumption about fauxqs behavior.

Consider adding a comment documenting that fauxqs resolves queues by path/name to make this dependency explicit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/sqs/test/consumers/SqsPermissionConsumer.startupResourcePolling.spec.ts`
at line 59, The hardcoded queueUrl constant (queueUrl) relies on an implicit
fauxqs behavior where queues are resolved by the path/name, which differs from
the SQS client endpoint configuration (endpoint: 'http://localhost:4566'); add a
concise inline comment next to the queueUrl definition in
SqsPermissionConsumer.startupResourcePolling.spec.ts explaining that fauxqs
matches by path/queue name (so exact host in the URL is not required), mention
this pattern is repeated across tests in packages/sqs and packages/sns, and
optionally include a one-line pointer to fauxqs docs or a note to change the URL
if the endpoint host changes to avoid future confusion.
packages/sns/test/consumers/SnsSqsPermissionConsumer.startupResourcePolling.spec.ts (1)

63-65: ⚠️ Potential issue | 🟡 Minor

Hardcoded queueUrl breaks fauxqs backend — use testAdmin.createQueue() return value

Line 65 hardcodes the LocalStack URL format (http://sqs.eu-west-1.localstack:4566/…). When QUEUE_BACKEND is fauxqs, testAdmin.createQueue() returns a different URL, so:

  1. locatorConfig.queueUrl points to the wrong endpoint — the consumer fails to locate the queue.
  2. expect(consumer.subscriptionProps.queueUrl).toBe(queueUrl) (e.g., lines 162, 260, 294, 327) will fail.

All testAdmin.createQueue(queueName) call-sites in this file ignore the returned URL, which already follows the correct pattern elsewhere (const { queueUrl } = await testAdmin.createQueue(queueName) in SnsPermissionPublisher.spec.ts L217 and SnsPermissionPublisher.multiStorePayloadOffloading.spec.ts L63).

🐛 Suggested fix pattern (apply to each call-site)
-  const queueUrl = `http://sqs.eu-west-1.localstack:4566/000000000000/${queueName}`
   ...
-  await testAdmin.createQueue(queueName)
+  const { queueUrl } = await testAdmin.createQueue(queueName)
   const consumer = new TestStartupResourcePollingConsumer(diContainer.cradle, {
     locatorConfig: {
       ...
       queueUrl,   // now backend-agnostic

Also applies to: 100-100, 156-156, 167-167, 192-192, 227-227, 270-270, 306-306, 332-332, 392-392, 549-549, 599-599

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/sns/test/consumers/SnsSqsPermissionConsumer.startupResourcePolling.spec.ts`
around lines 63 - 65, The tests hardcode queueUrl which breaks fauxqs; update
every testAdmin.createQueue(queueName) call in this file to capture and use its
returned URL (e.g., const { queueUrl } = await testAdmin.createQueue(queueName))
and replace usages of the hardcoded queueUrl variable and any assignments to
locatorConfig.queueUrl and expectations like
expect(consumer.subscriptionProps.queueUrl).toBe(queueUrl) to use the returned
queueUrl instead so the consumer locates the correct endpoint under both
LocalStack and fauxqs backends.
packages/sns/test/consumers/SnsSqsPermissionConsumer.spec.ts (1)

230-233: ⚠️ Potential issue | 🟠 Major

Fix SNS client configuration and extend testAdmin.createTopic to support tags.

These three tests call assertTopic(snsClient, stsClient, { Name: topicNome, Tags: [...] }) directly through HTTP, but the SNS client configuration has a critical gap: it lacks the fauxqs requestHandler that S3 and SQS use. This means HTTP SNS calls likely bypass fauxqs's HTTP handlers entirely.

Combined with the fact that testAdmin.createTopic() doesn't accept a tags parameter, there's no abstracted way to create tagged topics consistently. When beforeEach calls testAdmin.deleteTopics(), it uses the programmatic API (server.deleteTopic()), which may not find HTTP-created topics if fauxqs doesn't synchronize state between HTTP handlers and its in-memory store.

Required fix: Either:

  1. Add requestHandler: createLocalhostHandler() to the SNS client config (matching SQS/S3 pattern), then extend testAdmin.createTopic(name, opts?: { tags? }) to pass tags to server.createTopic(), OR
  2. Confirm that fauxqs SNS HTTP and programmatic APIs share state, then update the tests to use the extended testAdmin abstraction.

Also applies to: 268-271, 299-302

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/consumers/SnsSqsPermissionConsumer.spec.ts` around lines
230 - 233, The SNS tests create topics via HTTP using assertTopic(snsClient,
stsClient, { Name: topicNome, Tags: [...] }) but the snsClient is missing the
fauxqs requestHandler so HTTP requests bypass fauxqs handlers; add
requestHandler: createLocalhostHandler() to the SNS client configuration
(matching SQS/S3 clients) so HTTP calls hit fauxqs, and extend
testAdmin.createTopic(name, opts?: { tags?: Tag[] }) to forward tags to
server.createTopic(...) so tests can create tagged topics via the testAdmin
abstraction; alternatively, if you prefer keeping HTTP creation, ensure fauxqs
HTTP and programmatic stores are shared, but the recommended fix is to update
snsClient config and testAdmin.createTopic to accept and pass tags.
packages/sqs/test/consumers/SqsPermissionConsumer.spec.ts (1)

33-43: ⚠️ Potential issue | 🟠 Major

Hardcoded localstack queue URL may break under fauxqs backend.

Line 33 hardcodes http://sqs.eu-west-1.localstack:4566/000000000000/${queueName} which is a localstack-specific format. Since this PR aims to run tests against both fauxqs and localstack (per the CI matrix), this URL will likely differ when using fauxqs. Tests at Lines 71, 84, 115, 156, 208, 257, 288, 325, 391 all assert .toBe(queueUrl) against this hardcoded value.

Consider deriving the expected URL from testAdmin.createQueue(queueName) return value, or from the test AWS configuration, so it adapts to the active backend.

#!/bin/bash
# Check if there's a test config or fauxqs config that might produce matching URLs
echo "=== Check TEST_AWS_CONFIG for endpoint configuration ==="
rg -n 'TEST_AWS_CONFIG' --type=ts -A 10

echo "=== Check fauxqs endpoint/URL format ==="
rg -n 'fauxqs' --type=ts -C 3 -g '!node_modules'

echo "=== Check if queueUrl is hardcoded elsewhere ==="
rg -n 'sqs.eu-west-1.localstack' --type=ts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/consumers/SqsPermissionConsumer.spec.ts` around lines 33 -
43, The test hardcodes queueUrl (`const queueUrl =
`http://sqs.eu-west-1.localstack:4566/000000000000/${queueName}`) which breaks
when running against fauxqs; update the tests to derive the expected queue URL
dynamically (use the value returned by testAdmin.createQueue(queueName) or
obtain the endpoint/base URL from the test AWS configuration in the DI
container) and replace direct comparisons to queueUrl with that derived value;
locate usages around the spec (variables queueUrl, sqsClient, testAdmin and
assertions that call .toBe(queueUrl)) and ensure createQueue/deleteQueues calls
are awaited and their returned URL or config is used for assertions so the tests
work for both localstack and fauxqs.
🧹 Nitpick comments (12)
packages/gcp-pubsub/test/utils/gcsUtils.ts (1)

14-25: LGTM — optional: parallelize bucket cleanup

The logic is correct. The continue guard, per-bucket existence check, and ignoreNotFound on individual deletes are all appropriate for a test utility.

If this ends up used with many buckets, consider flattening to a single Promise.all to run bucket cleanup concurrently:

♻️ Optional parallel refactor
-export async function emptyBuckets(storage: Storage, ...bucketNames: string[]) {
-  for (const bucketName of bucketNames) {
-    const bucket = storage.bucket(bucketName)
-    const [exists] = await bucket.exists()
-
-    if (!exists) {
-      continue
-    }
-
-    const [files] = await bucket.getFiles()
-    await Promise.all(files.map((file) => file.delete({ ignoreNotFound: true })))
-  }
-}
+export async function emptyBuckets(storage: Storage, ...bucketNames: string[]) {
+  await Promise.all(
+    bucketNames.map(async (bucketName) => {
+      const bucket = storage.bucket(bucketName)
+      const [exists] = await bucket.exists()
+      if (!exists) return
+      const [files] = await bucket.getFiles()
+      await Promise.all(files.map((file) => file.delete({ ignoreNotFound: true })))
+    }),
+  )
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/gcp-pubsub/test/utils/gcsUtils.ts` around lines 14 - 25, The current
emptyBuckets function sequentially processes each bucket; to parallelize, map
over bucketNames and create an async cleanup promise per bucket that calls
storage.bucket(bucketName), awaits bucket.exists(), short-circuits if not
exists, then awaits bucket.getFiles() and Promise.all(files.map(f => f.delete({
ignoreNotFound: true }))); finally return Promise.all on that array of bucket
cleanup promises. Update emptyBuckets to return that single Promise so all
bucket deletions run concurrently while preserving the existing checks
(bucket.exists, bucket.getFiles, file.delete with ignoreNotFound).
packages/sqs/lib/fakes/TestSqsPublisher.spec.ts (1)

11-11: deleteQueue in beforeEach bypasses the Fauxqs-aware testAdmin.deleteQueues() abstraction

Lines 30-31 call deleteQueue(sqsClient, ...) directly while the rest of the provisioning was migrated to go through testAdmin (which routes to server.deleteQueue for Fauxqs vs. the SQS SDK for LocalStack). If Fauxqs requires routing deletions through the server handle rather than via the SQS-protocol endpoint, these teardown calls will silently no-op or error under QUEUE_BACKEND=fauxqs, leaving stale queues between test runs.

♻️ Suggested fix
-    await deleteQueue(sqsClient, queueName)
-    await deleteQueue(sqsClient, fifoQueueName)
+    await testAdmin.deleteQueues(queueName, fifoQueueName)

Also applies to: 30-31

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/lib/fakes/TestSqsPublisher.spec.ts` at line 11, The beforeEach
teardown currently calls deleteQueue(sqsClient, ...) which bypasses the
Fauxqs-aware abstraction; replace those direct deleteQueue calls with the
testAdmin.deleteQueues(...) call used elsewhere so teardown routes through
server.deleteQueue for QUEUE_BACKEND=fauxqs; update the TestSqsPublisher.spec.ts
beforeEach to invoke testAdmin.deleteQueues with the same queue names/urls (and
remove or stop importing deleteQueue) so teardown behaves correctly under both
Fauxqs and LocalStack.
packages/sqs/test/utils/s3Utils.ts (2)

15-30: emptyBuckets is duplicated verbatim across packages/sns and packages/sqs

The implementation at lines 15–30 is byte-for-byte identical to packages/sns/test/utils/s3Utils.ts lines 15–30. Consider extracting it to a shared test-utilities package to keep both copies in sync.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/s3Utils.ts` around lines 15 - 30, The function
emptyBuckets is duplicated verbatim; extract this helper into a single shared
test utilities module and have both packages import it instead of maintaining
separate copies: create a shared export (e.g., export async function
emptyBuckets(s3: S3, ...bucketNames: string[])) in the common test-utils
package, move the current implementation (including the try/catch that checks
NoSuchBucket) there, update both packages to import that symbol instead of their
local versions, and remove the duplicated definitions so only the shared
implementation remains.

18-18: Use listObjectsV2 instead of listObjects

ListObjects is deprecated by AWS; use ListObjectsV2 for new development. While the current 1,000-object limit is acceptable for test buckets in practice, listObjectsV2 with proper pagination (IsTruncated checking) would be more robust if bucket sizes grow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/s3Utils.ts` at line 18, Replace the deprecated
s3.listObjects call with s3.listObjectsV2 in the helper that currently calls
"const objects = await s3.listObjects({ Bucket: bucketName })", and implement
proper pagination: call listObjectsV2 with Bucket and optional
ContinuationToken, loop while IsTruncated is true, accumulate returned Contents
into a single array, and pass the accumulated list to the rest of the function;
ensure you reference/return the same variable names (e.g., objects or contents)
so callers of the helper (in s3Utils.ts) continue to work.
packages/sns/test/publishers/SnsPermissionPublisher.payloadOffloading.spec.ts (1)

37-55: testAdmin wiring is correct; consider adding emptyBuckets cleanup in afterAll.

The TestAwsResourceAdmin exposes an emptyBuckets method, and createBucket is called in beforeAll with no corresponding cleanup in afterAll. For Fauxqs this is harmless (server teardown cleans up), but for LocalStack, offloaded payload objects accumulate in s3BucketName across test runs. Adding an explicit cleanup keeps both backends symmetric:

🧹 Suggested `afterAll` addition
 afterAll(async () => {
+  await testAdmin.emptyBuckets(s3BucketName)
   const { awilixManager } = diContainer.cradle
   await awilixManager.executeDispose()
   await diContainer.dispose()
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/sns/test/publishers/SnsPermissionPublisher.payloadOffloading.spec.ts`
around lines 37 - 55, The test creates an S3 bucket via TestAwsResourceAdmin in
beforeAll but doesn't clean it up; add an afterAll that calls
testAdmin.emptyBuckets(s3BucketName) (or the appropriate emptyBuckets method on
the TestAwsResourceAdmin instance named testAdmin) to remove offloaded payload
objects after tests complete so LocalStack runs don't accumulate data; place
this cleanup in the same test file alongside the existing beforeAll/afterAll
hooks.
packages/sns/test/utils/fauxqsInstance.ts (2)

1-21: fauxqsInstance.ts is duplicated verbatim from packages/sqs/test/utils/fauxqsInstance.ts.

Both files are identical (isLocalstack flag, server singleton, ensureFauxqsServer, getFauxqsServer). The duplication will drift. Consider extracting a shared test-utils package in the monorepo, or having SNS import the SQS utility directly if a package-level dependency is acceptable for test utilities.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/utils/fauxqsInstance.ts` around lines 1 - 21, The
fauxqsInstance.ts file is a verbatim duplicate; consolidate the test utility by
removing duplication and having SNS tests reuse the single implementation:
either move the shared logic (isLocalstack, server singleton,
ensureFauxqsServer, getFauxqsServer) into a common test-utils package in the
monorepo and update imports, or have the SNS package import the existing
implementation from packages/sqs/test/utils/fauxqsInstance.ts (add a test-only
dependency if needed); update any import paths in SNS tests and delete the
duplicate file so there is a single source of truth.

17-21: getFauxqsServer() logs on every call — consider removing or guarding the log.

This function is called during DI container setup (once per test file), so it will emit a log for every test file in the suite. Given that ensureFauxqsServer() already logs state transitions, the per-call log in getFauxqsServer adds limited signal. The same pattern exists in packages/sqs/test/utils/fauxqsInstance.ts.

♻️ Suggested simplification
 export function getFauxqsServer(): FauxqsServer | undefined {
-  console.log(
-    `[fauxqs] getFauxqsServer: ${server ? 'instance available' : 'undefined (localstack mode)'}`,
-  )
   return server
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/utils/fauxqsInstance.ts` around lines 17 - 21, Remove the
unconditional console.log in getFauxqsServer (which currently logs server state
on every call) and either delete the log entirely or guard it behind a
debug/verbose flag (e.g., if (process.env.DEBUG_FAUXQS) { ... }) or replace it
with a logger that respects log levels; update both getFauxqsServer
implementations that reference the server variable and ensureFauxqsServer so
normal test runs are quiet while detailed logging can be enabled when needed.
packages/sns/test/utils/testAdmin.ts (1)

10-100: TestAwsResourceAdmin is largely duplicated from packages/sqs/test/utils/testAdmin.ts.

createQueue, deleteQueues, createBucket, and emptyBuckets are byte-for-byte identical between the SNS and SQS versions. The SNS variant simply adds createTopic/deleteTopics and makes snsClient/stsClient required. Consider extracting a shared base class (e.g. in a monorepo-level test-utils package, or by importing the SQS class as a base) so queue/bucket logic lives in one place.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/utils/testAdmin.ts` around lines 10 - 100,
TestAwsResourceAdmin duplicates SQS test admin logic; extract the shared
queue/bucket behavior into a common base class (or move into a monorepo
test-utils module) and have the SNS TestAwsResourceAdmin extend/use it.
Specifically, factor out the duplicated methods createQueue, deleteQueues,
createBucket, and emptyBuckets into a BaseTestAwsResourceAdmin (or shared
helper) that accepts server, sqsClient, s3, and region, then update the SNS
TestAwsResourceAdmin to keep its SNS-specific fields (snsClient, stsClient) and
implement createTopic/deleteTopics while delegating queue/bucket calls to the
new base/shared implementation.
packages/sns/test/utils/snsSubscriber.spec.ts (1)

34-41: Optional: move AWS resource cleanup before container disposal

deleteQueues / deleteTopics are called after both awilixManager.executeDispose() and diContainer.dispose(). This works today because the SQS/SNS clients have no dispose hooks, but it inverts the safer pattern used elsewhere in the suite (e.g., startupResourcePolling.spec.ts cleans up in beforeEach before disposal). If a dispose hook is ever added to these clients, cleanup would silently fail.

♻️ Suggested reorder
  afterEach(async () => {
+   await testAdmin.deleteQueues(QUEUE_NAME)
+   await testAdmin.deleteTopics(TOPIC_NAME)
    const { awilixManager } = diContainer.cradle
    await awilixManager.executeDispose()
    await diContainer.dispose()
-
-   await testAdmin.deleteQueues(QUEUE_NAME)
-   await testAdmin.deleteTopics(TOPIC_NAME)
  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/utils/snsSubscriber.spec.ts` around lines 34 - 41, Move the
AWS resource cleanup (calls to testAdmin.deleteQueues(QUEUE_NAME) and
testAdmin.deleteTopics(TOPIC_NAME)) to run before container disposal so they
execute while the SNS/SQS clients are still available; specifically, in the
afterEach in snsSubscriber.spec.ts, call testAdmin.deleteQueues and
testAdmin.deleteTopics before invoking awilixManager.executeDispose() and
diContainer.dispose(). Keep the same error handling/await semantics and ensure
the cleanup order mirrors startupResourcePolling.spec.ts so future dispose hooks
on the clients don't prevent deleting AWS resources.
packages/sqs/test/utils/fauxqsInstance.ts (2)

1-22: fauxqsInstance.ts is identical to packages/sns/test/utils/fauxqsInstance.ts — extract to a shared helper.

Both the SQS and SNS packages contain byte-for-byte duplicate implementations of ensureFauxqsServer / getFauxqsServer. Any future change (port, host, logger config) must be applied twice. Consider moving these to a shared internal package (e.g. packages/test-utils) or at minimum a shared workspace helper.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/fauxqsInstance.ts` around lines 1 - 22, The
fauxqsInstance.ts functions ensureFauxqsServer and getFauxqsServer (and the
shared server variable and startFauxqs import) are duplicated across packages;
extract them into a single shared test utility module (e.g.,
packages/test-utils) and have both SQS and SNS tests import that module. Move
the constants (isLocalstack), the server variable, and the functions
ensureFauxqsServer and getFauxqsServer plus the startFauxqs import into the new
shared file, update imports in packages/sqs/test/utils/fauxqsInstance.ts and
packages/sns/test/utils/fauxqsInstance.ts to re-export or forward to the shared
helper, and ensure behavior (port 4566, host 'localstack', logger:false and
console logs) remains identical after the change.

17-21: getFauxqsServer() logs on every call — can be noisy with beforeEach-scoped containers.

In test files that call registerDependencies() inside beforeEach (e.g. SqsEventBridgeConsumer.spec.ts), a new DI container is created per test. Although testAdmin uses SINGLETON_CONFIG within a single container, getFauxqsServer() is invoked once per container, emitting a log line for every test. At test-suite scale this adds significant log noise.

Consider either removing this log (the startup log in ensureFauxqsServer already covers the "server is running" signal) or downgrading to a debug-level logger.

✨ Proposed change
 export function getFauxqsServer(): FauxqsServer | undefined {
-  console.log(
-    `[fauxqs] getFauxqsServer: ${server ? 'instance available' : 'undefined (localstack mode)'}`,
-  )
   return server
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/fauxqsInstance.ts` around lines 17 - 21,
getFauxqsServer() currently emits a console.log on every call, causing noisy
test output when DI containers are recreated; remove the console.log line (or
replace it with a debug-level log call) inside getFauxqsServer so it no longer
logs on each invocation, and rely on the existing startup log in
ensureFauxqsServer for the server-running signal; locate the console.log in the
getFauxqsServer function and either delete it or change it to a debug call
(e.g., processLogger.debug or a configured logger) to suppress test noise.
packages/sqs/test/utils/testAdmin.ts (1)

10-32: Unused snsClient and stsClient fields.

These fields are stored but never referenced by any method. If they're planned for future SNS topic/STS operations, consider deferring their addition until actually needed (YAGNI), or add a brief comment indicating intent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/testAdmin.ts` around lines 10 - 32, The
TestAwsResourceAdmin class stores unused fields snsClient and stsClient; remove
these unused properties (snsClient and stsClient) and their assignments in the
constructor, or if you intend to keep them, add a brief TODO comment above the
private declarations indicating planned SNS/STS usage; update the constructor
parameter list to remove the corresponding opts.snsClient and opts.stsClient
entries (or keep them but document intent) and ensure no other code references
TestAwsResourceAdmin.snsClient or .stsClient so the class no longer contains
dead members.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/sns/test/utils/s3Utils.ts`:
- Around line 25-27: The catch block currently swallows all exceptions because
the body for "if (e instanceof NoSuchBucket) {}" is empty and non-NoSuchBucket
errors are not rethrown; update the catch to only suppress NoSuchBucket and
rethrow any other error (e.g., change to "if (e instanceof NoSuchBucket) { /*
ignore */ } else { throw e }" or equivalently "if (!(e instanceof NoSuchBucket))
throw e") so that errors like AccessDenied or network failures are propagated;
locate the catch in packages/sns/test/utils/s3Utils.ts around the S3 operation
that references NoSuchBucket and implement the rethrow logic there.

In `@packages/sns/test/utils/testAdmin.ts`:
- Around line 84-100: The createBucket and emptyBuckets methods use a non-null
assertion on this.s3 (calling assertBucket(this.s3!, name) and
emptyBuckets(this.s3!, ...names)) which will throw if the admin was constructed
without an S3 instance; add an explicit runtime guard that checks for this.s3
before calling the helper functions and throw a clear error or return when
missing (e.g., in TestAwsResourceAdmin.createBucket and
TestAwsResourceAdmin.emptyBuckets, after the server branch, if (!this.s3) throw
new Error("S3 client required for non-server operations") or similar) so callers
get a clear message instead of a TypeError; keep references to assertBucket and
emptyBuckets helper calls intact.

In
`@packages/sqs/test/publishers/SqsPermissionPublisher.multiStorePayloadOffloading.spec.ts`:
- Around line 48-49: The tests create S3 buckets via testAdmin.createBucket
(s3BucketNameStore1, s3BucketNameStore2) in beforeAll but never clean them up;
add an afterAll that calls testAdmin.emptyBuckets (or
testAdmin.emptyBuckets([s3BucketNameStore1, s3BucketNameStore2])) to remove
contents and avoid accumulation when QUEUE_BACKEND=localstack — apply the same
cleanup for the other bucket-creation block referenced around the second
occurrence (lines ~74-78) so both test setups call testAdmin.emptyBuckets in
their respective afterAll hooks.

In `@packages/sqs/test/utils/s3Utils.ts`:
- Around line 25-27: In the catch block that currently reads "catch (e) { if (e
instanceof NoSuchBucket) { }", change the logic so only NoSuchBucket is
swallowed and all other errors are rethrown: detect NoSuchBucket and ignore it,
otherwise throw the caught error (e) so AccessDenied/throttling/other failures
are surfaced; update the catch in the function surrounding the S3 bucket
teardown (the try/catch handling bucket deletion) to follow this behavior.

In `@packages/sqs/test/utils/testAdmin.ts`:
- Around line 64-70: createBucket (and likewise emptyBuckets) currently uses a
non-null assertion on this.s3 which will throw if neither server nor s3 were
provided; add an early guard in createBucket (and emptyBuckets) that checks if
this.server is falsy and this.s3 is falsy, and in that case throw or return a
clear, descriptive error (e.g., "S3 client not available: provide server or s3")
instead of using this.s3! and calling assertBucket; reference the methods
createBucket and emptyBuckets and the helper assertBucket when adding the guard.
- Around line 52-62: The server-side calls in deleteQueues, createBucket, and
emptyBuckets are invoked fire-and-forget; update deleteQueues to await each
this.server.deleteQueue(...) (or collect and await Promise.all over queueNames),
update createBucket to await this.server.createBucket(...), and update
emptyBuckets to await each this.server.emptyBucket(...) (or await Promise.all)
so the async server operations complete before the test cleanup proceeds; refer
to the deleteQueues, createBucket, and emptyBuckets functions and their
this.server.deleteQueue(), this.server.createBucket(), this.server.emptyBucket()
calls to apply the changes.

In `@packages/sqs/test/utils/testAwsConfig.ts`:
- Around line 26-36: The top-level await import('fauxqs') that assigns
createLocalhostHandler into s3Config runs at module evaluation and can fail if
the dev dependency is absent; change this to a lazy dynamic import by moving the
import into a function that constructs/returns the s3Config at call-time (e.g.,
add an async function like buildLocalS3Config or getS3Config that does const {
createLocalhostHandler } = await import('fauxqs') and then returns the s3Config
object with requestHandler: createLocalhostHandler()); update callers to use
that function so createLocalhostHandler is only imported when needed and not
during module initialization, keeping the symbol names createLocalhostHandler
and s3Config for easy location.

In `@turbo.json`:
- Line 12: Parallel test execution causes both packages to call startFauxqs({
port: 4566, host: 'localstack' }) and race to bind the same port; fix by either
(A) assigning per-package ports and updating client endpoints (e.g., change
startFauxqs call in packages/sqs and packages/sns to different port numbers and
update any client initialization that relies on QUEUE_BACKEND/localstack
endpoint) or (B) make turbo run "test:coverage" sequential when fauxqs is used
by adding a dependsOn guard in turbo.json so packages run one after another when
QUEUE_BACKEND=localstack; locate and update the startFauxqs calls in your
vitest.setup.ts/globalSetup and the turbo.json "test:coverage" entry
accordingly.

---

Outside diff comments:
In `@packages/sns/test/consumers/SnsSqsPermissionConsumer.spec.ts`:
- Around line 230-233: The SNS tests create topics via HTTP using
assertTopic(snsClient, stsClient, { Name: topicNome, Tags: [...] }) but the
snsClient is missing the fauxqs requestHandler so HTTP requests bypass fauxqs
handlers; add requestHandler: createLocalhostHandler() to the SNS client
configuration (matching SQS/S3 clients) so HTTP calls hit fauxqs, and extend
testAdmin.createTopic(name, opts?: { tags?: Tag[] }) to forward tags to
server.createTopic(...) so tests can create tagged topics via the testAdmin
abstraction; alternatively, if you prefer keeping HTTP creation, ensure fauxqs
HTTP and programmatic stores are shared, but the recommended fix is to update
snsClient config and testAdmin.createTopic to accept and pass tags.

In
`@packages/sns/test/consumers/SnsSqsPermissionConsumer.startupResourcePolling.spec.ts`:
- Around line 63-65: The tests hardcode queueUrl which breaks fauxqs; update
every testAdmin.createQueue(queueName) call in this file to capture and use its
returned URL (e.g., const { queueUrl } = await testAdmin.createQueue(queueName))
and replace usages of the hardcoded queueUrl variable and any assignments to
locatorConfig.queueUrl and expectations like
expect(consumer.subscriptionProps.queueUrl).toBe(queueUrl) to use the returned
queueUrl instead so the consumer locates the correct endpoint under both
LocalStack and fauxqs backends.

In `@packages/sqs/lib/fakes/TestSqsPublisher.spec.ts`:
- Around line 16-18: The tests currently hardcode queueUrl and fifoQueueUrl
strings which embed a LocalStack hostname/account and break when
QUEUE_BACKEND=fauxqs; update TestSqsPublisher.spec.ts to resolve queue URLs
dynamically instead of constructing them: after creating queues via
testAdmin.createQueue (or by calling GetQueueUrlCommand) capture and use the
returned queueUrl for publishing and verification, and replace all usages of the
queueUrl and fifoQueueUrl constants with those dynamic values so tests work for
both LocalStack and Fauxqs backends.

In `@packages/sqs/test/consumers/SqsPermissionConsumer.spec.ts`:
- Around line 33-43: The test hardcodes queueUrl (`const queueUrl =
`http://sqs.eu-west-1.localstack:4566/000000000000/${queueName}`) which breaks
when running against fauxqs; update the tests to derive the expected queue URL
dynamically (use the value returned by testAdmin.createQueue(queueName) or
obtain the endpoint/base URL from the test AWS configuration in the DI
container) and replace direct comparisons to queueUrl with that derived value;
locate usages around the spec (variables queueUrl, sqsClient, testAdmin and
assertions that call .toBe(queueUrl)) and ensure createQueue/deleteQueues calls
are awaited and their returned URL or config is used for assertions so the tests
work for both localstack and fauxqs.

In
`@packages/sqs/test/consumers/SqsPermissionConsumer.startupResourcePolling.spec.ts`:
- Line 59: The hardcoded queueUrl constant (queueUrl) relies on an implicit
fauxqs behavior where queues are resolved by the path/name, which differs from
the SQS client endpoint configuration (endpoint: 'http://localhost:4566'); add a
concise inline comment next to the queueUrl definition in
SqsPermissionConsumer.startupResourcePolling.spec.ts explaining that fauxqs
matches by path/queue name (so exact host in the URL is not required), mention
this pattern is repeated across tests in packages/sqs and packages/sns, and
optionally include a one-line pointer to fauxqs docs or a note to change the URL
if the endpoint host changes to avoid future confusion.

In `@packages/sqs/test/publishers/SqsPermissionPublisher.fifo.spec.ts`:
- Around line 275-332: The test creates an S3 bucket
('fifo-payload-offloading-test-bucket') via testAdmin.createBucket but never
cleans it up; remove the local const s3BucketName from the test and add a
cleanup call (testAdmin.emptyBuckets or equivalent) in the suite's afterEach so
the offloaded objects uploaded by the S3PayloadStore (used in payloadStoreConfig
and SqsPermissionPublisherFifo) are removed between runs; ensure the afterEach
references the same bucket name used when creating the bucket or empties all
test buckets.

---

Duplicate comments:
In `@packages/sqs/test/publishers/SqsPermissionPublisher.spec.ts`:
- Around line 29-34: Tests hardcode the Localstack URL and assert against that
literal (seen around assertions referencing the hardcoded format); update the
tests to derive the expected Localstack endpoint dynamically instead of using
the literal. In SqsPermissionPublisher.spec.ts use an existing runtime value
(for example diContainer.cradle.testAdmin or diContainer.cradle.sqsClient) to
build the expected URL—e.g. call testAdmin.getEndpoint() or read
sqsClient.config.endpoint or an env var the test harness provides—and replace
all assertions that compare to the hardcoded URL (the comparisons around
queueName and the listed assertion locations) to use that derived value so the
tests work under fauxqs/localstack configurations.

---

Nitpick comments:
In `@packages/gcp-pubsub/test/utils/gcsUtils.ts`:
- Around line 14-25: The current emptyBuckets function sequentially processes
each bucket; to parallelize, map over bucketNames and create an async cleanup
promise per bucket that calls storage.bucket(bucketName), awaits
bucket.exists(), short-circuits if not exists, then awaits bucket.getFiles() and
Promise.all(files.map(f => f.delete({ ignoreNotFound: true }))); finally return
Promise.all on that array of bucket cleanup promises. Update emptyBuckets to
return that single Promise so all bucket deletions run concurrently while
preserving the existing checks (bucket.exists, bucket.getFiles, file.delete with
ignoreNotFound).

In
`@packages/sns/test/publishers/SnsPermissionPublisher.payloadOffloading.spec.ts`:
- Around line 37-55: The test creates an S3 bucket via TestAwsResourceAdmin in
beforeAll but doesn't clean it up; add an afterAll that calls
testAdmin.emptyBuckets(s3BucketName) (or the appropriate emptyBuckets method on
the TestAwsResourceAdmin instance named testAdmin) to remove offloaded payload
objects after tests complete so LocalStack runs don't accumulate data; place
this cleanup in the same test file alongside the existing beforeAll/afterAll
hooks.

In `@packages/sns/test/utils/fauxqsInstance.ts`:
- Around line 1-21: The fauxqsInstance.ts file is a verbatim duplicate;
consolidate the test utility by removing duplication and having SNS tests reuse
the single implementation: either move the shared logic (isLocalstack, server
singleton, ensureFauxqsServer, getFauxqsServer) into a common test-utils package
in the monorepo and update imports, or have the SNS package import the existing
implementation from packages/sqs/test/utils/fauxqsInstance.ts (add a test-only
dependency if needed); update any import paths in SNS tests and delete the
duplicate file so there is a single source of truth.
- Around line 17-21: Remove the unconditional console.log in getFauxqsServer
(which currently logs server state on every call) and either delete the log
entirely or guard it behind a debug/verbose flag (e.g., if
(process.env.DEBUG_FAUXQS) { ... }) or replace it with a logger that respects
log levels; update both getFauxqsServer implementations that reference the
server variable and ensureFauxqsServer so normal test runs are quiet while
detailed logging can be enabled when needed.

In `@packages/sns/test/utils/snsSubscriber.spec.ts`:
- Around line 34-41: Move the AWS resource cleanup (calls to
testAdmin.deleteQueues(QUEUE_NAME) and testAdmin.deleteTopics(TOPIC_NAME)) to
run before container disposal so they execute while the SNS/SQS clients are
still available; specifically, in the afterEach in snsSubscriber.spec.ts, call
testAdmin.deleteQueues and testAdmin.deleteTopics before invoking
awilixManager.executeDispose() and diContainer.dispose(). Keep the same error
handling/await semantics and ensure the cleanup order mirrors
startupResourcePolling.spec.ts so future dispose hooks on the clients don't
prevent deleting AWS resources.

In `@packages/sns/test/utils/testAdmin.ts`:
- Around line 10-100: TestAwsResourceAdmin duplicates SQS test admin logic;
extract the shared queue/bucket behavior into a common base class (or move into
a monorepo test-utils module) and have the SNS TestAwsResourceAdmin extend/use
it. Specifically, factor out the duplicated methods createQueue, deleteQueues,
createBucket, and emptyBuckets into a BaseTestAwsResourceAdmin (or shared
helper) that accepts server, sqsClient, s3, and region, then update the SNS
TestAwsResourceAdmin to keep its SNS-specific fields (snsClient, stsClient) and
implement createTopic/deleteTopics while delegating queue/bucket calls to the
new base/shared implementation.

In `@packages/sqs/lib/fakes/TestSqsPublisher.spec.ts`:
- Line 11: The beforeEach teardown currently calls deleteQueue(sqsClient, ...)
which bypasses the Fauxqs-aware abstraction; replace those direct deleteQueue
calls with the testAdmin.deleteQueues(...) call used elsewhere so teardown
routes through server.deleteQueue for QUEUE_BACKEND=fauxqs; update the
TestSqsPublisher.spec.ts beforeEach to invoke testAdmin.deleteQueues with the
same queue names/urls (and remove or stop importing deleteQueue) so teardown
behaves correctly under both Fauxqs and LocalStack.

In `@packages/sqs/test/utils/fauxqsInstance.ts`:
- Around line 1-22: The fauxqsInstance.ts functions ensureFauxqsServer and
getFauxqsServer (and the shared server variable and startFauxqs import) are
duplicated across packages; extract them into a single shared test utility
module (e.g., packages/test-utils) and have both SQS and SNS tests import that
module. Move the constants (isLocalstack), the server variable, and the
functions ensureFauxqsServer and getFauxqsServer plus the startFauxqs import
into the new shared file, update imports in
packages/sqs/test/utils/fauxqsInstance.ts and
packages/sns/test/utils/fauxqsInstance.ts to re-export or forward to the shared
helper, and ensure behavior (port 4566, host 'localstack', logger:false and
console logs) remains identical after the change.
- Around line 17-21: getFauxqsServer() currently emits a console.log on every
call, causing noisy test output when DI containers are recreated; remove the
console.log line (or replace it with a debug-level log call) inside
getFauxqsServer so it no longer logs on each invocation, and rely on the
existing startup log in ensureFauxqsServer for the server-running signal; locate
the console.log in the getFauxqsServer function and either delete it or change
it to a debug call (e.g., processLogger.debug or a configured logger) to
suppress test noise.

In `@packages/sqs/test/utils/s3Utils.ts`:
- Around line 15-30: The function emptyBuckets is duplicated verbatim; extract
this helper into a single shared test utilities module and have both packages
import it instead of maintaining separate copies: create a shared export (e.g.,
export async function emptyBuckets(s3: S3, ...bucketNames: string[])) in the
common test-utils package, move the current implementation (including the
try/catch that checks NoSuchBucket) there, update both packages to import that
symbol instead of their local versions, and remove the duplicated definitions so
only the shared implementation remains.
- Line 18: Replace the deprecated s3.listObjects call with s3.listObjectsV2 in
the helper that currently calls "const objects = await s3.listObjects({ Bucket:
bucketName })", and implement proper pagination: call listObjectsV2 with Bucket
and optional ContinuationToken, loop while IsTruncated is true, accumulate
returned Contents into a single array, and pass the accumulated list to the rest
of the function; ensure you reference/return the same variable names (e.g.,
objects or contents) so callers of the helper (in s3Utils.ts) continue to work.

In `@packages/sqs/test/utils/testAdmin.ts`:
- Around line 10-32: The TestAwsResourceAdmin class stores unused fields
snsClient and stsClient; remove these unused properties (snsClient and
stsClient) and their assignments in the constructor, or if you intend to keep
them, add a brief TODO comment above the private declarations indicating planned
SNS/STS usage; update the constructor parameter list to remove the corresponding
opts.snsClient and opts.stsClient entries (or keep them but document intent) and
ensure no other code references TestAwsResourceAdmin.snsClient or .stsClient so
the class no longer contains dead members.

Comment on lines +25 to +27
} catch (e) {
if (e instanceof NoSuchBucket) {
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Non-NoSuchBucket errors are silently swallowed

The catch block catches all exceptions, but the if (e instanceof NoSuchBucket) {} body is empty and there is no else/throw. Any error that is not NoSuchBucket (e.g., AccessDenied, network failure) falls through the if and exits the catch block without being rethrown — silently lost. The intent is clearly to only ignore NoSuchBucket, but the condition is inverted.

🐛 Proposed fix
-    } catch (e) {
-      if (e instanceof NoSuchBucket) {
-      }
-    }
+    } catch (e) {
+      if (!(e instanceof NoSuchBucket)) {
+        throw e
+      }
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (e) {
if (e instanceof NoSuchBucket) {
}
} catch (e) {
if (!(e instanceof NoSuchBucket)) {
throw e
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/utils/s3Utils.ts` around lines 25 - 27, The catch block
currently swallows all exceptions because the body for "if (e instanceof
NoSuchBucket) {}" is empty and non-NoSuchBucket errors are not rethrown; update
the catch to only suppress NoSuchBucket and rethrow any other error (e.g.,
change to "if (e instanceof NoSuchBucket) { /* ignore */ } else { throw e }" or
equivalently "if (!(e instanceof NoSuchBucket)) throw e") so that errors like
AccessDenied or network failures are propagated; locate the catch in
packages/sns/test/utils/s3Utils.ts around the S3 operation that references
NoSuchBucket and implement the rethrow logic there.

Comment on lines +84 to +100
async createBucket(name: string) {
if (this.server) {
this.server.createBucket(name)
return
}
return await assertBucket(this.s3!, name)
}

async emptyBuckets(...names: string[]) {
if (this.server) {
for (const name of names) {
this.server.emptyBucket(name)
}
return
}
return await emptyBuckets(this.s3!, ...names)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

this.s3! will throw at runtime if s3 was not provided and the localstack path is taken.

s3 is declared optional (s3?: S3) in the constructor. In createBucket and emptyBuckets, the non-server branch calls assertBucket(this.s3!, name) / emptyBuckets(this.s3!, ...). If a caller constructs TestAwsResourceAdmin without an S3 instance and later exercises bucket operations against localstack, this will be a runtime TypeError that TypeScript's non-null assertion hides.

🛡️ Add an explicit guard
  async createBucket(name: string) {
    if (this.server) {
      this.server.createBucket(name)
      return
    }
+   if (!this.s3) throw new Error('TestAwsResourceAdmin: s3 client is required for localstack bucket operations')
    return await assertBucket(this.s3!, name)
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async createBucket(name: string) {
if (this.server) {
this.server.createBucket(name)
return
}
return await assertBucket(this.s3!, name)
}
async emptyBuckets(...names: string[]) {
if (this.server) {
for (const name of names) {
this.server.emptyBucket(name)
}
return
}
return await emptyBuckets(this.s3!, ...names)
}
async createBucket(name: string) {
if (this.server) {
this.server.createBucket(name)
return
}
if (!this.s3) throw new Error('TestAwsResourceAdmin: s3 client is required for localstack bucket operations')
return await assertBucket(this.s3!, name)
}
async emptyBuckets(...names: string[]) {
if (this.server) {
for (const name of names) {
this.server.emptyBucket(name)
}
return
}
return await emptyBuckets(this.s3!, ...names)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sns/test/utils/testAdmin.ts` around lines 84 - 100, The createBucket
and emptyBuckets methods use a non-null assertion on this.s3 (calling
assertBucket(this.s3!, name) and emptyBuckets(this.s3!, ...names)) which will
throw if the admin was constructed without an S3 instance; add an explicit
runtime guard that checks for this.s3 before calling the helper functions and
throw a clear error or return when missing (e.g., in
TestAwsResourceAdmin.createBucket and TestAwsResourceAdmin.emptyBuckets, after
the server branch, if (!this.s3) throw new Error("S3 client required for
non-server operations") or similar) so callers get a clear message instead of a
TypeError; keep references to assertBucket and emptyBuckets helper calls intact.

Comment on lines +48 to +49
await testAdmin.createBucket(s3BucketNameStore1)
await testAdmin.createBucket(s3BucketNameStore2)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

S3 buckets are created in beforeAll but never cleaned up in afterAll.

With fauxqs (in-memory, process-scoped) this is harmless, but when QUEUE_BACKEND=localstack the bucket contents accumulate across test runs. testAdmin.emptyBuckets already exists for exactly this purpose.

🧹 Proposed cleanup in `afterAll`
 afterAll(async () => {
+  await testAdmin.emptyBuckets(s3BucketNameStore1, s3BucketNameStore2)
   const { awilixManager } = diContainer.cradle
   await awilixManager.executeDispose()
   await diContainer.dispose()
 })

Also applies to: 74-78

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/sqs/test/publishers/SqsPermissionPublisher.multiStorePayloadOffloading.spec.ts`
around lines 48 - 49, The tests create S3 buckets via testAdmin.createBucket
(s3BucketNameStore1, s3BucketNameStore2) in beforeAll but never clean them up;
add an afterAll that calls testAdmin.emptyBuckets (or
testAdmin.emptyBuckets([s3BucketNameStore1, s3BucketNameStore2])) to remove
contents and avoid accumulation when QUEUE_BACKEND=localstack — apply the same
cleanup for the other bucket-creation block referenced around the second
occurrence (lines ~74-78) so both test setups call testAdmin.emptyBuckets in
their respective afterAll hooks.

Comment on lines +25 to +27
} catch (e) {
if (e instanceof NoSuchBucket) {
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Non-NoSuchBucket errors are silently swallowed — same as packages/sns/test/utils/s3Utils.ts

Same inverted catch-logic: all errors that are not NoSuchBucket silently exit the catch without being rethrown, masking real failures (e.g., AccessDenied, throttling) and leaving buckets dirty.

🐛 Proposed fix
-    } catch (e) {
-      if (e instanceof NoSuchBucket) {
-      }
-    }
+    } catch (e) {
+      if (!(e instanceof NoSuchBucket)) {
+        throw e
+      }
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (e) {
if (e instanceof NoSuchBucket) {
}
} catch (e) {
if (!(e instanceof NoSuchBucket)) {
throw e
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/s3Utils.ts` around lines 25 - 27, In the catch block
that currently reads "catch (e) { if (e instanceof NoSuchBucket) { }", change
the logic so only NoSuchBucket is swallowed and all other errors are rethrown:
detect NoSuchBucket and ignore it, otherwise throw the caught error (e) so
AccessDenied/throttling/other failures are surfaced; update the catch in the
function surrounding the S3 bucket teardown (the try/catch handling bucket
deletion) to follow this behavior.

Comment on lines +52 to +62
async deleteQueues(...queueNames: string[]) {
if (this.server) {
for (const name of queueNames) {
this.server.deleteQueue(name, { region: this.region })
}
return
}
for (const name of queueNames) {
await deleteQueue(this.sqsClient, name)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's look at the test file mentioned in the comment
echo "=== Content of testAdmin.ts (lines 52-62) ==="
sed -n '52,62p' packages/sqs/test/utils/testAdmin.ts

echo -e "\n=== Full file context (lines 40-70) ==="
sed -n '40,70p' packages/sqs/test/utils/testAdmin.ts

echo -e "\n=== Check package.json for fauxqs dependency ==="
cat package.json | grep -A 2 -B 2 'fauxqs' || echo "Not found in root package.json"

echo -e "\n=== Check packages/sqs/package.json ==="
cat packages/sqs/package.json | grep -A 2 -B 2 'fauxqs' || echo "Not found in packages/sqs/package.json"

Repository: kibertoad/message-queue-toolkit

Length of output: 1436


🏁 Script executed:

#!/bin/bash
# Look for fauxqs in node_modules
echo "=== Looking for fauxqs type definitions ==="
find node_modules/fauxqs -name "*.d.ts" -o -name "*.ts" 2>/dev/null | head -20

echo -e "\n=== Check if fauxqs exists and look for index.d.ts ==="
ls -la node_modules/fauxqs/ 2>/dev/null | head -20

echo -e "\n=== Search for emptyBucket in testAdmin.ts ==="
grep -n "emptyBucket" packages/sqs/test/utils/testAdmin.ts

echo -e "\n=== Full testAdmin.ts file to see all server method calls ==="
cat packages/sqs/test/utils/testAdmin.ts

Repository: kibertoad/message-queue-toolkit

Length of output: 2578


🌐 Web query:

fauxqs npm library FauxqsServer createQueue deleteQueue createBucket emptyBucket async Promise

💡 Result:

I couldn’t find any npm package or GitHub project named fauxqs nor any API docs mentioning FauxqsServer, createQueue/deleteQueue, or createBucket/emptyBucket after searching those exact identifiers. If you paste a link to the repo/npm page (or the exact package name), I can map the methods and their Promise/async behavior precisely.

If your goal is “a fake/local AWS SQS server” for tests, a common package is fake-sqs, which exposes an SQSServer you await to start (it uses async/Promises for server startup via bootstrap()). [1]

If you’re implementing real SQS queue creation/deletion semantics (or mocking them), note AWS SQS requires waiting at least 60 seconds after deleting a queue before recreating one with the same name. [2]

Sources: [1] [2]


🏁 Script executed:

#!/bin/bash
# Check if fauxqs is a workspace package
echo "=== Looking for fauxqs in workspace ==="
find . -name "package.json" -type f -exec grep -l '"name".*fauxqs' {} \;

echo -e "\n=== Search for fauxqs directory ==="
find . -type d -name "fauxqs" 2>/dev/null | head -10

echo -e "\n=== Check node_modules .package.json for fauxqs ==="
cat node_modules/fauxqs/package.json 2>/dev/null | head -30

echo -e "\n=== Look for index.js or index.d.ts in fauxqs ==="
ls -la node_modules/fauxqs/ 2>/dev/null

echo -e "\n=== Try to find type definitions for FauxqsServer ==="
rg "FauxqsServer" --type ts --type tsx -A 5 -B 5 | head -100

Repository: kibertoad/message-queue-toolkit

Length of output: 355


🏁 Script executed:

#!/bin/bash
# Search for how server methods are called
echo "=== Search for server.deleteQueue calls ==="
rg "server\.deleteQueue|server\.createQueue|server\.createBucket|server\.emptyBucket" -n

echo -e "\n=== Look for FauxqsServer type imports and definitions ==="
rg "FauxqsServer|from.*fauxqs" -n

echo -e "\n=== Search test files for await patterns with server methods ==="
rg "await.*server\.|server\.[a-zA-Z]+.*await" -n

echo -e "\n=== Check if createQueue returns a value (line 47) ==="
sed -n '45,50p' packages/sqs/test/utils/testAdmin.ts

Repository: kibertoad/message-queue-toolkit

Length of output: 3635


🏁 Script executed:

#!/bin/bash
# Check fauxqsInstance files
echo "=== Content of packages/sqs/test/utils/fauxqsInstance.ts ==="
cat packages/sqs/test/utils/fauxqsInstance.ts

echo -e "\n=== How createQueue result is used in tests ==="
rg "createQueue" packages/sqs/test -A 2 -B 2 --type ts | head -50

echo -e "\n=== Check SNS version too ==="
cat packages/sns/test/utils/fauxqsInstance.ts

Repository: kibertoad/message-queue-toolkit

Length of output: 5636


Add await or return when calling server methods to handle async operations.

The deleteQueue, createBucket, and emptyBucket server method calls are not awaited. While the async methods themselves will not throw, the underlying operations will execute fire-and-forget. Add await before each call or return before the loop/call to ensure test cleanup doesn't execute before operations complete:

  • deleteQueues: Add await before each this.server.deleteQueue() call or await Promise.all() for all deletions
  • createBucket: Add await before this.server.createBucket()
  • emptyBuckets: Add await for each this.server.emptyBucket() call

Note: createQueue already returns the promise, so it's handled correctly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/testAdmin.ts` around lines 52 - 62, The server-side
calls in deleteQueues, createBucket, and emptyBuckets are invoked
fire-and-forget; update deleteQueues to await each this.server.deleteQueue(...)
(or collect and await Promise.all over queueNames), update createBucket to await
this.server.createBucket(...), and update emptyBuckets to await each
this.server.emptyBucket(...) (or await Promise.all) so the async server
operations complete before the test cleanup proceeds; refer to the deleteQueues,
createBucket, and emptyBuckets functions and their this.server.deleteQueue(),
this.server.createBucket(), this.server.emptyBucket() calls to apply the
changes.

Comment on lines +64 to +70
async createBucket(name: string) {
if (this.server) {
this.server.createBucket(name)
return
}
return await assertBucket(this.s3!, name)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Non-null assertion on this.s3! will throw at runtime if s3 was not provided.

When server is undefined and s3 was also not supplied (it's optional in the constructor), createBucket and emptyBuckets will crash with an opaque error. Consider adding an early guard:

🛡️ Suggested defensive check
   async createBucket(name: string) {
     if (this.server) {
       this.server.createBucket(name)
       return
     }
+    if (!this.s3) {
+      throw new Error('S3 client is required when not using FauxqsServer')
+    }
-    return await assertBucket(this.s3!, name)
+    return await assertBucket(this.s3, name)
   }

The same applies to emptyBuckets on Line 79.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async createBucket(name: string) {
if (this.server) {
this.server.createBucket(name)
return
}
return await assertBucket(this.s3!, name)
}
async createBucket(name: string) {
if (this.server) {
this.server.createBucket(name)
return
}
if (!this.s3) {
throw new Error('S3 client is required when not using FauxqsServer')
}
return await assertBucket(this.s3, name)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/testAdmin.ts` around lines 64 - 70, createBucket (and
likewise emptyBuckets) currently uses a non-null assertion on this.s3 which will
throw if neither server nor s3 were provided; add an early guard in createBucket
(and emptyBuckets) that checks if this.server is falsy and this.s3 is falsy, and
in that case throw or return a clear, descriptive error (e.g., "S3 client not
available: provide server or s3") instead of using this.s3! and calling
assertBucket; reference the methods createBucket and emptyBuckets and the helper
assertBucket when adding the guard.

Comment on lines 26 to 36
} else {
const { createLocalhostHandler } = await import('fauxqs')
s3Config = {
endpoint: 'http://s3.localhost:4566',
region: 'eu-west-1',
credentials: {
accessKeyId: 'access',
secretAccessKey: 'secret',
},
requestHandler: createLocalhostHandler(),
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Top-level conditional await import('fauxqs') is valid ESM but couples module init to the dev dependency.

The dynamic import runs at module evaluation time (not lazily on first use). If fauxqs is ever missing from node_modules (e.g., a partial install), this will throw at import time rather than at the point of use, which makes the error message less actionable. This is a minor concern given the file lives exclusively in test utilities — just worth being aware of.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/sqs/test/utils/testAwsConfig.ts` around lines 26 - 36, The top-level
await import('fauxqs') that assigns createLocalhostHandler into s3Config runs at
module evaluation and can fail if the dev dependency is absent; change this to a
lazy dynamic import by moving the import into a function that constructs/returns
the s3Config at call-time (e.g., add an async function like buildLocalS3Config
or getS3Config that does const { createLocalhostHandler } = await
import('fauxqs') and then returns the s3Config object with requestHandler:
createLocalhostHandler()); update callers to use that function so
createLocalhostHandler is only imported when needed and not during module
initialization, keeping the symbol names createLocalhostHandler and s3Config for
easy location.

"test:coverage": {
"dependsOn": ["^test:coverage"]
},
"test:coverage": {},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Parallel test:coverage execution creates a port-4566 conflict for the fauxqs backend.

With "test:coverage": {}, TurboRepo will run test:coverage in all packages simultaneously (no dependsOn dependency ordering). Both packages/sqs and packages/sns call startFauxqs({ port: 4566, host: 'localstack' }) in their respective vitest.setup.ts/globalSetup files. When a developer runs turbo run test:coverage without QUEUE_BACKEND=localstack, both processes race to bind localstack:4566; the second one fails and its entire test suite crashes.

Two options to fix:

Option A — assign different ports per package (e.g. SQS on 4566, SNS on 4567) and update the matching client endpoints.

Option B — add a dependsOn guard so packages run sequentially when fauxqs is active:

🔧 Option B – sequential ordering
-        "test:coverage": {},
+        "test:coverage": {
+            "dependsOn": ["^test:coverage"]
+        },

Note: the fauxqs server is started in per-package vitest setup hooks; it lives only for the duration of that package's vitest process, so Option A (distinct ports per package) requires no shared state and is the cleaner long-term approach.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"test:coverage": {},
"test:coverage": {
"dependsOn": ["^test:coverage"]
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@turbo.json` at line 12, Parallel test execution causes both packages to call
startFauxqs({ port: 4566, host: 'localstack' }) and race to bind the same port;
fix by either (A) assigning per-package ports and updating client endpoints
(e.g., change startFauxqs call in packages/sqs and packages/sns to different
port numbers and update any client initialization that relies on
QUEUE_BACKEND/localstack endpoint) or (B) make turbo run "test:coverage"
sequential when fauxqs is used by adding a dependsOn guard in turbo.json so
packages run one after another when QUEUE_BACKEND=localstack; locate and update
the startFauxqs calls in your vitest.setup.ts/globalSetup and the turbo.json
"test:coverage" entry accordingly.

["packages/sqs"]="@message-queue-toolkit/sqs"
)

AWS_PACKAGE_PATHS=("packages/sqs" "packages/sns")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also add here s3-payload-store

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do it in a separate PR, currently it only uses fauxqs, but probably should use dual setup as well

@kibertoad kibertoad merged commit b7f58de into main Feb 22, 2026
16 checks passed
@kibertoad kibertoad deleted the feat/fauxsq branch February 22, 2026 17:14
@coderabbitai coderabbitai bot mentioned this pull request Feb 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants