Files
notification-wakeup-service/test/services/smsService.test.ts
T

175 lines
5.0 KiB
TypeScript

import assert from "node:assert/strict";
import { afterEach, beforeEach, describe, it } from "node:test";
import {
SMS_NOT_CONFIGURED,
consoleSmsSender,
resetSmsNotConfiguredWarning,
sendSms,
sendViaTwilio,
} from "../../src/services/smsService.js";
const TWILIO_KEYS = [
"TWILIO_ACCOUNT_SID",
"TWILIO_AUTH_TOKEN",
"TWILIO_FROM_NUMBER",
"TWILIO_MESSAGING_SERVICE_SID",
"NODE_ENV",
] as const;
let saved: Record<string, string | undefined>;
beforeEach(() => {
saved = {};
for (const key of TWILIO_KEYS) {
saved[key] = process.env[key];
delete process.env[key];
}
resetSmsNotConfiguredWarning();
});
afterEach(() => {
for (const key of TWILIO_KEYS) {
if (saved[key] === undefined) delete process.env[key];
else process.env[key] = saved[key];
}
});
type FetchArgs = { url: string; init: RequestInit };
function stubFetch(
response: { ok: boolean; status: number; body: unknown },
captured: FetchArgs[]
): typeof fetch {
return (async (url: string, init: RequestInit) => {
captured.push({ url, init });
return {
ok: response.ok,
status: response.status,
json: async () => response.body,
};
}) as unknown as typeof fetch;
}
describe("sendViaTwilio", () => {
const credentials = {
accountSid: "AC123",
authToken: "secret-token",
from: { From: "+15550000000" },
};
const originalFetch = globalThis.fetch;
afterEach(() => {
globalThis.fetch = originalFetch;
});
it("posts a form-encoded message and returns the sid", async () => {
const captured: FetchArgs[] = [];
globalThis.fetch = stubFetch(
{ ok: true, status: 201, body: { sid: "SM999" } },
captured
);
const result = await sendViaTwilio(credentials, "+15555550123", "hi");
assert.deepEqual(result, { status: "sent", messageId: "SM999" });
assert.equal(captured.length, 1);
assert.equal(
captured[0].url,
"https://api.twilio.com/2010-04-01/Accounts/AC123/Messages.json"
);
const headers = captured[0].init.headers as Record<string, string>;
assert.equal(
headers["Content-Type"],
"application/x-www-form-urlencoded"
);
assert.equal(
headers.Authorization,
"Basic " + Buffer.from("AC123:secret-token").toString("base64")
);
const form = new URLSearchParams(captured[0].init.body as string);
assert.equal(form.get("To"), "+15555550123");
assert.equal(form.get("Body"), "hi");
assert.equal(form.get("From"), "+15550000000");
});
it("sends MessagingServiceSid when that is how it is configured", async () => {
const captured: FetchArgs[] = [];
globalThis.fetch = stubFetch(
{ ok: true, status: 201, body: { sid: "SM1" } },
captured
);
await sendViaTwilio(
{
accountSid: "AC123",
authToken: "t",
from: { MessagingServiceSid: "MG9" },
},
"+15555550123",
"hi"
);
const form = new URLSearchParams(captured[0].init.body as string);
assert.equal(form.get("MessagingServiceSid"), "MG9");
assert.equal(form.get("From"), null);
});
it("reports the provider message on an error response", async () => {
globalThis.fetch = stubFetch(
{ ok: false, status: 400, body: { message: "Invalid 'To'" } },
[]
);
assert.deepEqual(await sendViaTwilio(credentials, "+1", "hi"), {
status: "failed",
error: "Invalid 'To'",
});
});
it("fails when a 2xx response carries no sid", async () => {
globalThis.fetch = stubFetch({ ok: true, status: 200, body: {} }, []);
const result = await sendViaTwilio(credentials, "+15555550123", "hi");
assert.equal(result.status, "failed");
});
it("turns a transport throw into a failed result", async () => {
globalThis.fetch = (async () => {
throw new Error("network down");
}) as unknown as typeof fetch;
assert.deepEqual(await sendViaTwilio(credentials, "+15555550123", "hi"), {
status: "failed",
error: "network down",
});
});
});
describe("sendSms configuration paths", () => {
it("fails with SMS_NOT_CONFIGURED when credentials are absent", async () => {
assert.deepEqual(await sendSms("+15555550123", "hi"), {
status: "failed",
error: SMS_NOT_CONFIGURED,
});
});
it("fails when an account is set but there is nothing to send from", async () => {
process.env.TWILIO_ACCOUNT_SID = "AC123";
process.env.TWILIO_AUTH_TOKEN = "t";
const result = await sendSms("+15555550123", "hi");
assert.equal(result.status, "failed");
assert.equal(
result.status === "failed" ? result.error : "",
SMS_NOT_CONFIGURED
);
});
it("uses the console adapter under test-local with no credentials", async () => {
process.env.NODE_ENV = "test-local";
const result = await sendSms("+15555550123", "hi");
assert.equal(result.status, "sent");
});
});
describe("consoleSmsSender", () => {
it("reports sent without touching the network", async () => {
const result = await consoleSmsSender("+15555550123", "hi");
assert.equal(result.status, "sent");
});
});