mirror of
https://github.com/geoffsee/open-gsio.git
synced 2025-09-08 22:56:46 +00:00
Add unit tests for ContactService, FeedbackService, and MetricsService
This commit is contained in:
150
workers/site/services/__tests__/ContactService.test.ts
Normal file
150
workers/site/services/__tests__/ContactService.test.ts
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import { getSnapshot } from 'mobx-state-tree';
|
||||||
|
import ContactService from '../ContactService';
|
||||||
|
import ContactRecord from '../../models/ContactRecord';
|
||||||
|
|
||||||
|
describe('ContactService', () => {
|
||||||
|
let contactService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Create a new instance of the service before each test
|
||||||
|
contactService = ContactService.create();
|
||||||
|
|
||||||
|
// Reset mocks
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Initial state', () => {
|
||||||
|
it('should have empty env and ctx objects initially', () => {
|
||||||
|
expect(contactService.env).toEqual({});
|
||||||
|
expect(contactService.ctx).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setEnv', () => {
|
||||||
|
it('should set the environment', () => {
|
||||||
|
const mockEnv = { KV_STORAGE: { put: vi.fn() }, EMAIL_SERVICE: { sendMail: vi.fn() } };
|
||||||
|
contactService.setEnv(mockEnv);
|
||||||
|
expect(contactService.env).toEqual(mockEnv);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setCtx', () => {
|
||||||
|
it('should set the execution context', () => {
|
||||||
|
const mockCtx = { waitUntil: vi.fn() };
|
||||||
|
contactService.setCtx(mockCtx);
|
||||||
|
expect(contactService.ctx).toEqual(mockCtx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('handleContact', () => {
|
||||||
|
it('should process a valid contact request and return a success response', async () => {
|
||||||
|
// Mock crypto.randomUUID
|
||||||
|
vi.stubGlobal('crypto', {
|
||||||
|
randomUUID: vi.fn().mockReturnValue('mock-uuid'),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock date for consistent testing
|
||||||
|
const mockDate = new Date('2023-01-01T12:00:00Z');
|
||||||
|
vi.useFakeTimers();
|
||||||
|
vi.setSystemTime(mockDate);
|
||||||
|
|
||||||
|
// Create mock request data
|
||||||
|
const contactData = {
|
||||||
|
markdown: 'Test message',
|
||||||
|
email: 'test@example.com',
|
||||||
|
firstname: 'John',
|
||||||
|
lastname: 'Doe',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create mock request
|
||||||
|
const mockRequest = {
|
||||||
|
json: vi.fn().mockResolvedValue(contactData),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create mock environment
|
||||||
|
const mockEnv = {
|
||||||
|
KV_STORAGE: {
|
||||||
|
put: vi.fn().mockResolvedValue(undefined),
|
||||||
|
},
|
||||||
|
EMAIL_SERVICE: {
|
||||||
|
sendMail: vi.fn().mockResolvedValue(undefined),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the environment
|
||||||
|
contactService.setEnv(mockEnv);
|
||||||
|
|
||||||
|
// Call the method
|
||||||
|
const result = await contactService.handleContact(mockRequest as any);
|
||||||
|
|
||||||
|
// Verify KV_STORAGE.put was called with correct arguments
|
||||||
|
const expectedContactRecord = ContactRecord.create({
|
||||||
|
message: contactData.markdown,
|
||||||
|
timestamp: mockDate.toISOString(),
|
||||||
|
email: contactData.email,
|
||||||
|
firstname: contactData.firstname,
|
||||||
|
lastname: contactData.lastname,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockEnv.KV_STORAGE.put).toHaveBeenCalledWith(
|
||||||
|
'contact:mock-uuid',
|
||||||
|
JSON.stringify(getSnapshot(expectedContactRecord)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify EMAIL_SERVICE.sendMail was called with correct arguments
|
||||||
|
expect(mockEnv.EMAIL_SERVICE.sendMail).toHaveBeenCalledWith({
|
||||||
|
to: 'geoff@seemueller.io',
|
||||||
|
plaintextMessage: expect.stringContaining(contactData.markdown),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify result is a success Response
|
||||||
|
expect(result).toBeInstanceOf(Response);
|
||||||
|
expect(result.status).toBe(200);
|
||||||
|
|
||||||
|
// Verify response body
|
||||||
|
const text = await result.clone().text();
|
||||||
|
expect(text).toBe('Contact record saved successfully');
|
||||||
|
|
||||||
|
// Restore real timers
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a 500 response when an error occurs', async () => {
|
||||||
|
// Create mock request that throws an error
|
||||||
|
const mockRequest = {
|
||||||
|
json: vi.fn().mockRejectedValue(new Error('Invalid JSON')),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create mock environment
|
||||||
|
const mockEnv = {
|
||||||
|
KV_STORAGE: {
|
||||||
|
put: vi.fn(),
|
||||||
|
},
|
||||||
|
EMAIL_SERVICE: {
|
||||||
|
sendMail: vi.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the environment
|
||||||
|
contactService.setEnv(mockEnv);
|
||||||
|
|
||||||
|
// Call the method
|
||||||
|
const result = await contactService.handleContact(mockRequest as any);
|
||||||
|
|
||||||
|
// Verify KV_STORAGE.put was not called
|
||||||
|
expect(mockEnv.KV_STORAGE.put).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Verify EMAIL_SERVICE.sendMail was not called
|
||||||
|
expect(mockEnv.EMAIL_SERVICE.sendMail).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Verify result is an error Response
|
||||||
|
expect(result).toBeInstanceOf(Response);
|
||||||
|
expect(result.status).toBe(500);
|
||||||
|
|
||||||
|
// Verify response body
|
||||||
|
const text = await result.clone().text();
|
||||||
|
expect(text).toBe('Failed to process contact request');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
203
workers/site/services/__tests__/FeedbackService.test.ts
Normal file
203
workers/site/services/__tests__/FeedbackService.test.ts
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import { getSnapshot } from 'mobx-state-tree';
|
||||||
|
import FeedbackService from '../FeedbackService';
|
||||||
|
import FeedbackRecord from '../../models/FeedbackRecord';
|
||||||
|
|
||||||
|
describe('FeedbackService', () => {
|
||||||
|
let feedbackService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Create a new instance of the service before each test
|
||||||
|
feedbackService = FeedbackService.create();
|
||||||
|
|
||||||
|
// Reset mocks
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Initial state', () => {
|
||||||
|
it('should have empty env and ctx objects initially', () => {
|
||||||
|
expect(feedbackService.env).toEqual({});
|
||||||
|
expect(feedbackService.ctx).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setEnv', () => {
|
||||||
|
it('should set the environment', () => {
|
||||||
|
const mockEnv = { KV_STORAGE: { put: vi.fn() }, EMAIL_SERVICE: { sendMail: vi.fn() } };
|
||||||
|
feedbackService.setEnv(mockEnv);
|
||||||
|
expect(feedbackService.env).toEqual(mockEnv);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setCtx', () => {
|
||||||
|
it('should set the execution context', () => {
|
||||||
|
const mockCtx = { waitUntil: vi.fn() };
|
||||||
|
feedbackService.setCtx(mockCtx);
|
||||||
|
expect(feedbackService.ctx).toEqual(mockCtx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('handleFeedback', () => {
|
||||||
|
it('should process a valid feedback request and return a success response', async () => {
|
||||||
|
// Mock crypto.randomUUID
|
||||||
|
vi.stubGlobal('crypto', {
|
||||||
|
randomUUID: vi.fn().mockReturnValue('mock-uuid'),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock date for consistent testing
|
||||||
|
const mockDate = new Date('2023-01-01T12:00:00Z');
|
||||||
|
vi.useFakeTimers();
|
||||||
|
vi.setSystemTime(mockDate);
|
||||||
|
|
||||||
|
// Create mock request data
|
||||||
|
const feedbackData = {
|
||||||
|
feedback: 'This is a test feedback',
|
||||||
|
user: 'TestUser',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create mock request
|
||||||
|
const mockRequest = {
|
||||||
|
json: vi.fn().mockResolvedValue(feedbackData),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create mock environment
|
||||||
|
const mockEnv = {
|
||||||
|
KV_STORAGE: {
|
||||||
|
put: vi.fn().mockResolvedValue(undefined),
|
||||||
|
},
|
||||||
|
EMAIL_SERVICE: {
|
||||||
|
sendMail: vi.fn().mockResolvedValue(undefined),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the environment
|
||||||
|
feedbackService.setEnv(mockEnv);
|
||||||
|
|
||||||
|
// Call the method
|
||||||
|
const result = await feedbackService.handleFeedback(mockRequest as any);
|
||||||
|
|
||||||
|
// Verify KV_STORAGE.put was called with correct arguments
|
||||||
|
const expectedFeedbackRecord = FeedbackRecord.create({
|
||||||
|
feedback: feedbackData.feedback,
|
||||||
|
timestamp: mockDate.toISOString(),
|
||||||
|
user: feedbackData.user,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockEnv.KV_STORAGE.put).toHaveBeenCalledWith(
|
||||||
|
'feedback:mock-uuid',
|
||||||
|
JSON.stringify(getSnapshot(expectedFeedbackRecord)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify EMAIL_SERVICE.sendMail was called with correct arguments
|
||||||
|
expect(mockEnv.EMAIL_SERVICE.sendMail).toHaveBeenCalledWith({
|
||||||
|
to: 'geoff@seemueller.io',
|
||||||
|
plaintextMessage: expect.stringContaining(feedbackData.feedback),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify result is a success Response
|
||||||
|
expect(result).toBeInstanceOf(Response);
|
||||||
|
expect(result.status).toBe(200);
|
||||||
|
|
||||||
|
// Verify response body
|
||||||
|
const text = await result.clone().text();
|
||||||
|
expect(text).toBe('Feedback saved successfully');
|
||||||
|
|
||||||
|
// Restore real timers
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use default values when not provided in the request', async () => {
|
||||||
|
// Mock crypto.randomUUID
|
||||||
|
vi.stubGlobal('crypto', {
|
||||||
|
randomUUID: vi.fn().mockReturnValue('mock-uuid'),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock date for consistent testing
|
||||||
|
const mockDate = new Date('2023-01-01T12:00:00Z');
|
||||||
|
vi.useFakeTimers();
|
||||||
|
vi.setSystemTime(mockDate);
|
||||||
|
|
||||||
|
// Create mock request data with only feedback
|
||||||
|
const feedbackData = {
|
||||||
|
feedback: 'This is a test feedback',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create mock request
|
||||||
|
const mockRequest = {
|
||||||
|
json: vi.fn().mockResolvedValue(feedbackData),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create mock environment
|
||||||
|
const mockEnv = {
|
||||||
|
KV_STORAGE: {
|
||||||
|
put: vi.fn().mockResolvedValue(undefined),
|
||||||
|
},
|
||||||
|
EMAIL_SERVICE: {
|
||||||
|
sendMail: vi.fn().mockResolvedValue(undefined),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the environment
|
||||||
|
feedbackService.setEnv(mockEnv);
|
||||||
|
|
||||||
|
// Call the method
|
||||||
|
const result = await feedbackService.handleFeedback(mockRequest as any);
|
||||||
|
|
||||||
|
// Verify KV_STORAGE.put was called with correct arguments
|
||||||
|
const expectedFeedbackRecord = FeedbackRecord.create({
|
||||||
|
feedback: feedbackData.feedback,
|
||||||
|
timestamp: mockDate.toISOString(),
|
||||||
|
user: 'Anonymous', // Default value
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockEnv.KV_STORAGE.put).toHaveBeenCalledWith(
|
||||||
|
'feedback:mock-uuid',
|
||||||
|
JSON.stringify(getSnapshot(expectedFeedbackRecord)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify result is a success Response
|
||||||
|
expect(result).toBeInstanceOf(Response);
|
||||||
|
expect(result.status).toBe(200);
|
||||||
|
|
||||||
|
// Restore real timers
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a 500 response when an error occurs', async () => {
|
||||||
|
// Create mock request that throws an error
|
||||||
|
const mockRequest = {
|
||||||
|
json: vi.fn().mockRejectedValue(new Error('Invalid JSON')),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create mock environment
|
||||||
|
const mockEnv = {
|
||||||
|
KV_STORAGE: {
|
||||||
|
put: vi.fn(),
|
||||||
|
},
|
||||||
|
EMAIL_SERVICE: {
|
||||||
|
sendMail: vi.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the environment
|
||||||
|
feedbackService.setEnv(mockEnv);
|
||||||
|
|
||||||
|
// Call the method
|
||||||
|
const result = await feedbackService.handleFeedback(mockRequest as any);
|
||||||
|
|
||||||
|
// Verify KV_STORAGE.put was not called
|
||||||
|
expect(mockEnv.KV_STORAGE.put).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Verify EMAIL_SERVICE.sendMail was not called
|
||||||
|
expect(mockEnv.EMAIL_SERVICE.sendMail).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Verify result is an error Response
|
||||||
|
expect(result).toBeInstanceOf(Response);
|
||||||
|
expect(result.status).toBe(500);
|
||||||
|
|
||||||
|
// Verify response body
|
||||||
|
const text = await result.clone().text();
|
||||||
|
expect(text).toBe('Failed to process feedback request');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
136
workers/site/services/__tests__/MetricsService.test.ts
Normal file
136
workers/site/services/__tests__/MetricsService.test.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import MetricsService from '../MetricsService';
|
||||||
|
|
||||||
|
describe('MetricsService', () => {
|
||||||
|
let metricsService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Create a new instance of the service before each test
|
||||||
|
metricsService = MetricsService.create();
|
||||||
|
|
||||||
|
// Reset mocks
|
||||||
|
vi.resetAllMocks();
|
||||||
|
|
||||||
|
// Mock fetch
|
||||||
|
global.fetch = vi.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Initial state', () => {
|
||||||
|
it('should have empty env and ctx objects initially', () => {
|
||||||
|
expect(metricsService.env).toEqual({});
|
||||||
|
expect(metricsService.ctx).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have isCollectingMetrics set to true by default', () => {
|
||||||
|
expect(metricsService.isCollectingMetrics).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setEnv', () => {
|
||||||
|
it('should set the environment', () => {
|
||||||
|
const mockEnv = { METRICS_API_KEY: 'test-key' };
|
||||||
|
metricsService.setEnv(mockEnv);
|
||||||
|
expect(metricsService.env).toEqual(mockEnv);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setCtx', () => {
|
||||||
|
it('should set the execution context', () => {
|
||||||
|
const mockCtx = { waitUntil: vi.fn() };
|
||||||
|
metricsService.setCtx(mockCtx);
|
||||||
|
expect(metricsService.ctx).toEqual(mockCtx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('handleMetricsRequest', () => {
|
||||||
|
it('should proxy GET requests to metrics.seemueller.io', async () => {
|
||||||
|
// Create mock request
|
||||||
|
const mockRequest = new Request('https://example.com/metrics/path?query=value', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: new Headers({ 'Content-Type': 'application/json' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create mock response
|
||||||
|
const mockResponse = new Response('{"data": "test"}', {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock fetch to return the mock response
|
||||||
|
global.fetch.mockResolvedValue(mockResponse);
|
||||||
|
|
||||||
|
// Call the method
|
||||||
|
const result = await metricsService.handleMetricsRequest(mockRequest);
|
||||||
|
|
||||||
|
// Verify fetch was called with correct arguments
|
||||||
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
|
'https://metrics.seemueller.io/metrics/path?query=value',
|
||||||
|
expect.objectContaining({
|
||||||
|
method: 'GET',
|
||||||
|
body: null,
|
||||||
|
redirect: 'follow',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify result is the expected response
|
||||||
|
expect(result).toBe(mockResponse);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy POST requests with body to metrics.seemueller.io', async () => {
|
||||||
|
// Create mock request with body
|
||||||
|
const mockBody = JSON.stringify({ test: 'data' });
|
||||||
|
const mockRequest = new Request('https://example.com/metrics/path', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: new Headers({ 'Content-Type': 'application/json' }),
|
||||||
|
body: mockBody,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create mock response
|
||||||
|
const mockResponse = new Response('{"success": true}', {
|
||||||
|
status: 201,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock fetch to return the mock response
|
||||||
|
global.fetch.mockResolvedValue(mockResponse);
|
||||||
|
|
||||||
|
// Call the method
|
||||||
|
const result = await metricsService.handleMetricsRequest(mockRequest);
|
||||||
|
|
||||||
|
// Verify fetch was called with correct arguments
|
||||||
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
|
'https://metrics.seemueller.io/metrics/path',
|
||||||
|
expect.objectContaining({
|
||||||
|
method: 'POST',
|
||||||
|
body: mockRequest.body,
|
||||||
|
redirect: 'follow',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify result is the expected response
|
||||||
|
expect(result).toBe(mockResponse);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a 500 response when fetch fails', async () => {
|
||||||
|
// Create mock request
|
||||||
|
const mockRequest = new Request('https://example.com/metrics/path');
|
||||||
|
|
||||||
|
// Mock fetch to throw an error
|
||||||
|
global.fetch.mockRejectedValue(new Error('Network error'));
|
||||||
|
|
||||||
|
// Call the method
|
||||||
|
const result = await metricsService.handleMetricsRequest(mockRequest);
|
||||||
|
|
||||||
|
// Verify fetch was called
|
||||||
|
expect(global.fetch).toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Verify result is an error Response
|
||||||
|
expect(result).toBeInstanceOf(Response);
|
||||||
|
expect(result.status).toBe(500);
|
||||||
|
|
||||||
|
// Verify response body
|
||||||
|
const text = await result.clone().text();
|
||||||
|
expect(text).toBe('Failed to fetch metrics');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Reference in New Issue
Block a user