Community 93 Premium feature complete
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* @ai-summary Integration tests for community stations API
|
||||
* @ai-context Tests full API workflow with database
|
||||
*/
|
||||
|
||||
import { FastifyInstance } from 'fastify';
|
||||
import { Pool } from 'pg';
|
||||
import { buildApp } from '../../../../app';
|
||||
import { pool as pgPool } from '../../../../core/config/database';
|
||||
|
||||
describe('Community Stations API Integration Tests', () => {
|
||||
let app: FastifyInstance;
|
||||
let pool: Pool;
|
||||
|
||||
const testUserId = 'auth0|test-user-123';
|
||||
const testAdminId = 'auth0|test-admin-123';
|
||||
|
||||
const mockStationData = {
|
||||
name: 'Test Gas Station',
|
||||
address: '123 Main St',
|
||||
city: 'Springfield',
|
||||
state: 'IL',
|
||||
zipCode: '62701',
|
||||
latitude: 39.7817,
|
||||
longitude: -89.6501,
|
||||
brand: 'Shell',
|
||||
has93Octane: true,
|
||||
has93OctaneEthanolFree: false,
|
||||
price93: 3.50,
|
||||
notes: 'Great service'
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
pool = pgPool;
|
||||
app = await buildApp();
|
||||
|
||||
// Clean up test data
|
||||
await pool.query('DELETE FROM community_stations WHERE submitted_by IN ($1, $2)', [testUserId, testAdminId]);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
// Clean up test data
|
||||
await pool.query('DELETE FROM community_stations WHERE submitted_by IN ($1, $2)', [testUserId, testAdminId]);
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('POST /api/stations/community - Submit station', () => {
|
||||
it('should submit a new community station', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/stations/community',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
},
|
||||
payload: mockStationData
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(201);
|
||||
const body = JSON.parse(response.body);
|
||||
expect(body.id).toBeDefined();
|
||||
expect(body.status).toBe('pending');
|
||||
expect(body.submittedBy).toBe(testUserId);
|
||||
expect(body.name).toBe(mockStationData.name);
|
||||
});
|
||||
|
||||
it('should validate required fields', async () => {
|
||||
const incompleteData = {
|
||||
name: 'Test Station',
|
||||
address: '123 Main St'
|
||||
// Missing latitude and longitude
|
||||
};
|
||||
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/stations/community',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
},
|
||||
payload: incompleteData
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(400);
|
||||
const body = JSON.parse(response.body);
|
||||
expect(body.error).toBe('Validation error');
|
||||
});
|
||||
|
||||
it('should validate latitude bounds', async () => {
|
||||
const invalidData = {
|
||||
...mockStationData,
|
||||
latitude: 91 // Invalid: must be between -90 and 90
|
||||
};
|
||||
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/stations/community',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
},
|
||||
payload: invalidData
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(400);
|
||||
const body = JSON.parse(response.body);
|
||||
expect(body.error).toBe('Validation error');
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/stations/community',
|
||||
payload: mockStationData
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/stations/community/mine - Get user submissions', () => {
|
||||
let submittedStationId: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Submit a test station
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/stations/community',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
},
|
||||
payload: mockStationData
|
||||
});
|
||||
|
||||
const body = JSON.parse(response.body);
|
||||
submittedStationId = body.id;
|
||||
});
|
||||
|
||||
it('should retrieve user submissions', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/stations/community/mine',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
const body = JSON.parse(response.body);
|
||||
expect(body.total).toBeGreaterThan(0);
|
||||
expect(Array.isArray(body.stations)).toBe(true);
|
||||
expect(body.stations.some((s: any) => s.id === submittedStationId)).toBe(true);
|
||||
});
|
||||
|
||||
it('should support pagination', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/stations/community/mine?limit=10&offset=0',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
const body = JSON.parse(response.body);
|
||||
expect(body.stations).toBeDefined();
|
||||
expect(body.total).toBeDefined();
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/stations/community/mine'
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /api/stations/community/:id - Withdraw submission', () => {
|
||||
let pendingStationId: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Submit a test station
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/stations/community',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
},
|
||||
payload: {
|
||||
...mockStationData,
|
||||
name: 'Pending Station to Withdraw'
|
||||
}
|
||||
});
|
||||
|
||||
const body = JSON.parse(response.body);
|
||||
pendingStationId = body.id;
|
||||
});
|
||||
|
||||
it('should allow user to withdraw own pending submission', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'DELETE',
|
||||
url: `/api/stations/community/${pendingStationId}`,
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(204);
|
||||
|
||||
// Verify it's deleted
|
||||
const checkResponse = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/stations/community/mine',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
}
|
||||
});
|
||||
|
||||
const body = JSON.parse(checkResponse.body);
|
||||
expect(body.stations.some((s: any) => s.id === pendingStationId)).toBe(false);
|
||||
});
|
||||
|
||||
it('should reject withdrawal of non-existent station', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'DELETE',
|
||||
url: '/api/stations/community/invalid-id',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(400);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'DELETE',
|
||||
url: `/api/stations/community/${pendingStationId}`
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/stations/community/approved - Get approved stations', () => {
|
||||
it('should retrieve approved stations', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/stations/community/approved',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
const body = JSON.parse(response.body);
|
||||
expect(body.total).toBeDefined();
|
||||
expect(Array.isArray(body.stations)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return only approved stations', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/stations/community/approved',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
}
|
||||
});
|
||||
|
||||
const body = JSON.parse(response.body);
|
||||
body.stations.forEach((station: any) => {
|
||||
expect(station.status).toBe('approved');
|
||||
});
|
||||
});
|
||||
|
||||
it('should support pagination', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/stations/community/approved?limit=10&offset=0',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /api/stations/community/nearby - Find nearby stations', () => {
|
||||
it('should find nearby approved stations', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/stations/community/nearby',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
},
|
||||
payload: {
|
||||
latitude: 39.7817,
|
||||
longitude: -89.6501,
|
||||
radiusKm: 50
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
const body = JSON.parse(response.body);
|
||||
expect(Array.isArray(body.stations)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate location coordinates', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/stations/community/nearby',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
},
|
||||
payload: {
|
||||
latitude: 91, // Invalid
|
||||
longitude: -89.6501,
|
||||
radiusKm: 50
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(400);
|
||||
});
|
||||
|
||||
it('should validate radius', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/stations/community/nearby',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
},
|
||||
payload: {
|
||||
latitude: 39.7817,
|
||||
longitude: -89.6501,
|
||||
radiusKm: 1000 // Too large
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Admin endpoints', () => {
|
||||
it('should require admin role for admin endpoints', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/admin/community-stations',
|
||||
headers: {
|
||||
authorization: `Bearer ${testUserId}`
|
||||
}
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(403);
|
||||
});
|
||||
|
||||
// Note: Full admin endpoint testing would require proper admin role setup
|
||||
// These tests verify the routes are protected
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* @ai-summary Unit tests for community stations service
|
||||
* @ai-context Tests business logic without database dependencies
|
||||
*/
|
||||
|
||||
import { CommunityStationsService } from '../../domain/community-stations.service';
|
||||
import { CommunityStationsRepository } from '../../data/community-stations.repository';
|
||||
import { SubmitCommunityStationBody, CommunityStation } from '../../domain/community-stations.types';
|
||||
import { redis } from '../../../../core/config/redis';
|
||||
|
||||
// Mock repository and redis
|
||||
jest.mock('../../data/community-stations.repository');
|
||||
jest.mock('../../../../core/config/redis');
|
||||
|
||||
describe('CommunityStationsService', () => {
|
||||
let service: CommunityStationsService;
|
||||
let mockRepository: jest.Mocked<CommunityStationsRepository>;
|
||||
let mockRedis: jest.Mocked<typeof redis>;
|
||||
|
||||
const testUserId = 'test-user-123';
|
||||
const testAdminId = 'admin-123';
|
||||
const testStationId = 'station-123';
|
||||
|
||||
const mockStationData: SubmitCommunityStationBody = {
|
||||
name: 'Test Gas Station',
|
||||
address: '123 Main St',
|
||||
city: 'Springfield',
|
||||
state: 'IL',
|
||||
zipCode: '62701',
|
||||
latitude: 39.7817,
|
||||
longitude: -89.6501,
|
||||
brand: 'Shell',
|
||||
has93Octane: true,
|
||||
has93OctaneEthanolFree: false,
|
||||
price93: 3.50,
|
||||
notes: 'Great service'
|
||||
};
|
||||
|
||||
const mockStation: CommunityStation = {
|
||||
id: testStationId,
|
||||
submittedBy: testUserId,
|
||||
...mockStationData,
|
||||
status: 'pending',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockRepository = CommunityStationsRepository as jest.Mocked<typeof CommunityStationsRepository>;
|
||||
mockRedis = redis as jest.Mocked<typeof redis>;
|
||||
|
||||
// Setup default mock implementations
|
||||
(mockRedis.get as jest.Mock).mockResolvedValue(null);
|
||||
(mockRedis.setex as jest.Mock).mockResolvedValue(true);
|
||||
(mockRedis.del as jest.Mock).mockResolvedValue(1);
|
||||
(mockRedis.keys as jest.Mock).mockResolvedValue([]);
|
||||
|
||||
service = new CommunityStationsService(mockRepository as any);
|
||||
});
|
||||
|
||||
describe('submitStation', () => {
|
||||
it('should successfully submit a new station', async () => {
|
||||
(mockRepository.prototype.submitStation as jest.Mock).mockResolvedValue(mockStation);
|
||||
|
||||
const result = await service.submitStation(testUserId, mockStationData);
|
||||
|
||||
expect(result).toEqual(mockStation);
|
||||
expect(mockRepository.prototype.submitStation).toHaveBeenCalledWith(testUserId, mockStationData);
|
||||
expect(mockRedis.keys).toHaveBeenCalledWith('mvp:community-stations:pending:*');
|
||||
});
|
||||
|
||||
it('should invalidate pending cache after submission', async () => {
|
||||
(mockRepository.prototype.submitStation as jest.Mock).mockResolvedValue(mockStation);
|
||||
(mockRedis.keys as jest.Mock).mockResolvedValue(['pending-key-1']);
|
||||
|
||||
await service.submitStation(testUserId, mockStationData);
|
||||
|
||||
expect(mockRedis.del).toHaveBeenCalledWith('pending-key-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMySubmissions', () => {
|
||||
it('should retrieve user submissions', async () => {
|
||||
const mockResult = { total: 1, stations: [mockStation] };
|
||||
(mockRepository.prototype.getUserSubmissions as jest.Mock).mockResolvedValue(mockResult);
|
||||
|
||||
const result = await service.getMySubmissions(testUserId, 100, 0);
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockRepository.prototype.getUserSubmissions).toHaveBeenCalledWith(testUserId, 100, 0);
|
||||
});
|
||||
|
||||
it('should use default pagination values', async () => {
|
||||
const mockResult = { total: 1, stations: [mockStation] };
|
||||
(mockRepository.prototype.getUserSubmissions as jest.Mock).mockResolvedValue(mockResult);
|
||||
|
||||
await service.getMySubmissions(testUserId);
|
||||
|
||||
expect(mockRepository.prototype.getUserSubmissions).toHaveBeenCalledWith(testUserId, 100, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('withdrawSubmission', () => {
|
||||
it('should allow user to withdraw own pending submission', async () => {
|
||||
(mockRepository.prototype.getStationById as jest.Mock).mockResolvedValue(mockStation);
|
||||
(mockRepository.prototype.deleteStation as jest.Mock).mockResolvedValue(true);
|
||||
|
||||
await service.withdrawSubmission(testUserId, testStationId);
|
||||
|
||||
expect(mockRepository.prototype.deleteStation).toHaveBeenCalledWith(testStationId);
|
||||
expect(mockRedis.keys).toHaveBeenCalledWith('mvp:community-stations:pending:*');
|
||||
});
|
||||
|
||||
it('should prevent withdrawal of non-existent station', async () => {
|
||||
(mockRepository.prototype.getStationById as jest.Mock).mockResolvedValue(null);
|
||||
|
||||
await expect(service.withdrawSubmission(testUserId, testStationId))
|
||||
.rejects.toThrow('Station not found');
|
||||
});
|
||||
|
||||
it('should prevent withdrawal by non-owner', async () => {
|
||||
(mockRepository.prototype.getStationById as jest.Mock).mockResolvedValue(mockStation);
|
||||
|
||||
await expect(service.withdrawSubmission('other-user', testStationId))
|
||||
.rejects.toThrow('Unauthorized: You can only withdraw your own submissions');
|
||||
});
|
||||
|
||||
it('should prevent withdrawal of non-pending submission', async () => {
|
||||
const approvedStation = { ...mockStation, status: 'approved' as const };
|
||||
(mockRepository.prototype.getStationById as jest.Mock).mockResolvedValue(approvedStation);
|
||||
|
||||
await expect(service.withdrawSubmission(testUserId, testStationId))
|
||||
.rejects.toThrow('Can only withdraw pending submissions');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getApprovedStations', () => {
|
||||
it('should retrieve approved stations from cache if available', async () => {
|
||||
const cachedResult = { total: 1, stations: [{ ...mockStation, status: 'approved' as const }] };
|
||||
(mockRedis.get as jest.Mock).mockResolvedValue(JSON.stringify(cachedResult));
|
||||
|
||||
const result = await service.getApprovedStations(100, 0);
|
||||
|
||||
expect(result).toEqual(cachedResult);
|
||||
expect(mockRepository.prototype.getApprovedStations).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fetch from repository and cache if not in cache', async () => {
|
||||
const mockResult = { total: 1, stations: [{ ...mockStation, status: 'approved' as const }] };
|
||||
(mockRedis.get as jest.Mock).mockResolvedValue(null);
|
||||
(mockRepository.prototype.getApprovedStations as jest.Mock).mockResolvedValue(mockResult);
|
||||
|
||||
const result = await service.getApprovedStations(100, 0);
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockRepository.prototype.getApprovedStations).toHaveBeenCalledWith(100, 0);
|
||||
expect(mockRedis.setex).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getApprovedNearby', () => {
|
||||
it('should find nearby approved stations', async () => {
|
||||
const nearbyStations = [{ ...mockStation, status: 'approved' as const }];
|
||||
(mockRedis.get as jest.Mock).mockResolvedValue(null);
|
||||
(mockRepository.prototype.getNearbyApprovedStations as jest.Mock).mockResolvedValue(nearbyStations);
|
||||
|
||||
const result = await service.getApprovedNearby({
|
||||
latitude: 39.7817,
|
||||
longitude: -89.6501,
|
||||
radiusKm: 50
|
||||
});
|
||||
|
||||
expect(result).toEqual(nearbyStations);
|
||||
expect(mockRepository.prototype.getNearbyApprovedStations).toHaveBeenCalledWith(39.7817, -89.6501, 50);
|
||||
});
|
||||
|
||||
it('should use default radius if not provided', async () => {
|
||||
(mockRedis.get as jest.Mock).mockResolvedValue(null);
|
||||
(mockRepository.prototype.getNearbyApprovedStations as jest.Mock).mockResolvedValue([]);
|
||||
|
||||
await service.getApprovedNearby({
|
||||
latitude: 39.7817,
|
||||
longitude: -89.6501
|
||||
});
|
||||
|
||||
expect(mockRepository.prototype.getNearbyApprovedStations).toHaveBeenCalledWith(39.7817, -89.6501, 50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPendingReview', () => {
|
||||
it('should retrieve pending submissions for review', async () => {
|
||||
const mockResult = { total: 1, stations: [mockStation] };
|
||||
(mockRepository.prototype.getPendingStations as jest.Mock).mockResolvedValue(mockResult);
|
||||
|
||||
const result = await service.getPendingReview(100, 0);
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockRepository.prototype.getPendingStations).toHaveBeenCalledWith(100, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reviewStation', () => {
|
||||
it('should approve a pending station', async () => {
|
||||
const approvedStation = { ...mockStation, status: 'approved' as const, reviewedBy: testAdminId, reviewedAt: new Date() };
|
||||
(mockRepository.prototype.reviewStation as jest.Mock).mockResolvedValue(approvedStation);
|
||||
(mockRedis.keys as jest.Mock).mockResolvedValue([]);
|
||||
|
||||
const result = await service.reviewStation(testAdminId, testStationId, 'approved');
|
||||
|
||||
expect(result).toEqual(approvedStation);
|
||||
expect(mockRepository.prototype.reviewStation).toHaveBeenCalledWith(
|
||||
testStationId,
|
||||
testAdminId,
|
||||
'approved',
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject a station with reason', async () => {
|
||||
const rejectionReason = 'Invalid location';
|
||||
const rejectedStation = { ...mockStation, status: 'rejected' as const, rejectionReason };
|
||||
(mockRepository.prototype.reviewStation as jest.Mock).mockResolvedValue(rejectedStation);
|
||||
(mockRedis.keys as jest.Mock).mockResolvedValue([]);
|
||||
|
||||
const result = await service.reviewStation(testAdminId, testStationId, 'rejected', rejectionReason);
|
||||
|
||||
expect(result).toEqual(rejectedStation);
|
||||
expect(mockRepository.prototype.reviewStation).toHaveBeenCalledWith(
|
||||
testStationId,
|
||||
testAdminId,
|
||||
'rejected',
|
||||
rejectionReason
|
||||
);
|
||||
});
|
||||
|
||||
it('should require rejection reason when rejecting', async () => {
|
||||
await expect(service.reviewStation(testAdminId, testStationId, 'rejected'))
|
||||
.rejects.toThrow('Rejection reason required when rejecting a station');
|
||||
});
|
||||
|
||||
it('should invalidate appropriate caches on approval', async () => {
|
||||
const approvedStation = { ...mockStation, status: 'approved' as const };
|
||||
(mockRepository.prototype.reviewStation as jest.Mock).mockResolvedValue(approvedStation);
|
||||
(mockRedis.keys as jest.Mock).mockResolvedValue(['key1', 'key2']);
|
||||
|
||||
await service.reviewStation(testAdminId, testStationId, 'approved');
|
||||
|
||||
expect(mockRedis.del).toHaveBeenCalledWith('key1', 'key2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('cache invalidation', () => {
|
||||
it('should handle cache invalidation errors gracefully', async () => {
|
||||
(mockRepository.prototype.submitStation as jest.Mock).mockResolvedValue(mockStation);
|
||||
(mockRedis.keys as jest.Mock).mockRejectedValue(new Error('Redis error'));
|
||||
|
||||
// Should not throw
|
||||
const result = await service.submitStation(testUserId, mockStationData);
|
||||
expect(result).toEqual(mockStation);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user