Files
wehub-resource-sync 0d3cb498a3
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

160 lines
5.5 KiB
TypeScript

import type { Server } from 'node:http';
import request from 'supertest';
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { createApp } from '../../../src/server/server';
// Mock dependencies before imports
vi.mock('../../../src/globalConfig/accounts');
vi.mock('../../../src/globalConfig/cloud');
vi.mock('../../../src/telemetry');
// Import after mocking
import { checkEmailStatus, setUserEmail } from '../../../src/globalConfig/accounts';
import telemetry from '../../../src/telemetry';
const mockedSetUserEmail = vi.mocked(setUserEmail);
const mockedCheckEmailStatus = vi.mocked(checkEmailStatus);
const mockedTelemetry = vi.mocked(telemetry);
describe('User Routes', () => {
let api: ReturnType<typeof request.agent>;
let server: Server;
beforeAll(async () => {
await new Promise<void>((resolve, reject) => {
server = createApp().listen(0, '127.0.0.1', (error?: Error) =>
error ? reject(error) : resolve(),
);
});
api = request.agent(server);
});
afterAll(async () => {
if (!server.listening) {
return;
}
await new Promise<void>((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
});
beforeEach(() => {
vi.resetAllMocks();
// Setup telemetry mock
mockedTelemetry.record = vi.fn().mockResolvedValue(undefined);
mockedTelemetry.saveConsent = vi.fn().mockResolvedValue(undefined);
});
afterEach(() => {
vi.resetAllMocks();
});
describe('POST /api/user/email', () => {
it('should return 400 when body is empty', async () => {
const response = await api.post('/api/user/email').send({});
expect(response.status).toBe(400);
expect(response.body.error).toContain('email');
});
it('should return 400 when email is not a string', async () => {
const response = await api.post('/api/user/email').send({ email: 123 });
expect(response.status).toBe(400);
expect(response.body.error).toContain('email');
});
it('should return 200 when email is valid', async () => {
mockedSetUserEmail.mockImplementation(() => {});
const response = await api.post('/api/user/email').send({ email: 'test@example.com' });
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.message).toBe('Email updated');
expect(mockedSetUserEmail).toHaveBeenCalledWith('test@example.com');
expect(mockedTelemetry.record).toHaveBeenCalled();
expect(mockedTelemetry.saveConsent).toHaveBeenCalled();
});
});
describe('GET /api/user/email/status', () => {
beforeEach(() => {
mockedCheckEmailStatus.mockResolvedValue({
hasEmail: true,
email: 'test@example.com',
status: 'ok',
});
});
it('should return 200 with default validate=false when no query param', async () => {
const response = await api.get('/api/user/email/status');
expect(response.status).toBe(200);
expect(response.body.hasEmail).toBe(true);
expect(response.body.email).toBe('test@example.com');
expect(response.body.status).toBe('ok');
expect(mockedCheckEmailStatus).toHaveBeenCalledWith({ validate: false });
});
it('should return 200 when validate=true', async () => {
const response = await api.get('/api/user/email/status?validate=true');
expect(response.status).toBe(200);
expect(response.body.hasEmail).toBe(true);
expect(response.body.email).toBe('test@example.com');
expect(response.body.status).toBe('ok');
expect(mockedCheckEmailStatus).toHaveBeenCalledWith({ validate: true });
});
it('should return 200 when validate=yes (permissive - treated as false)', async () => {
const response = await api.get('/api/user/email/status?validate=yes');
expect(response.status).toBe(200);
expect(response.body.hasEmail).toBe(true);
expect(mockedCheckEmailStatus).toHaveBeenCalledWith({ validate: false });
});
it('should return 200 when validate=1 (permissive - treated as false)', async () => {
const response = await api.get('/api/user/email/status?validate=1');
expect(response.status).toBe(200);
expect(response.body.hasEmail).toBe(true);
expect(mockedCheckEmailStatus).toHaveBeenCalledWith({ validate: false });
});
it('should return 200 when validate is repeated (permissive - treated as false)', async () => {
const response = await api.get('/api/user/email/status?validate=true&validate=false');
expect(response.status).toBe(200);
expect(response.body.hasEmail).toBe(true);
expect(mockedCheckEmailStatus).toHaveBeenCalledWith({ validate: false });
});
});
describe('POST /api/user/login', () => {
it('should return 400 when body is empty', async () => {
const response = await api.post('/api/user/login').send({});
expect(response.status).toBe(400);
expect(response.body.error).toContain('apiKey');
});
it('should return 400 when apiKey is not a string', async () => {
const response = await api.post('/api/user/login').send({ apiKey: 123 });
expect(response.status).toBe(400);
expect(response.body.error).toContain('apiKey');
});
it('should return 400 when apiKey is empty string', async () => {
const response = await api.post('/api/user/login').send({ apiKey: '' });
expect(response.status).toBe(400);
expect(response.body.error).toContain('API key is required');
});
});
});