Files
portal/src/routes/reset-password/page.server.spec.ts
T
NeilClack 38d1752f5b admin: Superforms + Zod validation, three-address-defaults schema, profiles toast fix
- Add Superforms + Zod 4 for all admin form validation
- Replace address isDefault with three separate boolean defaults (Mailing, Billing, Shipping)
- Update profiles page with three-checkbox address form and toast improvements
- Convert all admin page server actions to pass request directly to crud
- Export superFormErrorsToFieldErrors helper for profiles server actions
- Regenerate Prisma client for new schema fields
2026-07-07 13:36:23 -05:00

195 lines
5.6 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { isRedirect, isActionFailure } from '@sveltejs/kit';
import { createFormRequest, createUrl } from '$lib/test-utils';
const { resetPassword } = vi.hoisted(() => ({
resetPassword: vi.fn()
}));
const { findFirst } = vi.hoisted(() => ({
findFirst: vi.fn()
}));
vi.mock('$lib/db', () => ({
prisma: {
verification: {
findFirst
}
}
}));
vi.mock('$lib/auth', () => ({
auth: {
api: {
resetPassword
}
}
}));
import { load, actions } from './+page.server';
describe('reset-password page server load', () => {
beforeEach(() => {
findFirst.mockReset();
});
it('redirects to / when token is missing', async () => {
const url = createUrl('/reset-password');
const err = await Promise.resolve(load({ url } as any)).catch((e: unknown) => e);
expect(isRedirect(err)).toBe(true);
if (isRedirect(err)) {
expect(err.location).toBe('/');
}
expect(findFirst).not.toHaveBeenCalled();
});
it('redirects to / when verification record is not found', async () => {
findFirst.mockResolvedValue(null);
const url = createUrl('/reset-password', { token: 'invalid-token' });
const err = await Promise.resolve(load({ url } as any)).catch((e: unknown) => e);
expect(isRedirect(err)).toBe(true);
if (isRedirect(err)) {
expect(err.location).toBe('/');
}
expect(findFirst).toHaveBeenCalledWith({
where: {
value: 'invalid-token',
expiresAt: { gt: expect.any(Date) }
}
});
});
it('returns undefined when token is valid (verification found)', async () => {
findFirst.mockResolvedValue({ id: 'v-1', value: 'valid-token' });
const url = createUrl('/reset-password', { token: 'valid-token' });
const result = await load({ url } as any);
expect(result).toBeUndefined();
});
it('redirects to / when verification is expired', async () => {
findFirst.mockResolvedValue(null);
const url = createUrl('/reset-password', { token: 'expired-token' });
const err = await Promise.resolve(load({ url } as any)).catch((e: unknown) => e);
expect(isRedirect(err)).toBe(true);
if (isRedirect(err)) {
expect(err.location).toBe('/');
}
});
});
describe('reset-password page server action', () => {
beforeEach(() => {
resetPassword.mockReset();
});
const invokeDefault = (url: URL, request: Request) => actions.default({ request, url } as any);
it('returns fail(400) when token is missing', async () => {
const url = createUrl('/reset-password');
const result = await invokeDefault(url, createFormRequest({}));
expect(isActionFailure(result)).toBe(true);
if (isActionFailure(result)) {
expect(result.data).toEqual({ error: 'Invalid or missing reset token.' });
}
expect(resetPassword).not.toHaveBeenCalled();
});
it('returns fail(400) when password is too short', async () => {
const url = createUrl('/reset-password', { token: 'token-1' });
const result = await invokeDefault(
url,
createFormRequest({ password: 'short', password2: 'short' })
);
expect(isActionFailure(result)).toBe(true);
if (isActionFailure(result)) {
expect(result.data).toEqual({ error: 'Password must be at least 8 characters.' });
}
expect(resetPassword).not.toHaveBeenCalled();
});
it('returns fail(400) when password is empty', async () => {
const url = createUrl('/reset-password', { token: 'token-1' });
const result = await invokeDefault(url, createFormRequest({ password: '', password2: '' }));
expect(isActionFailure(result)).toBe(true);
if (isActionFailure(result)) {
expect(result.data).toEqual({ error: 'Password must be at least 8 characters.' });
}
expect(resetPassword).not.toHaveBeenCalled();
});
it('returns fail(400) when passwords do not match', async () => {
const url = createUrl('/reset-password', { token: 'token-1' });
const result = await invokeDefault(
url,
createFormRequest({ password: 'longenough1', password2: 'longenough2' })
);
expect(isActionFailure(result)).toBe(true);
if (isActionFailure(result)) {
expect(result.data).toEqual({ error: 'Passwords do not match.' });
}
expect(resetPassword).not.toHaveBeenCalled();
});
it('returns fail(400) when resetPassword throws', async () => {
resetPassword.mockRejectedValue(new Error('Token expired'));
const url = createUrl('/reset-password', { token: 'token-1' });
const result = await invokeDefault(
url,
createFormRequest({ password: 'longenough1', password2: 'longenough1' })
);
expect(isActionFailure(result)).toBe(true);
if (isActionFailure(result)) {
expect(result.data).toEqual({ error: 'Token expired' });
}
});
it('returns fail(400) with default message when non-Error is thrown', async () => {
resetPassword.mockRejectedValue(null);
const url = createUrl('/reset-password', { token: 'token-1' });
const result = await invokeDefault(
url,
createFormRequest({ password: 'longenough1', password2: 'longenough1' })
);
expect(isActionFailure(result)).toBe(true);
if (isActionFailure(result)) {
expect(result.data).toEqual({ error: 'Failed to reset password.' });
}
});
it('redirects to /login on successful reset', async () => {
resetPassword.mockResolvedValue(undefined);
const url = createUrl('/reset-password', { token: 'token-1' });
const err = await Promise.resolve(
invokeDefault(url, createFormRequest({ password: 'longenough1', password2: 'longenough1' }))
).catch((e: unknown) => e);
expect(isRedirect(err)).toBe(true);
if (isRedirect(err)) {
expect(err.status).toBe(303);
expect(err.location).toBe('/login');
}
expect(resetPassword).toHaveBeenCalledWith({
body: {
newPassword: 'longenough1',
token: 'token-1'
}
});
});
});