Repository: ordermentum/steveo
Pull request #922: [DevX-432] Add SQS Health check fifo compatibility
Author: login: ish-bindra
Created: 2025-03-28T04:34:14Z | Merged: 2025-03-28T05:14:59Z

This PR adds support for health checking FIFO sqs queues.

Also fixes eslint config across packages to refer to the right tsconfig.json file and refactors sqs consumer test file to have factory methods and use the right types.


Diff:
diff --git a/.changeset/puny-guests-push.md b/.changeset/puny-guests-push.md
new file mode 100644
index 000000000..ae5bcba2f
--- /dev/null
+++ b/.changeset/puny-guests-push.md
@@ -0,0 +1,5 @@
+---
+"steveo": patch
+---
+
+Support FIFO queues when doing health checks in sqs consumers
diff --git a/packages/cloudwatch/test/.eslintrc b/packages/cloudwatch/test/.eslintrc
index d7b37a0e0..03100c7da 100644
--- a/packages/cloudwatch/test/.eslintrc
+++ b/packages/cloudwatch/test/.eslintrc
@@ -4,7 +4,6 @@
     "@ordermentum/ordermentum/jest"
   ],
   "parserOptions": {
-    "project": "../tsconfig.test.json"
+    "project": "./tsconfig.test.json"
   }
-}
-
+}
\ No newline at end of file
diff --git a/packages/datadog/test/.eslintrc b/packages/datadog/test/.eslintrc
index 1d647c59f..03100c7da 100644
--- a/packages/datadog/test/.eslintrc
+++ b/packages/datadog/test/.eslintrc
@@ -4,6 +4,6 @@
     "@ordermentum/ordermentum/jest"
   ],
   "parserOptions": {
-    "project": "../tsconfig.test.json"
+    "project": "./tsconfig.test.json"
   }
-}
+}
\ No newline at end of file
diff --git a/packages/newrelic/test/.eslintrc b/packages/newrelic/test/.eslintrc
index 1d647c59f..03100c7da 100644
--- a/packages/newrelic/test/.eslintrc
+++ b/packages/newrelic/test/.eslintrc
@@ -4,6 +4,6 @@
     "@ordermentum/ordermentum/jest"
   ],
   "parserOptions": {
-    "project": "../tsconfig.test.json"
+    "project": "./tsconfig.test.json"
   }
-}
+}
\ No newline at end of file
diff --git a/packages/sentry/test/.eslintrc b/packages/sentry/test/.eslintrc
index 1d647c59f..03100c7da 100644
--- a/packages/sentry/test/.eslintrc
+++ b/packages/sentry/test/.eslintrc
@@ -4,6 +4,6 @@
     "@ordermentum/ordermentum/jest"
   ],
   "parserOptions": {
-    "project": "../tsconfig.test.json"
+    "project": "./tsconfig.test.json"
   }
-}
+}
\ No newline at end of file
diff --git a/packages/statsd/test/.eslintrc b/packages/statsd/test/.eslintrc
index 1d647c59f..03100c7da 100644
--- a/packages/statsd/test/.eslintrc
+++ b/packages/statsd/test/.eslintrc
@@ -4,6 +4,6 @@
     "@ordermentum/ordermentum/jest"
   ],
   "parserOptions": {
-    "project": "../tsconfig.test.json"
+    "project": "./tsconfig.test.json"
   }
-}
+}
\ No newline at end of file
diff --git a/packages/steveo/src/consumers/sqs.ts b/packages/steveo/src/consumers/sqs.ts
index e108e9c11..b7d997024 100644
--- a/packages/steveo/src/consumers/sqs.ts
+++ b/packages/steveo/src/consumers/sqs.ts
@@ -284,8 +284,9 @@ class SqsRunner extends BaseRunner implements IRunner {
     if (!item) {
       throw new Error('No queues registered');
     }
+    const topic = this.getTopic(item);
 
-    await this.sqs.getQueueUrl({ QueueName: item });
+    await this.sqs.getQueueUrl({ QueueName: topic });
   };
 
   async getQueueUrl(topic: string) {
diff --git a/packages/steveo/test/.eslintrc b/packages/steveo/test/.eslintrc
index 1d647c59f..03100c7da 100644
--- a/packages/steveo/test/.eslintrc
+++ b/packages/steveo/test/.eslintrc
@@ -4,6 +4,6 @@
     "@ordermentum/ordermentum/jest"
   ],
   "parserOptions": {
-    "project": "../tsconfig.test.json"
+    "project": "./tsconfig.test.json"
   }
-}
+}
\ No newline at end of file
diff --git a/packages/steveo/test/consumer/sqs_test.ts b/packages/steveo/test/consumer/sqs_test.ts
index 518fdcea8..b15dc2f71 100644
--- a/packages/steveo/test/consumer/sqs_test.ts
+++ b/packages/steveo/test/consumer/sqs_test.ts
@@ -8,6 +8,91 @@ import Runner from '../../src/consumers/sqs';
 import { build } from '../../src/lib/pool';
 import Registry from '../../src/runtime/registry';
 import { Steveo } from '../../src';
+import {
+  DummyConfiguration,
+  IRegistry,
+  SQSConfiguration,
+} from '../../src/common';
+
+interface MockRegistryOptions {
+  subscribeStub?: sinon.SinonStub;
+  fifo?: boolean;
+  waitToCommit?: boolean;
+  emitStub?: sinon.SinonStub;
+}
+
+function createMockRegistry(
+  sandbox: sinon.SinonSandbox,
+  options: MockRegistryOptions = {}
+): IRegistry {
+  const {
+    subscribeStub = sandbox.stub().resolves({ some: 'success' }),
+    fifo = false,
+    waitToCommit = false,
+    emitStub = sandbox.stub(),
+  } = options;
+
+  const taskOptions: Record<string, unknown> = {};
+
+  if (fifo) {
+    taskOptions.fifo = true;
+  }
+
+  if (waitToCommit) {
+    taskOptions.waitToCommit = true;
+  }
+
+  const task: Record<string, unknown> = {
+    publish: sandbox.stub(),
+    subscribe: subscribeStub,
+  };
+
+  if (Object.keys(taskOptions).length > 0) {
+    task.options = taskOptions;
+  }
+
+  return {
+    registeredTasks: {},
+    items: new Map(),
+    heartbeat: 0,
+    addNewTask: sandbox.stub(),
+    removeTask: sandbox.stub(),
+    getTopics: sandbox.stub().returns([]),
+    getTaskTopics: sandbox.stub().returns([]),
+    getTopic: sandbox.stub().callsFake(name => name),
+    addTopic: sandbox.stub(),
+    getTask: sandbox.stub().returns(task),
+    emit: emitStub,
+    events: {
+      emit: emitStub,
+      on: sandbox.stub(),
+    },
+  };
+}
+
+function createSQSConfig(): SQSConfiguration {
+  return {
+    engine: 'sqs' as const,
+    apiVersion: '2012-11-05',
+    messageRetentionPeriod: '345600', // 4 days
+    receiveMessageWaitTimeSeconds: '20',
+    maxNumberOfMessages: 10,
+    visibilityTimeout: 30,
+    waitTimeSeconds: 20,
+  };
+}
+
+function createSteveoInstance(
+  registry: IRegistry,
+  config: SQSConfiguration | DummyConfiguration = createSQSConfig()
+): Steveo {
+  const log = logger({ level: 'debug' });
+  const steveo = new Steveo(config, log);
+  steveo.registry = registry;
+  steveo.pool = build(registry);
+
+  return steveo;
+}
 
 describe('runner/sqs', () => {
   let runner: Runner;
@@ -28,7 +113,7 @@ describe('runner/sqs', () => {
       registry,
       pool: build(registry),
     };
-    // @ts-ignore
+    // @ts-expect-error - Steveo type doesn't expose registry property
     runner = new Runner(steveo);
   });
 
@@ -43,48 +128,19 @@ describe('runner/sqs', () => {
   });
 
   it('should create queue with fifo suffix if enabled', async () => {
-    const anotherRegistry = {
-      registeredTasks: [],
-      addNewTask: () => {},
-      removeTask: () => {},
-      getTopics: () => [],
-      getTask: () => ({
-        options: {
-          fifo: true,
-        },
-      }),
-      emit: sandbox.stub(),
-      events: {
-        emit: sandbox.stub(),
-      },
-    };
-
-    const log = logger({ level: 'debug' });
-    const config = {
-      engine: 'sqs' as const,
-      logger: log,
-      registry: anotherRegistry,
-    };
-
-    //@ts-expect-error
-    const steveo = new Steveo(config);
-    // @ts-ignore
-    steveo.registry = anotherRegistry;
-    // @ts-ignore
-    steveo.pool = build(anotherRegistry);
-
-    const anotherRunner = new Runner(steveo);
+    // Create a registry with fifo enabled
+    const registry = createMockRegistry(sandbox, { fifo: true });
+    const steveo = createSteveoInstance(registry);
+    const runner = new Runner(steveo);
 
-    const createQueueStub = sandbox
-      .stub(anotherRunner.sqs, 'createQueue')
-      // @ts-ignore
-      .returns({ promise: async () => {} });
+    // Stub the createQueue method
+    const createQueueStub = sandbox.stub(runner.sqs, 'createQueue').resolves();
 
-    await anotherRunner.createQueue('a-topic');
+    await runner.createQueue('a-topic');
 
-    expect(createQueueStub.called).to.be.true;
+    expect(createQueueStub.called).to.equal(true);
 
-    const createQueueArgs: any = createQueueStub.args[0][0];
+    const createQueueArgs = createQueueStub.args[0][0] as { QueueName: string };
     expect(createQueueArgs.QueueName, 'should have fifo suffix').to.eqls(
       'a-topic.fifo'
     );
@@ -92,44 +148,16 @@ describe('runner/sqs', () => {
 
   it('should invoke callback when receives a message on topic', async () => {
     const subscribeStub = sandbox.stub().resolves({ some: 'success' });
-    const anotherRegistry = {
-      registeredTasks: [],
-      addNewTask: () => {},
-      removeTask: () => {},
-      getTopics: () => [],
-      getTask: () => ({
-        publish: () => {},
-        subscribe: subscribeStub,
-      }),
-      emit: sandbox.stub(),
-      events: {
-        emit: sandbox.stub(),
-      },
-    };
-
-    const log = logger({ level: 'debug' });
-    const config = {
-      engine: 'sqs' as const,
-      logger: log,
-      registry: anotherRegistry,
-    };
-
-    //@ts-expect-error
-    const steveo = new Steveo(config);
-    // @ts-ignore
-    steveo.registry = anotherRegistry;
-    // @ts-ignore
-    steveo.pool = build(anotherRegistry);
+    const registry = createMockRegistry(sandbox, { subscribeStub });
+    const steveo = createSteveoInstance(registry);
+    const runner = new Runner(steveo);
 
-    const anotherRunner = new Runner(steveo);
-
-    // @ts-ignore
+    // Stub the deleteMessage method
     const deleteMessageStub = sandbox
-      .stub(anotherRunner.sqs, 'deleteMessage')
-      // @ts-ignore
-      .returns({ promise: async () => {} });
+      .stub(runner.sqs, 'deleteMessage')
+      .resolves();
 
-    await anotherRunner.receive(
+    await runner.receive(
       [
         { ReceiptHandle: v4(), Body: JSON.stringify({ data: 'Hello' }) },
         { ReceiptHandle: v4(), Body: JSON.stringify({ data: 'World' }) },
@@ -146,46 +174,15 @@ describe('runner/sqs', () => {
 
   it('should invoke callback with context if context is present in message body', async () => {
     const subscribeStub = sandbox.stub().resolves({ some: 'success' });
-    const anotherRegistry = {
-      registeredTasks: [],
-      addNewTask: () => {},
-      removeTask: () => {},
-      getTopics: () => [],
-      getTask: () => ({
-        publish: () => {},
-        subscribe: subscribeStub,
-      }),
-      emit: sandbox.stub(),
-      events: {
-        emit: sandbox.stub(),
-      },
-    };
-
-    const log = logger({ level: 'debug' });
-    const config = {
-      engine: 'sqs' as const,
-      logger: log,
-      registry: anotherRegistry,
-    };
-
-    //@ts-expect-error
-    const steveo = new Steveo(config);
-    // @ts-ignore
-    steveo.registry = anotherRegistry;
-    // @ts-ignore
-    steveo.pool = build(anotherRegistry);
-
-    const anotherRunner = new Runner(steveo);
+    const registry = createMockRegistry(sandbox, { subscribeStub });
+    const steveo = createSteveoInstance(registry);
+    const runner = new Runner(steveo);
 
-    // @ts-ignore
-    sandbox
-      .stub(anotherRunner.sqs, 'deleteMessage')
-      // @ts-ignore
-      .returns({ promise: async () => {} });
+    sandbox.stub(runner.sqs, 'deleteMessage').resolves();
 
     const inputContext = { contextKey: 'contextValue' };
     const messageBody = { data: 'Hello', _meta: inputContext };
-    await anotherRunner.receive(
+    await runner.receive(
       [
         {
           ReceiptHandle: v4(),
@@ -205,47 +202,19 @@ describe('runner/sqs', () => {
 
   it('should delete message if waitToCommit is true after processing', async () => {
     const subscribeStub = sandbox.stub().resolves({ some: 'success' });
-    const anotherRegistry = {
-      registeredTasks: [],
-      addNewTask: () => {},
-      removeTask: () => {},
-      getTopics: () => [],
-      getTask: () => ({
-        publish: () => {},
-        subscribe: subscribeStub,
-        options: {
-          waitToCommit: true,
-        },
-      }),
-      emit: sandbox.stub(),
-      events: {
-        emit: sandbox.stub(),
-      },
-    };
-
-    const log = logger({ level: 'debug' });
-    const config = {
-      engine: 'sqs' as const,
-      logger: log,
-      registry: anotherRegistry,
-    };
-
-    //@ts-expect-error
-    const steveo = new Steveo(config);
-    // @ts-ignore
-    steveo.registry = anotherRegistry;
-    // @ts-ignore
-    steveo.pool = build(anotherRegistry);
-
-    const anotherRunner = new Runner(steveo);
+    const registry = createMockRegistry(sandbox, {
+      subscribeStub,
+      waitToCommit: true,
+    });
+    const steveo = createSteveoInstance(registry);
+    const runner = new Runner(steveo);
 
-    // @ts-ignore
+    // Stub the deleteMessage method
     const deleteMessageStub = sandbox
-      .stub(anotherRunner.sqs, 'deleteMessage')
-      // @ts-ignore
-      .returns({ promise: async () => {} });
+      .stub(runner.sqs, 'deleteMessage')
+      .resolves();
 
-    await anotherRunner.receive(
+    await runner.receive(
       [
         { ReceiptHandle: v4(), Body: JSON.stringify({ data: 'Hello' }) },
         { ReceiptHandle: v4(), Body: JSON.stringify({ data: 'World' }) },
@@ -262,54 +231,26 @@ describe('runner/sqs', () => {
 
   it('should delete message using the FIFO QueueUrl if enabled in task', async () => {
     const subscribeStub = sandbox.stub().resolves({ some: 'success' });
-    const anotherRegistry = {
-      registeredTasks: [],
-      addNewTask: () => {},
-      removeTask: () => {},
-      getTopics: () => [],
-      getTaskTopics: () => [],
-      getTask: () => ({
-        publish: () => {},
-        subscribe: subscribeStub,
-        options: {
-          fifo: true,
-        },
-      }),
-      emit: sandbox.stub(),
-      events: {
-        emit: sandbox.stub(),
-      },
-    };
-
-    const log = logger({ level: 'debug' });
-    const config = {
-      engine: 'sqs' as const,
-      logger: log,
-      registry: anotherRegistry,
-    };
-
-    //@ts-expect-error
-    const steveo = new Steveo(config);
-    // @ts-ignore
-    steveo.registry = anotherRegistry;
-    // @ts-ignore
-    steveo.pool = build(anotherRegistry);
-
-    const anotherRunner = new Runner(steveo);
+    const registry = createMockRegistry(sandbox, {
+      subscribeStub,
+      fifo: true,
+    });
+    const steveo = createSteveoInstance(registry);
+    const runner = new Runner(steveo);
 
-    // @ts-ignore
+    // Stub the deleteMessage method
     const deleteMessageStub = sandbox
-      .stub(anotherRunner.sqs, 'deleteMessage')
+      .stub(runner.sqs, 'deleteMessage')
       .resolves({});
 
     const randomId = randomUUID();
-    sandbox.stub(anotherRunner.sqs, 'getQueueUrl').resolves({
+    sandbox.stub(runner.sqs, 'getQueueUrl').resolves({
       QueueUrl: `https://ap-southeast2.aws.com/${randomId}/a-topic.fifo`,
     });
 
-    const url: string = await anotherRunner.getQueueUrl('a-topic');
+    const url: string = await runner.getQueueUrl('a-topic');
 
-    await anotherRunner.receive(
+    await runner.receive(
       [
         { ReceiptHandle: v4(), Body: JSON.stringify({ data: 'Hello' }) },
         { ReceiptHandle: v4(), Body: JSON.stringify({ data: 'World' }) },
@@ -327,213 +268,155 @@ describe('runner/sqs', () => {
 
   it('get all urls for queues', async () => {
     const subscribeStub = sandbox.stub().resolves({ some: 'success' });
-    const anotherRegistry = {
-      getTask: () => ({
-        publish: () => {},
-        subscribe: subscribeStub,
-      }),
-      emit: sandbox.stub(),
-      events: {
-        emit: sandbox.stub(),
-      },
-    };
+    const registry = createMockRegistry(sandbox, { subscribeStub });
 
+    // Use a different config for this test
     const config = {
       engine: 'dummy' as const,
     };
+    const steveo = createSteveoInstance(registry, config);
+    const runner = new Runner(steveo);
 
-    const steveo = new Steveo(config);
-    // @ts-ignore
-    steveo.registry = anotherRegistry;
-
-    const anotherRunner = new Runner(steveo);
     const getQueueUrlAsyncStub = sandbox
-      .stub(anotherRunner.sqs, 'getQueueUrl')
+      .stub(runner.sqs, 'getQueueUrl')
       .resolves({ QueueUrl: 'https://ap-southeast2.aws.com' });
 
-    expect(anotherRunner.sqsUrls).to.deep.equal({});
-    await anotherRunner.getQueueUrl('test');
-    expect(anotherRunner.sqsUrls).to.deep.equal({
+    expect(runner.sqsUrls).to.deep.equal({});
+    await runner.getQueueUrl('test');
+    expect(runner.sqsUrls).to.deep.equal({
       test: 'https://ap-southeast2.aws.com',
     });
-    await anotherRunner.getQueueUrl('test');
+    await runner.getQueueUrl('test');
     expect(getQueueUrlAsyncStub.calledOnce).to.equal(true);
   });
 
   it('continues to work if queue does not exist', async () => {
     const subscribeStub = sandbox.stub().resolves({ some: 'success' });
-    const anotherRegistry = {
-      getTask: () => ({
-        publish: () => {},
-        subscribe: subscribeStub,
-      }),
-      emit: sandbox.stub(),
-      events: {
-        emit: sandbox.stub(),
-      },
-    };
+    const registry = createMockRegistry(sandbox, { subscribeStub });
 
+    // Use a different config for this test
     const config = {
       engine: 'dummy' as const,
     };
+    const steveo = createSteveoInstance(registry, config);
+    const runner = new Runner(steveo);
 
-    const steveo = new Steveo(config);
-    // @ts-ignore
-    steveo.registry = anotherRegistry;
-
-    const anotherRunner = new Runner(steveo);
     const getQueueUrlAsyncStub = sandbox
-      .stub(anotherRunner.sqs, 'getQueueUrl')
+      .stub(runner.sqs, 'getQueueUrl')
       .rejects(new Error());
 
-    expect(anotherRunner.sqsUrls).to.deep.equal({});
-    await anotherRunner.getQueueUrl('test');
-    expect(anotherRunner.sqsUrls).to.deep.equal({});
-    await anotherRunner.getQueueUrl('test');
+    expect(runner.sqsUrls).to.deep.equal({});
+    await runner.getQueueUrl('test');
+    expect(runner.sqsUrls).to.deep.equal({});
+    await runner.getQueueUrl('test');
     expect(getQueueUrlAsyncStub.callCount).to.equal(2);
   });
 
   describe('process', () => {
     it('terminates', async () => {
       const subscribeStub = sandbox.stub().resolves({ some: 'success' });
+      const registry = createMockRegistry(sandbox, {
+        subscribeStub,
+      });
 
-      const anotherRegistry = {
-        getTask: () => ({
-          publish: () => {},
-          subscribe: subscribeStub,
-        }),
-        getTopics: () => ['test'],
-        getTaskTopics: () => ['test'],
-        emit: sandbox.stub(),
-        events: {
-          emit: sandbox.stub(),
-        },
-      };
+      // Override getTopics and getTaskTopics for this test
+      registry.getTopics = sandbox.stub().returns(['test']);
+      registry.getTaskTopics = sandbox.stub().returns(['test']);
 
+      // Use a different config for this test
       const config = {
         engine: 'dummy' as const,
       };
+      const steveo = createSteveoInstance(registry, config);
+      const runner = new Runner(steveo);
 
-      const steveo = new Steveo(config);
-      // @ts-ignore
-      steveo.registry = anotherRegistry;
-      const anotherRunner = new Runner(steveo);
-      anotherRunner.state = 'terminating';
-      await anotherRunner.process();
-      expect(anotherRunner.state).to.equal('terminated');
+      runner.state = 'terminating';
+      await runner.process();
+      expect(runner.state).to.equal('terminated');
     });
 
     it('paused', async () => {
       const subscribeStub = sandbox.stub().resolves({ some: 'success' });
+      const registry = createMockRegistry(sandbox, {
+        subscribeStub,
+      });
 
-      const anotherRegistry = {
-        getTask: () => ({
-          publish: () => {},
-          subscribe: subscribeStub,
-        }),
-        getTopics: () => ['test'],
-        getTaskTopics: () => ['test'],
-        emit: sandbox.stub(),
-        events: {
-          emit: sandbox.stub(),
-        },
-      };
+      // Override getTopics and getTaskTopics for this test
+      registry.getTopics = sandbox.stub().returns(['test']);
+      registry.getTaskTopics = sandbox.stub().returns(['test']);
 
+      // Use a different config for this test
       const config = {
         engine: 'dummy' as const,
       };
-
-      const steveo = new Steveo(config);
-      // @ts-ignore
-      steveo.registry = anotherRegistry;
-      // @ts-ignore
-      const anotherRunner = new Runner(steveo);
+      const steveo = createSteveoInstance(registry, config);
+      const runner = new Runner(steveo);
 
       const getQueueUrlAsyncStub = sandbox
-        .stub(anotherRunner.sqs, 'getQueueUrl')
+        .stub(runner.sqs, 'getQueueUrl')
         .resolves({ QueueUrl: 'https://ap-southeast2.aws.com' });
 
       const receiveMessageAsyncStub = sandbox
-        .stub(anotherRunner.sqs, 'receiveMessage')
+        .stub(runner.sqs, 'receiveMessage')
         .resolves([]);
 
-      anotherRunner.state = 'paused';
-      await anotherRunner.process();
+      runner.state = 'paused';
+      await runner.process();
       expect(getQueueUrlAsyncStub.calledOnce).to.equal(false);
       expect(receiveMessageAsyncStub.calledOnce).to.equal(false);
     });
 
     it('processes a message', async () => {
       const subscribeStub = sandbox.stub().resolves({ some: 'success' });
+      const registry = createMockRegistry(sandbox, {
+        subscribeStub,
+      });
 
-      const anotherRegistry = {
-        getTask: () => ({
-          publish: () => {},
-          subscribe: subscribeStub,
-        }),
-        getTopics: () => ['test'],
-        getTaskTopics: () => ['test'],
-        emit: sandbox.stub(),
-        events: {
-          emit: sandbox.stub(),
-        },
-      };
+      // Override getTopics and getTaskTopics for this test
+      registry.getTopics = sandbox.stub().returns(['test']);
+      registry.getTaskTopics = sandbox.stub().returns(['test']);
 
+      // Use a different config for this test
       const config = {
         engine: 'dummy' as const,
       };
-
-      const steveo = new Steveo(config);
-      // @ts-ignore
-      steveo.registry = anotherRegistry;
-
-      const anotherRunner = new Runner(steveo);
+      const steveo = createSteveoInstance(registry, config);
+      const runner = new Runner(steveo);
 
       const getQueueUrlAsyncStub = sandbox
-        .stub(anotherRunner.sqs, 'getQueueUrl')
+        .stub(runner.sqs, 'getQueueUrl')
         .resolves({ QueueUrl: 'https://ap-southeast2.aws.com' });
 
       const receiveMessageAsyncStub = sandbox
-        .stub(anotherRunner.sqs, 'receiveMessage')
+        .stub(runner.sqs, 'receiveMessage')
         .resolves([]);
 
-      await anotherRunner.process();
+      await runner.process();
       expect(getQueueUrlAsyncStub.calledOnce).to.equal(true);
       expect(receiveMessageAsyncStub.calledOnce).to.equal(true);
     });
 
     it('processes a message from a FIFO queue', async () => {
       const subscribeStub = sandbox.stub().resolves({ some: 'success' });
+      const registry = createMockRegistry(sandbox, {
+        subscribeStub,
+        fifo: true,
+      });
 
-      const anotherRegistry = {
-        getTask: () => ({
-          publish: () => {},
-          subscribe: subscribeStub,
-          options: {
-            fifo: true,
-          },
-        }),
-        getTopics: () => ['test'],
-        getTaskTopics: () => ['test'],
-        emit: sandbox.stub(),
-        events: {
-          emit: sandbox.stub(),
-        },
-      };
+      // Override getTopics and getTaskTopics for this test
+      registry.getTopics = sandbox.stub().returns(['test']);
+      registry.getTaskTopics = sandbox.stub().returns(['test']);
 
+      // Use a different config for this test
       const config = {
         engine: 'dummy' as const,
       };
-
-      const steveo = new Steveo(config);
-      // @ts-ignore
-      steveo.registry = anotherRegistry;
-
-      const anotherRunner = new Runner(steveo);
+      const steveo = createSteveoInstance(registry, config);
+      const runner = new Runner(steveo);
 
       const randomId = randomUUID();
       const getQueueUrlAsyncStub = sandbox
-        .stub(anotherRunner.sqs, 'getQueueUrl')
+        .stub(runner.sqs, 'getQueueUrl')
         // if are logic is correct, and that it appends .fifo to the plain `test` topic
         //  this would be the argument passed on the getQueueUrl to SQS
         .withArgs({ QueueName: 'test.fifo' })
@@ -542,10 +425,10 @@ describe('runner/sqs', () => {
         });
 
       const receiveMessageAsyncStub = sandbox
-        .stub(anotherRunner.sqs, 'receiveMessage')
+        .stub(runner.sqs, 'receiveMessage')
         .resolves([]);
 
-      await anotherRunner.process();
+      await runner.process();
       expect(getQueueUrlAsyncStub.calledOnce).to.equal(true);
       expect(receiveMessageAsyncStub.calledOnce).to.equal(true);
 
@@ -559,32 +442,23 @@ describe('runner/sqs', () => {
   it('should invoke capture error when callback throws error on receiving a message on topic', async () => {
     const subscribeStub = sandbox.stub().throws({ some: 'error' });
     const emitStub = sandbox.stub();
-    const anotherRegistry = {
-      getTask: () => ({
-        publish: () => {},
-        subscribe: subscribeStub,
-      }),
-      emit: emitStub,
-      events: {
-        emit: emitStub,
-      },
-    };
+    const registry = createMockRegistry(sandbox, {
+      subscribeStub,
+      emitStub,
+    });
+
+    // Use a different config for this test
     const config = {
       engine: 'dummy' as const,
     };
-
-    const steveo = new Steveo(config);
-    // @ts-ignore
-    steveo.registry = anotherRegistry;
-
-    const anotherRunner = new Runner(steveo);
+    const steveo = createSteveoInstance(registry, config);
+    const runner = new Runner(steveo);
 
     const deleteMessageStub = sandbox
-      .stub(anotherRunner.sqs, 'deleteMessage')
-      // @ts-ignore
-      .returns({ promise: async () => {} });
+      .stub(runner.sqs, 'deleteMessage')
+      .resolves();
 
-    await anotherRunner.receive(
+    await runner.receive(
       [
         {
           ReceiptHandle: v4(),
@@ -612,54 +486,83 @@ describe('runner/sqs', () => {
     ).to.equal(true);
   });
 
+  describe('healthCheck', () => {
+    it('should call getQueueUrl with .fifo suffix for FIFO queues', async () => {
+      const subscribeStub = sandbox.stub().resolves({ some: 'success' });
+      const registry = createMockRegistry(sandbox, {
+        subscribeStub,
+        fifo: true,
+      });
+      registry.getTopics = sandbox.stub().returns(['fifo-health-topic']);
+      const steveo = createSteveoInstance(registry);
+      const runner = new Runner(steveo);
+
+      const getQueueUrlStub = sandbox
+        .stub(runner.sqs, 'getQueueUrl')
+        .resolves({});
+
+      await runner.healthCheck();
+
+      expect(getQueueUrlStub.calledOnce).to.equal(true);
+      expect(getQueueUrlStub.args[0][0]).to.deep.equal({
+        QueueName: 'fifo-health-topic.fifo',
+      });
+    });
+
+    it('should call getQueueUrl without .fifo suffix for standard queues', async () => {
+      const subscribeStub = sandbox.stub().resolves({ some: 'success' });
+      const registry = createMockRegistry(sandbox, { subscribeStub });
+      registry.getTopics = sandbox.stub().returns(['standard-health-topic']);
+      const steveo = createSteveoInstance(registry);
+      const runner = new Runner(steveo);
+
+      const getQueueUrlStub = sandbox
+        .stub(runner.sqs, 'getQueueUrl')
+        .resolves({});
+
+      await runner.healthCheck();
+
+      expect(getQueueUrlStub.calledOnce).to.equal(true);
+      expect(getQueueUrlStub.args[0][0]).to.deep.equal({
+        QueueName: 'standard-health-topic',
+      });
+    });
+
+    it('should throw error if no queues are registered', async () => {
+      const registry = createMockRegistry(sandbox);
+      registry.getTopics = sandbox.stub().returns([]);
+      const steveo = createSteveoInstance(registry);
+      const runner = new Runner(steveo);
+      try {
+        await runner.healthCheck();
+        expect.fail('Expected an error to be thrown');
+      } catch (err) {
+        expect(err).to.be.instanceOf(Error);
+        expect((err as Error).message).to.equal('No queues registered');
+      }
+    });
+  });
+
   it('calculates duration of a task run correctly', async () => {
     clock.restore();
     const startMs = Date.now();
     const subscribeStub = sandbox.stub().resolves({ some: 'success' });
-    const anotherRegistry = {
-      registeredTasks: [],
-      addNewTask: () => {},
-      removeTask: () => {},
-      getTopics: () => [],
-      getTask: () => ({
-        publish: () => {},
-        subscribe: subscribeStub,
-      }),
-      emit: sandbox.stub(),
-      events: {
-        emit: sandbox.stub(),
-      },
-    };
+    const registry = createMockRegistry(sandbox, { subscribeStub });
+    const config = createSQSConfig();
+    const steveo = createSteveoInstance(registry, config);
+    const runner = new Runner(steveo);
 
-    const log = logger({ level: 'debug' });
-    const config = {
-      engine: 'sqs' as const,
-      logger: log,
-      registry: anotherRegistry,
-    };
-
-    //@ts-expect-error
-    const steveo = new Steveo(config);
-    // @ts-ignore
-    steveo.registry = anotherRegistry;
-    // @ts-ignore
-    steveo.pool = build(anotherRegistry);
-
-    const anotherRunner = new Runner(steveo);
-
-    // @ts-ignore
-    sandbox
-      .stub(anotherRunner.sqs, 'deleteMessage')
-      // @ts-ignore
-      .returns({ promise: async () => {} });
+    sandbox.stub(runner.sqs, 'deleteMessage').resolves();
 
     const inputContext = { contextKey: 'contextValue', startMs };
     const messageBody = { data: 'Hello', _meta: inputContext };
 
     // Wait a second to avoid flakiness
-    await new Promise(resolve => setTimeout(resolve, 1000));
+    await new Promise(resolve => {
+      setTimeout(resolve, 1000);
+    });
 
-    await anotherRunner.receive(
+    await runner.receive(
       [
         {
           ReceiptHandle: v4(),
@@ -672,6 +575,8 @@ describe('runner/sqs', () => {
     // Greater than or equal to 1 second and less than a reasonable 15 seconds, as the test runs in a few milliseconds
     // To catch calculation errors
     // Note: Duration is in milliseconds
-    expect(subscribeStub.args[0][1].duration).to.be.greaterThanOrEqual(1000).lessThan(15000);
+    expect(subscribeStub.args[0][1].duration)
+      .to.be.greaterThanOrEqual(1000)
+      .lessThan(15000);
   });
 });
diff --git a/packages/storage-postgres/test/.eslintrc b/packages/storage-postgres/test/.eslintrc
index d7b37a0e0..03100c7da 100644
--- a/packages/storage-postgres/test/.eslintrc
+++ b/packages/storage-postgres/test/.eslintrc
@@ -4,7 +4,6 @@
     "@ordermentum/ordermentum/jest"
   ],
   "parserOptions": {
-    "project": "../tsconfig.test.json"
+    "project": "./tsconfig.test.json"
   }
-}
-
+}
\ No newline at end of file


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This pull request adds support for health checking FIFO SQS queues while also updating the ESLint configuration and refactoring the SQS consumer tests.  
- Added logic to derive a FIFO queue name using getTopic.  
- Updated the SQS getQueueUrl call to use the derived topic.  
- Included an accompanying changeset note for tracking.

### Reviewed Changes

Copilot reviewed 3 out of 10 changed files in this pull request and generated no comments.

| File                                               | Description                                            |
| -------------------------------------------------- | ------------------------------------------------------ |
| packages/steveo/src/consumers/sqs.ts               | Updates to support FIFO queues by mapping item to topic |
| .changeset/puny-guests-push.md                      | Added changeset note for FIFO queue health check       |

<details>
<summary>Files not reviewed (7)</summary>

* **packages/cloudwatch/test/.eslintrc**: Language not supported
* **packages/datadog/test/.eslintrc**: Language not supported
* **packages/newrelic/test/.eslintrc**: Language not supported
* **packages/sentry/test/.eslintrc**: Language not supported
* **packages/statsd/test/.eslintrc**: Language not supported
* **packages/steveo/test/.eslintrc**: Language not supported
* **packages/storage-postgres/test/.eslintrc**: Language not supported
</details>

<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**packages/steveo/src/consumers/sqs.ts:287**
* [nitpick] The variable name 'topic' might be confusing since the context is about SQS queues rather than topics. Consider renaming it to 'queueName' if it represents the queue name for FIFO queues.
```
const topic = this.getTopic(item);
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-03-28T04:34:28Z
user: login: raadad
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T04:43:52Z
user: login: ellulpatrick
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T04:57:46Z

Review comments:
diff_hunk: @@ -284,8 +284,9 @@ class SqsRunner extends BaseRunner implements IRunner {
     if (!item) {
       throw new Error('No queues registered');
     }
+    const topic = this.getTopic(item);
path: packages/steveo/src/consumers/sqs.ts
user: login: ellulpatrick
body: ❤️ 
created_at: 2025-03-28T04:53:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,6 +8,91 @@ import Runner from '../../src/consumers/sqs';
 import { build } from '../../src/lib/pool';
 import Registry from '../../src/runtime/registry';
 import { Steveo } from '../../src';
+import {
+  DummyConfiguration,
+  IRegistry,
+  SQSConfiguration,
+} from '../../src/common';
+
+interface MockRegistryOptions {
+  subscribeStub?: sinon.SinonStub;
+  fifo?: boolean;
+  waitToCommit?: boolean;
+  emitStub?: sinon.SinonStub;
+}
+
+function createMockRegistry(
path: packages/steveo/test/consumer/sqs_test.ts
user: login: ellulpatrick
body: ❤️ 
created_at: 2025-03-28T04:56:24Z
author_association: CONTRIBUTOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-03-28T04:34:17Z
body: ###  🦋  Changeset detected

Latest commit: 4f897da984ca59c01b10702d453bbc916a32c730

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name   | Type  |
| ------ | ----- |
| steveo | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/ordermentum/steveo/new/health-check-fifo-compatibility?filename=.changeset/twelve-cobras-battle.md&value=---%0A%22%40steveojs%2Fcloudwatch%22%3A%20patch%0A%22%40steveojs%2Fdatadog%22%3A%20patch%0A%22%40steveojs%2Fnewrelic%22%3A%20patch%0A%22%40steveojs%2Fsentry%22%3A%20patch%0A%22%40steveojs%2Fstatsd%22%3A%20patch%0A%22steveo%22%3A%20patch%0A%22%40steveojs%2Fstorage-postgres%22%3A%20patch%0A---%0A%0A%5BDevX-432%5D%20Add%20SQS%20Health%20check%20fifo%20compatibility%0A)


author_association: NONE
user: login: deepsource-io[bot]
created_at: 2025-03-28T04:34:41Z
body: <div><p>Here's the code health analysis summary for commits <code>0de54de..4f897da</code>. <a href="https://app.deepsource.com/gh/ordermentum/steveo/run/2f1358ae-20d5-4747-9b90-4babf2cc5e62/">View details on DeepSource</a>&nbsp;↗.</p><h3>Analysis Summary</h3><table><thead><tr><th>Analyzer</th><th>Status</th><th>Summary</th><th>Link</th></tr></thead><tbody><tr><td><img src="https://static.deepsource.com/analyzer_logos/javascript.svg" alt="DeepSource JavaScript Logo" width="16px" height="16px"/><strong>JavaScript</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td><div>🎯 102 occurences resolved</div></td><td><a href="https://app.deepsource.com/gh/ordermentum/steveo/run/2f1358ae-20d5-4747-9b90-4babf2cc5e62/javascript/">View Check</a>&nbsp;↗</td></tr></tbody></table><hr/><blockquote><div>💡 If you’re a repository administrator, you can configure the quality gates from the <a href="https://app.deepsource.com/gh/ordermentum/steveo/settings/reporting">settings</a>.</div></blockquote></div>

author_association: NONE