fix: license key not showing in email when created via admin page
Problem: The admin createKey action had two issues preventing the license key from appearing in the delivery email: 1. Wrong variable name: passed 'licenseKey' (camelCase) but the Resend template expects 'license_key' (snake_case). 2. Wrong template: used the default RESEND_TEMPLATE_ID (a generic template) instead of the 'license-key-delivery' template that the automation endpoint uses for license key emails. Solution: - Changed template variable from 'licenseKey' to 'license_key' - Added explicit templateId: 'license-key-delivery' to match the automation endpoint (api/v1/automations/create/account) - Updated email subject to 'Your Rhemawave License Key' for consistency Tests: Added page.server.spec.ts for the admin license-keys createKey action covering: unauthenticated rejection, missing email/level validation, user-not-found error, successful key creation with correct template ID and license_key variable, fallback to user.name when profile is missing, and graceful handling when email sending fails.
This commit is contained in:
@@ -60,10 +60,11 @@ export const actions = {
|
||||
const profile = await prisma.profile.findUnique({ where: { userId: user.id } });
|
||||
await sendMail({
|
||||
to: email,
|
||||
subject: 'Your License Key',
|
||||
subject: 'Your Rhemawave License Key',
|
||||
templateId: 'license-key-delivery',
|
||||
data: {
|
||||
firstName: profile?.firstName ?? user.name ?? 'Valued Customer',
|
||||
licenseKey: key
|
||||
license_key: key
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { isHttpError } from '@sveltejs/kit';
|
||||
import { createLocals, mockUser, createFormRequest } from '$lib/test-utils';
|
||||
|
||||
const { sendMail } = vi.hoisted(() => ({
|
||||
sendMail: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('$lib/server/resend', () => ({ sendMail }));
|
||||
|
||||
vi.mock('$lib/svelte-admin/registry', () => ({
|
||||
getModelConfig: vi.fn(() => ({ slug: 'license-keys', listFields: [], searchFields: [] }))
|
||||
}));
|
||||
|
||||
vi.mock('$lib/svelte-admin/crud', () => ({
|
||||
loadRecords: vi.fn().mockResolvedValue({ records: [], relationOptionsData: {} })
|
||||
}));
|
||||
|
||||
vi.mock('$lib/db', () => ({
|
||||
prisma: {
|
||||
user: { findUnique: vi.fn() },
|
||||
profile: { findUnique: vi.fn() },
|
||||
licenseKey: { create: vi.fn() },
|
||||
subscriptionLevel: { findMany: vi.fn() }
|
||||
}
|
||||
}));
|
||||
|
||||
import { prisma } from '$lib/db';
|
||||
import { actions } from './+page.server';
|
||||
|
||||
const mockFindUnique = prisma.user.findUnique as ReturnType<typeof vi.fn>;
|
||||
const mockProfileFindUnique = prisma.profile.findUnique as ReturnType<typeof vi.fn>;
|
||||
const mockLicenseKeyCreate = prisma.licenseKey.create as ReturnType<typeof vi.fn>;
|
||||
|
||||
describe('admin license-keys createKey action', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('rejects unauthenticated requests', async () => {
|
||||
const locals = createLocals();
|
||||
const req = createFormRequest({ email: 'jane@example.com', level: 'standard' });
|
||||
|
||||
const err = await actions.createKey({ request: req, locals } as any).catch((e) => e);
|
||||
expect(isHttpError(err, 401)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns error when email is missing', async () => {
|
||||
const locals = createLocals(mockUser() as any);
|
||||
const req = createFormRequest({ level: 'standard' });
|
||||
|
||||
const result = await actions.createKey({ request: req, locals } as any);
|
||||
expect(result).toEqual({ success: false, error: 'Email is required' });
|
||||
});
|
||||
|
||||
it('returns error when level is missing', async () => {
|
||||
const locals = createLocals(mockUser() as any);
|
||||
const req = createFormRequest({ email: 'jane@example.com' });
|
||||
|
||||
const result = await actions.createKey({ request: req, locals } as any);
|
||||
expect(result).toEqual({ success: false, error: 'Subscription level is required' });
|
||||
});
|
||||
|
||||
it('returns error when user is not found', async () => {
|
||||
mockFindUnique.mockResolvedValue(null);
|
||||
const locals = createLocals(mockUser() as any);
|
||||
const req = createFormRequest({ email: 'nobody@example.com', level: 'standard' });
|
||||
|
||||
const result = await actions.createKey({ request: req, locals } as any);
|
||||
expect(result).toEqual({ success: false, error: 'No user found with that email' });
|
||||
});
|
||||
|
||||
it('creates a license key and sends email with license_key variable', async () => {
|
||||
const user = mockUser();
|
||||
const profile = { firstName: 'Jane' };
|
||||
|
||||
mockFindUnique.mockResolvedValue(user);
|
||||
mockProfileFindUnique.mockResolvedValue(profile);
|
||||
mockLicenseKeyCreate.mockResolvedValue({});
|
||||
sendMail.mockResolvedValue({ id: 'email-1' });
|
||||
|
||||
const locals = createLocals(user as any);
|
||||
const req = createFormRequest({ email: user.email, level: 'standard' });
|
||||
|
||||
const result = await actions.createKey({ request: req, locals } as any);
|
||||
|
||||
expect(result).toEqual({ success: true, error: undefined });
|
||||
expect(mockLicenseKeyCreate).toHaveBeenCalledOnce();
|
||||
|
||||
const createCall = mockLicenseKeyCreate.mock.calls[0][0];
|
||||
expect(createCall.data.key).toMatch(/^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/);
|
||||
expect(createCall.data.active).toBe(true);
|
||||
expect(createCall.data.level).toBe('standard');
|
||||
expect(createCall.data.userId).toBe('user-1');
|
||||
|
||||
expect(sendMail).toHaveBeenCalledOnce();
|
||||
const mailCall = sendMail.mock.calls[0][0];
|
||||
expect(mailCall.to).toBe(user.email);
|
||||
expect(mailCall.templateId).toBe('license-key-delivery');
|
||||
expect(mailCall.data.license_key).toBeDefined();
|
||||
expect(mailCall.data.license_key).toBe(createCall.data.key);
|
||||
expect(mailCall.data.firstName).toBe('Jane');
|
||||
});
|
||||
|
||||
it('sends email with license_key even when profile is missing', async () => {
|
||||
const user = mockUser();
|
||||
|
||||
mockFindUnique.mockResolvedValue(user);
|
||||
mockProfileFindUnique.mockResolvedValue(null);
|
||||
mockLicenseKeyCreate.mockResolvedValue({});
|
||||
sendMail.mockResolvedValue({ id: 'email-1' });
|
||||
|
||||
const locals = createLocals(user as any);
|
||||
const req = createFormRequest({ email: user.email, level: 'premium' });
|
||||
|
||||
await actions.createKey({ request: req, locals } as any);
|
||||
|
||||
const mailCall = sendMail.mock.calls[0][0];
|
||||
expect(mailCall.templateId).toBe('license-key-delivery');
|
||||
expect(mailCall.data.license_key).toBeDefined();
|
||||
expect(mailCall.data.firstName).toBe('Jane Doe');
|
||||
});
|
||||
|
||||
it('still returns success when email sending fails', async () => {
|
||||
const user = mockUser();
|
||||
|
||||
mockFindUnique.mockResolvedValue(user);
|
||||
mockProfileFindUnique.mockResolvedValue({ firstName: 'Jane' });
|
||||
mockLicenseKeyCreate.mockResolvedValue({});
|
||||
sendMail.mockRejectedValue(new Error('Resend API down'));
|
||||
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
const locals = createLocals(user as any);
|
||||
const req = createFormRequest({ email: user.email, level: 'standard' });
|
||||
|
||||
const result = await actions.createKey({ request: req, locals } as any);
|
||||
expect(result).toEqual({ success: true, error: undefined });
|
||||
expect(mockLicenseKeyCreate).toHaveBeenCalledOnce();
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user