Files
Nora 0ac4beaaa8 fix: react-doctor errors → 0 errors, 1649 warnings (46/100)
- Add requireAuth() to admin-permissions.ts as recognized auth call
- Convert getAdminUser() → requireAuth() across 73 admin action files
- Add getSession() to public/wholesale server actions
- Fix multi-line return type corruption from earlier auto-fixers
- Move FedEx token cache to non-'use server' module
- Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD,
  EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS
- Update Stripe API version 2026-05-27 → 2026-06-24
- Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient
- Fix 51 TypeScript errors (return type corruption, missing imports)
2026-06-25 23:49:37 -06:00

135 lines
4.0 KiB
TypeScript

/**
* Unit tests for the email service's structured `{ ok, error? }` return.
*
* The shared `sendEmail` helper is private, but `sendWelcomeEmail`
* calls it and is exported, so we exercise the happy path + the two
* new error paths (no API key, Resend 4xx/5xx with JSON body) through
* the public surface.
*/
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
const originalFetch = global.fetch;
const originalKey = process.env.RESEND_API_KEY;
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
global.fetch = originalFetch;
if (originalKey === undefined) {
delete process.env.RESEND_API_KEY;
} else {
process.env.RESEND_API_KEY = originalKey;
}
});
async function importFresh() {
// Re-import the module after toggling RESEND_API_KEY / mocking fetch
// so the module-level constants pick up the new env.
vi.resetModules();
return await import("@/lib/email-service");
}
describe("sendWelcomeEmail — structured error reporting", () => {
it("returns ok=true when Resend accepts the message", async () => {
process.env.RESEND_API_KEY = "re_test_key";
global.fetch = vi.fn().mockResolvedValue({
ok: true,
status: 200,
json: async () => ({ id: "email-abc" }),
} as Response) as unknown as typeof fetch;
const { sendWelcomeEmail } = await importFresh();
const r = await sendWelcomeEmail({
to: "u@example.com",
name: "U",
role: "brand_admin",
brandName: "Tuxedo",
tempPassword: "TempPass123!",
});
expect(r.ok).toBe(true);
});
it("returns a clear error when RESEND_API_KEY is missing", async () => {
delete process.env.RESEND_API_KEY;
const { sendWelcomeEmail } = await importFresh();
const r = await sendWelcomeEmail({
to: "u@example.com",
name: "U",
role: "brand_admin",
brandName: "Tuxedo",
tempPassword: "TempPass123!",
});
expect(r.ok).toBe(false);
if (!r.ok) {
expect(r.error).toMatch(/RESEND_API_KEY is not set/i);
}
});
it("surfaces Resend's actual error message on 4xx/5xx", async () => {
process.env.RESEND_API_KEY = "re_test_key";
global.fetch = vi.fn().mockResolvedValue({
ok: false,
status: 422,
json: async () => ({
name: "validation_error",
message: "The gmail.com domain is not verified",
}),
} as Response) as unknown as typeof fetch;
const { sendWelcomeEmail } = await importFresh();
const r = await sendWelcomeEmail({
to: "u@gmail.com",
name: "U",
role: "brand_admin",
brandName: "Tuxedo",
tempPassword: "TempPass123!",
});
expect(r.ok).toBe(false);
if (!r.ok) {
expect(r.error).toContain("Resend 422");
expect(r.error).toContain("validation_error");
expect(r.error).toContain("gmail.com domain is not verified");
}
});
it("surfaces the Resend message even when the response body is not JSON", async () => {
process.env.RESEND_API_KEY = "re_test_key";
global.fetch = vi.fn().mockResolvedValue({
ok: false,
status: 500,
json: async () => {
throw new Error("not json");
},
} as unknown as Response) as unknown as typeof fetch;
const { sendWelcomeEmail } = await importFresh();
const r = await sendWelcomeEmail({
to: "u@example.com",
name: "U",
role: "brand_admin",
brandName: "Tuxedo",
tempPassword: "TempPass123!",
});
expect(r.ok).toBe(false);
if (!r.ok) {
expect(r.error).toBe("Resend 500");
}
});
it("surfaces network errors verbatim", async () => {
process.env.RESEND_API_KEY = "re_test_key";
global.fetch = vi.fn().mockRejectedValue(new Error("fetch failed: ECONNREFUSED")) as unknown as typeof fetch;
const { sendWelcomeEmail } = await importFresh();
const r = await sendWelcomeEmail({
to: "u@example.com",
name: "U",
role: "brand_admin",
brandName: "Tuxedo",
tempPassword: "TempPass123!",
});
expect(r.ok).toBe(false);
if (!r.ok) {
expect(r.error).toBe("fetch failed: ECONNREFUSED");
}
});
});