Files
flyer-crawler.projectium.com/src/tests/integration/flyer.integration.test.ts

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);
});
});
});