/ tests / inbound / inbound-processor-outer-catch.test.js
inbound-processor-outer-catch.test.js
 1  /**
 2   * Tests for Inbound Processor - outer catch blocks
 3   * Lines 69-71 (pollAllChannels) and 113-115 (processAllReplies)
 4   *
 5   * These outer catch blocks fire when a successful handler return value
 6   * causes the post-processing code (e.g. results.sms.stored) to throw.
 7   * We achieve this by mocking handlers to return null so that accessing
 8   * .stored / .sent on null throws a TypeError in the outer try block.
 9   */
10  
11  import { test, mock } from 'node:test';
12  import assert from 'node:assert/strict';
13  
14  // Mock SMS to return null (not throw) so results.sms = null
15  // Then results.sms.stored at line 62 throws TypeError (outer catch, lines 69-71)
16  mock.module('../../src/inbound/sms.js', {
17    namedExports: {
18      pollInboundSMS: async () => null,
19      processPendingReplies: async () => null,
20    },
21  });
22  
23  // Mock email to return null similarly
24  mock.module('../../src/inbound/email.js', {
25    namedExports: {
26      pollInboundEmails: async () => null,
27      processPendingReplies: async () => null,
28    },
29  });
30  
31  const { pollAllChannels, processAllReplies } = await import('../../src/inbound/processor.js');
32  
33  test('pollAllChannels outer catch: throws when handler returns null (results.sms.stored fails)', async () => {
34    // When pollInboundSMS returns null, results.sms = null
35    // Then results.sms.stored at line 62 throws TypeError
36    // This triggers the outer catch at lines 69-71 which re-throws
37    await assert.rejects(
38      () => pollAllChannels(),
39      err => {
40        // Should be a TypeError from null.stored
41        assert.ok(err instanceof TypeError, `Expected TypeError, got: ${err.constructor.name}`);
42        return true;
43      }
44    );
45  });
46  
47  test('processAllReplies outer catch: throws when handler returns null (results.sms.sent fails)', async () => {
48    // When processPendingReplies returns null, results.sms = null
49    // Then results.sms.sent at line 106 throws TypeError
50    // This triggers the outer catch at lines 113-115 which re-throws
51    await assert.rejects(
52      () => processAllReplies(),
53      err => {
54        assert.ok(err instanceof TypeError, `Expected TypeError, got: ${err.constructor.name}`);
55        return true;
56      }
57    );
58  });
59  
60  test('pollAllChannels outer catch: error is a TypeError about null property access', async () => {
61    try {
62      await pollAllChannels();
63      assert.fail('Should have thrown');
64    } catch (err) {
65      assert.ok(err instanceof TypeError);
66      // Error message should mention null/undefined property access
67      assert.ok(
68        err.message.includes('null') ||
69          err.message.includes('undefined') ||
70          err.message.includes('Cannot'),
71        `Unexpected error message: ${err.message}`
72      );
73    }
74  });
75  
76  test('processAllReplies outer catch: error is a TypeError about null property access', async () => {
77    try {
78      await processAllReplies();
79      assert.fail('Should have thrown');
80    } catch (err) {
81      assert.ok(err instanceof TypeError);
82      assert.ok(
83        err.message.includes('null') ||
84          err.message.includes('undefined') ||
85          err.message.includes('Cannot'),
86        `Unexpected error message: ${err.message}`
87      );
88    }
89  });