168 lines
6.1 KiB
TypeScript
168 lines
6.1 KiB
TypeScript
// src/tests/integration/ai.integration.test.ts
|
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
import supertest from 'supertest';
|
|
import app from '../../../server';
|
|
import fs from 'node:fs/promises';
|
|
import path from 'path';
|
|
import { createAndLoginUser } from '../utils/testHelpers';
|
|
|
|
/**
|
|
* @vitest-environment node
|
|
*/
|
|
|
|
const request = supertest(app);
|
|
|
|
interface TestGeolocationCoordinates {
|
|
latitude: number;
|
|
longitude: number;
|
|
accuracy: number;
|
|
altitude: number | null;
|
|
altitudeAccuracy: number | null;
|
|
heading: number | null;
|
|
speed: number | null;
|
|
toJSON(): object;
|
|
}
|
|
|
|
describe('AI API Routes Integration Tests', () => {
|
|
let authToken: string;
|
|
|
|
beforeAll(async () => {
|
|
// Create and log in as a new user for authenticated tests.
|
|
({ token: authToken } = await createAndLoginUser({ fullName: 'AI Tester' }));
|
|
});
|
|
|
|
afterAll(async () => {
|
|
// Clean up any files created in the flyer-images directory during these tests.
|
|
const uploadDir = path.resolve(__dirname, '../../../flyer-images');
|
|
try {
|
|
const files = await fs.readdir(uploadDir);
|
|
// Target files created by the 'image' and 'images' multer instances.
|
|
const testFiles = files.filter((f) => f.startsWith('image-') || f.startsWith('images-'));
|
|
for (const file of testFiles) {
|
|
await fs.unlink(path.join(uploadDir, file));
|
|
}
|
|
} catch (error) {
|
|
console.error('Error during AI integration test file cleanup:', error);
|
|
}
|
|
});
|
|
|
|
it('POST /api/ai/check-flyer should return a boolean', async () => {
|
|
const response = await request
|
|
.post('/api/ai/check-flyer')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.attach('image', Buffer.from('content'), 'test.jpg');
|
|
const result = response.body;
|
|
expect(response.status).toBe(200);
|
|
// The backend is stubbed to always return true for this check
|
|
expect(result.is_flyer).toBe(true);
|
|
});
|
|
|
|
it('POST /api/ai/extract-address should return a stubbed address', async () => {
|
|
const response = await request
|
|
.post('/api/ai/extract-address')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.attach('image', Buffer.from('content'), 'test.jpg');
|
|
const result = response.body;
|
|
expect(response.status).toBe(200);
|
|
expect(result.address).toBe('not identified');
|
|
});
|
|
|
|
it('POST /api/ai/extract-logo should return a stubbed response', async () => {
|
|
const response = await request
|
|
.post('/api/ai/extract-logo')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.attach('images', Buffer.from('content'), 'test.jpg');
|
|
const result = response.body;
|
|
expect(response.status).toBe(200);
|
|
expect(result).toEqual({ store_logo_base_64: null });
|
|
});
|
|
|
|
it('POST /api/ai/quick-insights should return a stubbed insight', async () => {
|
|
const response = await request
|
|
.post('/api/ai/quick-insights')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({ items: [{ item: 'test' }] });
|
|
const result = response.body;
|
|
expect(response.status).toBe(200);
|
|
expect(result.text).toBe('This is a server-generated quick insight: buy the cheap stuff!');
|
|
});
|
|
|
|
it('POST /api/ai/deep-dive should return a stubbed analysis', async () => {
|
|
const response = await request
|
|
.post('/api/ai/deep-dive')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({ items: [{ item: 'test' }] });
|
|
const result = response.body;
|
|
expect(response.status).toBe(200);
|
|
expect(result.text).toBe('This is a server-generated deep dive analysis. It is very detailed.');
|
|
});
|
|
|
|
it('POST /api/ai/search-web should return a stubbed search result', async () => {
|
|
const response = await request
|
|
.post('/api/ai/search-web')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({ query: 'test query' });
|
|
const result = response.body;
|
|
expect(response.status).toBe(200);
|
|
expect(result).toEqual({ text: 'The web says this is good.', sources: [] });
|
|
});
|
|
|
|
it('POST /api/ai/plan-trip should return an error as the feature is disabled', async () => {
|
|
// The GeolocationCoordinates type requires more than just lat/lng.
|
|
// We create a complete mock object to satisfy the type.
|
|
const mockLocation: TestGeolocationCoordinates = {
|
|
latitude: 48.4284,
|
|
longitude: -123.3656,
|
|
accuracy: 100,
|
|
altitude: null,
|
|
altitudeAccuracy: null,
|
|
heading: null,
|
|
speed: null,
|
|
toJSON: function () {
|
|
return {
|
|
latitude: this.latitude,
|
|
longitude: this.longitude,
|
|
accuracy: this.accuracy,
|
|
altitude: this.altitude,
|
|
altitudeAccuracy: this.altitudeAccuracy,
|
|
heading: this.heading,
|
|
speed: this.speed,
|
|
};
|
|
},
|
|
};
|
|
const mockStore = {
|
|
name: 'Test Store for Trip',
|
|
store_id: 1,
|
|
created_at: new Date().toISOString(),
|
|
updated_at: new Date().toISOString(),
|
|
};
|
|
const response = await request
|
|
.post('/api/ai/plan-trip')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({ items: [], store: mockStore, userLocation: mockLocation });
|
|
// The service for this endpoint is disabled and throws an error, which results in a 500.
|
|
expect(response.status).toBe(500);
|
|
const errorResult = response.body;
|
|
expect(errorResult.message).toContain('planTripWithMaps');
|
|
});
|
|
|
|
it('POST /api/ai/generate-image should reject because it is not implemented', async () => {
|
|
// The backend for this is not stubbed and will throw an error.
|
|
// This test confirms that the endpoint is protected and responds as expected to a failure.
|
|
const response = await request
|
|
.post('/api/ai/generate-image')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({ prompt: 'a test prompt' });
|
|
expect(response.status).toBe(501);
|
|
});
|
|
|
|
it('POST /api/ai/generate-speech should reject because it is not implemented', async () => {
|
|
// The backend for this is not stubbed and will throw an error.
|
|
const response = await request
|
|
.post('/api/ai/generate-speech')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({ text: 'a test prompt' });
|
|
expect(response.status).toBe(501);
|
|
});
|
|
});
|