Backend features: - AWS SES email service with HTML templates - Email verification with dual method (link + 6-digit PIN code) - Password reset workflow with secure tokens - WSDC API proxy for dancer lookup and auto-fill registration - Extended User model with verification and WSDC fields - Email verification middleware for protected routes Frontend features: - Two-step registration with WSDC ID lookup - Password strength indicator component - Email verification page with code input - Password reset flow (request + reset pages) - Verification banner for unverified users - Updated authentication context and API service Testing: - 65 unit tests with 100% coverage of new features - Tests for auth utils, email service, WSDC controller, and middleware - Integration tests for full authentication flows - Comprehensive mocking of AWS SES and external APIs Database: - Migration: add WSDC fields (firstName, lastName, wsdcId) - Migration: add email verification fields (token, code, expiry) - Migration: add password reset fields (token, expiry) Documentation: - Complete Phase 1.5 documentation - Test suite documentation and best practices - Updated session context with new features
213 lines
5.7 KiB
JavaScript
213 lines
5.7 KiB
JavaScript
/**
|
|
* WSDC Controller Tests (Phase 1.5)
|
|
* Tests for WSDC API proxy functionality
|
|
*/
|
|
|
|
const request = require('supertest');
|
|
const app = require('../app');
|
|
|
|
// Mock fetch globally
|
|
global.fetch = jest.fn();
|
|
|
|
describe('WSDC Controller Tests (Phase 1.5)', () => {
|
|
beforeEach(() => {
|
|
fetch.mockClear();
|
|
});
|
|
|
|
describe('GET /api/wsdc/lookup', () => {
|
|
it('should lookup dancer by WSDC ID successfully', async () => {
|
|
const mockDancerData = {
|
|
dancer_wsdcid: 26997,
|
|
dancer_first: 'Radoslaw',
|
|
dancer_last: 'Gierwialo',
|
|
recent_year: 2025
|
|
};
|
|
|
|
fetch.mockResolvedValue({
|
|
ok: true,
|
|
json: async () => mockDancerData
|
|
});
|
|
|
|
const response = await request(app)
|
|
.get('/api/wsdc/lookup?id=26997');
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.success).toBe(true);
|
|
expect(response.body.dancer).toMatchObject({
|
|
wsdcId: 26997,
|
|
firstName: 'Radoslaw',
|
|
lastName: 'Gierwialo'
|
|
});
|
|
});
|
|
|
|
it('should return 400 if WSDC ID is missing', async () => {
|
|
const response = await request(app)
|
|
.get('/api/wsdc/lookup');
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Bad Request');
|
|
expect(response.body.message).toContain('WSDC ID is required');
|
|
});
|
|
|
|
it('should return 400 for invalid WSDC ID format', async () => {
|
|
const response = await request(app)
|
|
.get('/api/wsdc/lookup?id=abc123');
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Bad Request');
|
|
expect(response.body.message).toContain('Invalid WSDC ID format');
|
|
});
|
|
|
|
it('should return 400 for WSDC ID too long', async () => {
|
|
const response = await request(app)
|
|
.get('/api/wsdc/lookup?id=12345678901'); // 11 digits
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Bad Request');
|
|
});
|
|
|
|
it('should return 404 if dancer not found', async () => {
|
|
fetch.mockResolvedValue({
|
|
ok: true,
|
|
json: async () => ({}) // Empty response
|
|
});
|
|
|
|
const response = await request(app)
|
|
.get('/api/wsdc/lookup?id=99999');
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(response.body.error).toBe('Not Found');
|
|
expect(response.body.message).toContain('not found');
|
|
});
|
|
|
|
it('should return 502 if WSDC API fails', async () => {
|
|
fetch.mockResolvedValue({
|
|
ok: false,
|
|
status: 500,
|
|
statusText: 'Internal Server Error'
|
|
});
|
|
|
|
const response = await request(app)
|
|
.get('/api/wsdc/lookup?id=26997');
|
|
|
|
expect(response.status).toBe(502);
|
|
expect(response.body.error).toBe('Bad Gateway');
|
|
});
|
|
|
|
it('should handle network errors', async () => {
|
|
fetch.mockRejectedValue(new Error('Network error'));
|
|
|
|
const response = await request(app)
|
|
.get('/api/wsdc/lookup?id=26997');
|
|
|
|
expect(response.status).toBe(500);
|
|
expect(response.body.error).toBe('Internal Server Error');
|
|
});
|
|
|
|
it('should call WSDC API with correct URL', async () => {
|
|
fetch.mockResolvedValue({
|
|
ok: true,
|
|
json: async () => ({
|
|
dancer_wsdcid: 26997,
|
|
dancer_first: 'Test',
|
|
dancer_last: 'User'
|
|
})
|
|
});
|
|
|
|
await request(app)
|
|
.get('/api/wsdc/lookup?id=26997');
|
|
|
|
expect(fetch).toHaveBeenCalledWith(
|
|
'https://points.worldsdc.com/lookup2020/find?q=26997'
|
|
);
|
|
});
|
|
|
|
it('should validate numeric WSDC IDs only', async () => {
|
|
const invalidIds = ['abc', '123abc', 'test', '!@#$'];
|
|
|
|
for (const id of invalidIds) {
|
|
const response = await request(app)
|
|
.get(`/api/wsdc/lookup?id=${id}`);
|
|
|
|
expect(response.status).toBe(400);
|
|
}
|
|
});
|
|
|
|
it('should accept valid numeric WSDC IDs', async () => {
|
|
fetch.mockResolvedValue({
|
|
ok: true,
|
|
json: async () => ({
|
|
dancer_wsdcid: 123,
|
|
dancer_first: 'Test',
|
|
dancer_last: 'User'
|
|
})
|
|
});
|
|
|
|
const validIds = ['1', '123', '12345', '1234567890'];
|
|
|
|
for (const id of validIds) {
|
|
const response = await request(app)
|
|
.get(`/api/wsdc/lookup?id=${id}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
}
|
|
});
|
|
|
|
it('should include optional fields if available', async () => {
|
|
const mockDancerData = {
|
|
dancer_wsdcid: 26997,
|
|
dancer_first: 'Radoslaw',
|
|
dancer_last: 'Gierwialo',
|
|
recent_year: 2025,
|
|
dominate_data: {
|
|
short_dominate_role: 'Leader'
|
|
}
|
|
};
|
|
|
|
fetch.mockResolvedValue({
|
|
ok: true,
|
|
json: async () => mockDancerData
|
|
});
|
|
|
|
const response = await request(app)
|
|
.get('/api/wsdc/lookup?id=26997');
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.dancer.recentYear).toBe(2025);
|
|
expect(response.body.dancer.dominateRole).toBe('Leader');
|
|
});
|
|
|
|
it('should handle missing optional fields gracefully', async () => {
|
|
const mockDancerData = {
|
|
dancer_wsdcid: 26997,
|
|
dancer_first: 'Test',
|
|
dancer_last: 'User'
|
|
// No recent_year or dominate_data
|
|
};
|
|
|
|
fetch.mockResolvedValue({
|
|
ok: true,
|
|
json: async () => mockDancerData
|
|
});
|
|
|
|
const response = await request(app)
|
|
.get('/api/wsdc/lookup?id=26997');
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.dancer.dominateRole).toBeNull();
|
|
});
|
|
|
|
it('should log errors for debugging', async () => {
|
|
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
|
|
fetch.mockRejectedValue(new Error('Test error'));
|
|
|
|
await request(app)
|
|
.get('/api/wsdc/lookup?id=26997');
|
|
|
|
expect(consoleSpy).toHaveBeenCalled();
|
|
consoleSpy.mockRestore();
|
|
});
|
|
});
|
|
});
|