Community 93 Premium feature complete

This commit is contained in:
Eric Gullickson
2025-12-21 11:31:10 -06:00
parent 1bde31247f
commit 95f5e89e48
60 changed files with 8061 additions and 350 deletions

View File

@@ -0,0 +1,222 @@
/**
* @ai-summary Tests for Community Stations API Client
*/
import axios from 'axios';
import { communityStationsApi } from '../../api/community-stations.api';
import { SubmitStationData, ReviewDecision } from '../../types/community-stations.types';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
describe('Community Stations API Client', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('submitStation', () => {
it('should submit a station successfully', async () => {
const submitData: SubmitStationData = {
name: 'Shell Downtown',
address: '123 Main St',
city: 'Denver',
state: 'CO',
zipCode: '80202',
latitude: 39.7392,
longitude: -104.9903,
brand: 'Shell',
has93Octane: true,
has93OctaneEthanolFree: false,
price93: 3.599,
notes: 'Good quality',
};
const mockResponse = {
data: {
id: '1',
...submitData,
status: 'pending',
submittedBy: 'user@example.com',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
};
mockedAxios.post.mockResolvedValueOnce(mockResponse);
const result = await communityStationsApi.submitStation(submitData);
expect(result).toEqual(mockResponse.data);
expect(mockedAxios.post).toHaveBeenCalledWith(
'/stations/community/submit',
submitData
);
});
it('should handle submission errors', async () => {
const submitData: SubmitStationData = {
name: 'Shell',
address: '123 Main St',
latitude: 39.7392,
longitude: -104.9903,
has93Octane: true,
has93OctaneEthanolFree: false,
};
const mockError = new Error('Network error');
mockedAxios.post.mockRejectedValueOnce(mockError);
await expect(communityStationsApi.submitStation(submitData)).rejects.toThrow('Network error');
});
});
describe('getMySubmissions', () => {
it('should fetch user submissions', async () => {
const mockSubmissions = [
{
id: '1',
name: 'Shell Downtown',
address: '123 Main St',
latitude: 39.7392,
longitude: -104.9903,
has93Octane: true,
has93OctaneEthanolFree: false,
status: 'pending',
submittedBy: 'user@example.com',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
];
mockedAxios.get.mockResolvedValueOnce({ data: mockSubmissions });
const result = await communityStationsApi.getMySubmissions();
expect(result).toEqual(mockSubmissions);
expect(mockedAxios.get).toHaveBeenCalledWith('/stations/community/mine');
});
});
describe('withdrawSubmission', () => {
it('should withdraw a submission', async () => {
mockedAxios.delete.mockResolvedValueOnce({ data: null });
await communityStationsApi.withdrawSubmission('1');
expect(mockedAxios.delete).toHaveBeenCalledWith('/stations/community/1');
});
});
describe('getApprovedStations', () => {
it('should fetch approved stations with pagination', async () => {
const mockResponse = {
data: {
stations: [
{
id: '1',
name: 'Shell Downtown',
address: '123 Main St',
latitude: 39.7392,
longitude: -104.9903,
has93Octane: true,
has93OctaneEthanolFree: false,
status: 'approved',
submittedBy: 'user@example.com',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
],
total: 1,
page: 0,
limit: 50,
},
};
mockedAxios.get.mockResolvedValueOnce(mockResponse);
const result = await communityStationsApi.getApprovedStations(0, 50);
expect(result).toEqual(mockResponse.data);
expect(mockedAxios.get).toHaveBeenCalledWith('/stations/community/approved', {
params: { page: 0, limit: 50 },
});
});
});
describe('reviewStation (admin)', () => {
it('should approve a station', async () => {
const decision: ReviewDecision = { status: 'approved' };
const mockResponse = {
data: {
id: '1',
status: 'approved',
reviewedAt: new Date().toISOString(),
reviewedBy: 'admin@example.com',
},
};
mockedAxios.patch.mockResolvedValueOnce(mockResponse);
const result = await communityStationsApi.reviewStation('1', decision);
expect(result).toEqual(mockResponse.data);
expect(mockedAxios.patch).toHaveBeenCalledWith(
'/stations/community/admin/1/review',
decision
);
});
it('should reject a station with reason', async () => {
const decision: ReviewDecision = {
status: 'rejected',
rejectionReason: 'Invalid location',
};
const mockResponse = {
data: {
id: '1',
status: 'rejected',
rejectionReason: 'Invalid location',
reviewedAt: new Date().toISOString(),
reviewedBy: 'admin@example.com',
},
};
mockedAxios.patch.mockResolvedValueOnce(mockResponse);
const result = await communityStationsApi.reviewStation('1', decision);
expect(result).toEqual(mockResponse.data);
expect(mockedAxios.patch).toHaveBeenCalledWith(
'/stations/community/admin/1/review',
decision
);
});
});
describe('bulkReviewStations (admin)', () => {
it('should bulk approve stations', async () => {
const ids = ['1', '2', '3'];
const decision: ReviewDecision = { status: 'approved' };
const mockResponse = {
data: [
{ id: '1', status: 'approved' },
{ id: '2', status: 'approved' },
{ id: '3', status: 'approved' },
],
};
mockedAxios.post.mockResolvedValueOnce(mockResponse);
const result = await communityStationsApi.bulkReviewStations(ids, decision);
expect(result).toEqual(mockResponse.data);
expect(mockedAxios.post).toHaveBeenCalledWith('/stations/community/admin/bulk-review', {
ids,
...decision,
});
});
});
});