Files
goodgo-platform/e2e/api/search.spec.ts
Ho Ngoc Hai 60a0b3c8e1 test(e2e): add comprehensive E2E tests for listings, search, payments, subscriptions, admin
Expand Playwright E2E test coverage from 17 to 86 tests covering:
- Listings CRUD (create, search, filter, detail, status update)
- Search (text search, geo search, validation, Typesense fallback)
- Payments (create, list transactions, auth guards)
- Subscriptions (plans, create, quota, billing, usage metering)
- Admin authorization guards (all endpoints reject non-admin users)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 02:23:52 +07:00

169 lines
4.7 KiB
TypeScript

import { test, expect } from '@playwright/test';
test.describe('Search API', () => {
test.describe('GET /search — Text search', () => {
test('returns search results for a query', async ({ request }) => {
const res = await request.get('/search', {
params: { q: 'apartment' },
});
// Typesense may not be running in test env — accept 200 or 503
if (res.status() === 503) {
test.skip(true, 'Typesense not available');
return;
}
expect(res.status()).toBe(200);
const body = await res.json();
expect(body).toHaveProperty('data');
expect(Array.isArray(body.data)).toBeTruthy();
expect(body).toHaveProperty('total');
});
test('returns empty results for nonsense query', async ({ request }) => {
const res = await request.get('/search', {
params: { q: 'zzzznotexistingproperty999' },
});
if (res.status() === 503) {
test.skip(true, 'Typesense not available');
return;
}
expect(res.status()).toBe(200);
const body = await res.json();
expect(body.data).toHaveLength(0);
});
test('filters by property type', async ({ request }) => {
const res = await request.get('/search', {
params: { propertyType: 'VILLA', q: '' },
});
if (res.status() === 503) {
test.skip(true, 'Typesense not available');
return;
}
expect(res.status()).toBe(200);
const body = await res.json();
for (const item of body.data) {
expect(item.propertyType).toBe('VILLA');
}
});
test('filters by price range', async ({ request }) => {
const res = await request.get('/search', {
params: { priceMin: 1000000000, priceMax: 10000000000 },
});
if (res.status() === 503) {
test.skip(true, 'Typesense not available');
return;
}
expect(res.status()).toBe(200);
});
test('supports sorting', async ({ request }) => {
const res = await request.get('/search', {
params: { sortBy: 'price_asc' },
});
if (res.status() === 503) {
test.skip(true, 'Typesense not available');
return;
}
expect(res.status()).toBe(200);
});
test('paginates correctly', async ({ request }) => {
const res = await request.get('/search', {
params: { page: 1, perPage: 5 },
});
if (res.status() === 503) {
test.skip(true, 'Typesense not available');
return;
}
expect(res.status()).toBe(200);
const body = await res.json();
expect(body.data.length).toBeLessThanOrEqual(5);
});
});
test.describe('GET /search/geo — Geo search', () => {
test('returns results for geo search in Ho Chi Minh City', async ({ request }) => {
const res = await request.get('/search/geo', {
params: { lat: 10.7769, lng: 106.7009, radiusKm: 5 },
});
if (res.status() === 503) {
test.skip(true, 'Typesense not available');
return;
}
expect(res.status()).toBe(200);
const body = await res.json();
expect(body).toHaveProperty('data');
expect(Array.isArray(body.data)).toBeTruthy();
});
test('rejects missing required geo params', async ({ request }) => {
const res = await request.get('/search/geo', {
params: { lat: 10.7769 },
});
expect(res.ok()).toBeFalsy();
expect(res.status()).toBe(400);
});
test('rejects invalid latitude', async ({ request }) => {
const res = await request.get('/search/geo', {
params: { lat: 999, lng: 106.7009, radiusKm: 5 },
});
expect(res.ok()).toBeFalsy();
expect(res.status()).toBe(400);
});
test('rejects radius exceeding max', async ({ request }) => {
const res = await request.get('/search/geo', {
params: { lat: 10.7769, lng: 106.7009, radiusKm: 200 },
});
expect(res.ok()).toBeFalsy();
expect(res.status()).toBe(400);
});
test('filters geo results by property type', async ({ request }) => {
const res = await request.get('/search/geo', {
params: {
lat: 10.7769,
lng: 106.7009,
radiusKm: 10,
propertyType: 'APARTMENT',
},
});
if (res.status() === 503) {
test.skip(true, 'Typesense not available');
return;
}
expect(res.status()).toBe(200);
});
});
test.describe('POST /search/reindex — Admin reindex', () => {
test('rejects unauthenticated reindex request', async ({ request }) => {
const res = await request.post('/search/reindex');
expect(res.ok()).toBeFalsy();
expect(res.status()).toBe(401);
});
});
});