151 lines
5.6 KiB
TypeScript
151 lines
5.6 KiB
TypeScript
// src/tests/integration/flyer.integration.test.ts
|
|
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
|
|
import supertest from 'supertest';
|
|
import { getPool } from '../../services/db/connection.db';
|
|
import type { Flyer, FlyerItem } from '../../types';
|
|
import { cleanupDb } from '../utils/cleanup';
|
|
import { TEST_EXAMPLE_DOMAIN } from '../utils/testHelpers';
|
|
import {
|
|
createStoreWithLocation,
|
|
cleanupStoreLocations,
|
|
type CreatedStoreLocation,
|
|
} from '../utils/storeHelpers';
|
|
|
|
/**
|
|
* @vitest-environment node
|
|
*/
|
|
|
|
describe('Public Flyer API Routes Integration Tests', () => {
|
|
let flyers: Flyer[] = [];
|
|
// Use a supertest instance for all requests in this file
|
|
let request: ReturnType<typeof supertest>;
|
|
let testStoreId: number;
|
|
let createdFlyerId: number;
|
|
const createdStoreLocations: CreatedStoreLocation[] = [];
|
|
|
|
// Fetch flyers once before all tests in this suite to use in subsequent tests.
|
|
beforeAll(async () => {
|
|
vi.stubEnv('FRONTEND_URL', 'https://example.com');
|
|
const app = (await import('../../../server')).default;
|
|
request = supertest(app);
|
|
|
|
// Ensure at least one flyer exists
|
|
const store = await createStoreWithLocation(getPool(), {
|
|
name: 'Integration Test Store',
|
|
address: '123 Test St',
|
|
city: 'Toronto',
|
|
province: 'ON',
|
|
postalCode: 'M5V 1A1',
|
|
});
|
|
createdStoreLocations.push(store);
|
|
testStoreId = store.storeId;
|
|
|
|
const flyerRes = await getPool().query(
|
|
`INSERT INTO public.flyers (store_id, file_name, image_url, icon_url, item_count, checksum)
|
|
VALUES ($1, 'integration-test.jpg', '${TEST_EXAMPLE_DOMAIN}/flyer-images/integration-test.jpg', '${TEST_EXAMPLE_DOMAIN}/flyer-images/icons/integration-test.jpg', 1, $2) RETURNING flyer_id`,
|
|
[testStoreId, `${Date.now().toString(16)}`.padEnd(64, '0')],
|
|
);
|
|
createdFlyerId = flyerRes.rows[0].flyer_id;
|
|
|
|
// Add an item to it
|
|
await getPool().query(
|
|
`INSERT INTO public.flyer_items (flyer_id, item, price_display, price_in_cents, quantity)
|
|
VALUES ($1, 'Integration Test Item', '$1.99', 199, 'each')`,
|
|
[createdFlyerId],
|
|
);
|
|
|
|
const response = await request.get('/api/flyers');
|
|
flyers = response.body.data;
|
|
});
|
|
|
|
afterAll(async () => {
|
|
vi.unstubAllEnvs();
|
|
// Clean up the test data created in beforeAll to prevent polluting the test database.
|
|
await cleanupDb({
|
|
flyerIds: [createdFlyerId],
|
|
storeIds: [testStoreId],
|
|
});
|
|
await cleanupStoreLocations(getPool(), createdStoreLocations);
|
|
});
|
|
|
|
describe('GET /api/flyers', () => {
|
|
it('should return a list of flyers', async () => {
|
|
// Act: Call the API endpoint using the client function.
|
|
const response = await request.get('/api/flyers');
|
|
const flyers: Flyer[] = response.body.data;
|
|
expect(response.status).toBe(200);
|
|
expect(flyers).toBeInstanceOf(Array);
|
|
|
|
// We created a flyer in beforeAll, so we expect the array not to be empty.
|
|
expect(flyers.length).toBeGreaterThan(0);
|
|
|
|
// Check the shape of the first flyer object to ensure it matches the expected type.
|
|
const firstFlyer = flyers[0];
|
|
expect(firstFlyer).toHaveProperty('flyer_id');
|
|
expect(firstFlyer).toHaveProperty('file_name');
|
|
expect(firstFlyer).toHaveProperty('image_url');
|
|
expect(firstFlyer).toHaveProperty('store');
|
|
expect(firstFlyer.store).toHaveProperty('store_id');
|
|
expect(firstFlyer.store).toHaveProperty('name');
|
|
});
|
|
});
|
|
|
|
describe('GET /api/flyers/:id/items', () => {
|
|
it('should return items for a specific flyer', async () => {
|
|
// Arrange: Ensure we have at least one flyer to test with.
|
|
expect(flyers.length).toBeGreaterThan(0);
|
|
const testFlyer = flyers[0];
|
|
|
|
// Act: Fetch items for the first flyer.
|
|
const response = await request.get(`/api/flyers/${testFlyer.flyer_id}/items`);
|
|
const items: FlyerItem[] = response.body.data;
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(items).toBeInstanceOf(Array);
|
|
|
|
// If there are items, check the shape of the first one.
|
|
if (items.length > 0) {
|
|
const firstItem = items[0];
|
|
expect(firstItem).toHaveProperty('flyer_item_id');
|
|
expect(firstItem).toHaveProperty('item');
|
|
expect(firstItem).toHaveProperty('price_display');
|
|
expect(firstItem.flyer_id).toBe(testFlyer.flyer_id);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('POST /api/flyers/items/batch-fetch', () => {
|
|
it('should return items for multiple flyer IDs', async () => {
|
|
// Arrange: Get IDs from the flyers fetched in beforeAll.
|
|
const flyerIds = flyers.map((f) => f.flyer_id);
|
|
expect(flyerIds.length).toBeGreaterThan(0);
|
|
|
|
// Act: Fetch items for all available flyers.
|
|
const response = await request.post('/api/flyers/items/batch-fetch').send({ flyerIds });
|
|
const items: FlyerItem[] = response.body.data;
|
|
expect(response.status).toBe(200);
|
|
expect(items).toBeInstanceOf(Array);
|
|
// The total number of items should be greater than or equal to the number of flyers (assuming at least one item per flyer).
|
|
if (items.length > 0) {
|
|
expect(items[0]).toHaveProperty('flyer_item_id');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('POST /api/flyers/items/batch-count', () => {
|
|
it('should return the total count of items for multiple flyer IDs', async () => {
|
|
// Arrange
|
|
const flyerIds = flyers.map((f) => f.flyer_id);
|
|
expect(flyerIds.length).toBeGreaterThan(0);
|
|
|
|
// Act
|
|
const response = await request.post('/api/flyers/items/batch-count').send({ flyerIds });
|
|
const result = response.body.data;
|
|
|
|
// Assert
|
|
expect(result.count).toBeTypeOf('number');
|
|
expect(result.count).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
});
|