97 lines
3.3 KiB
TypeScript
97 lines
3.3 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
const API_BASE = 'http://localhost:9100'
|
|
|
|
test.describe('API health and public endpoints', () => {
|
|
test('health endpoint returns 200', async ({ request }) => {
|
|
const res = await request.get(`${API_BASE}/api/health`)
|
|
expect(res.status()).toBe(200)
|
|
})
|
|
|
|
test('fights list returns valid JSON', async ({ request }) => {
|
|
const res = await request.get(`${API_BASE}/api/fights`)
|
|
expect(res.status()).toBe(200)
|
|
const data = await res.json()
|
|
expect(Array.isArray(data.fights)).toBe(true)
|
|
})
|
|
|
|
test('leaderboard returns valid JSON', async ({ request }) => {
|
|
const res = await request.get(`${API_BASE}/api/bots/leaderboard`)
|
|
expect(res.status()).toBe(200)
|
|
const data = await res.json()
|
|
expect(data).toHaveProperty('leaderboard')
|
|
})
|
|
|
|
test('public stats returns valid JSON', async ({ request }) => {
|
|
const res = await request.get(`${API_BASE}/api/stats/public`)
|
|
expect(res.status()).toBe(200)
|
|
const data = await res.json()
|
|
expect(data).toBeDefined()
|
|
})
|
|
|
|
test('tournaments list returns valid JSON', async ({ request }) => {
|
|
const res = await request.get(`${API_BASE}/api/tournaments`)
|
|
expect(res.status()).toBe(200)
|
|
const data = await res.json()
|
|
expect(data).toHaveProperty('tournaments')
|
|
})
|
|
|
|
test('check-name endpoint works', async ({ request }) => {
|
|
const res = await request.get(`${API_BASE}/api/auth/check-name/TestBotName123`)
|
|
expect(res.status()).toBe(200)
|
|
const data = await res.json()
|
|
expect(typeof data.available).toBe('boolean')
|
|
})
|
|
})
|
|
|
|
test.describe('API auth protection', () => {
|
|
test('admin stats requires auth', async ({ request }) => {
|
|
const res = await request.get(`${API_BASE}/api/admin/stats`)
|
|
expect(res.status()).toBe(403)
|
|
})
|
|
|
|
test('payment confirm without auth returns 400/404', async ({ request }) => {
|
|
const res = await request.post(`${API_BASE}/api/payments/confirm/nonexistent`, {
|
|
data: {},
|
|
})
|
|
// Should be 400 or 404, not 500
|
|
expect([400, 404]).toContain(res.status())
|
|
})
|
|
|
|
test('fight respond without valid fight returns 404', async ({ request }) => {
|
|
const res = await request.post(`${API_BASE}/api/fights/nonexistent/respond`, {
|
|
data: { botId: 'fake', answer: 'test' },
|
|
})
|
|
expect([400, 404]).toContain(res.status())
|
|
})
|
|
|
|
test('queue join with nonexistent bot returns 404', async ({ request }) => {
|
|
const res = await request.post(`${API_BASE}/api/queue/join/nonexistent-bot-id`)
|
|
expect([400, 404]).toContain(res.status())
|
|
})
|
|
})
|
|
|
|
test.describe('API rate limiting', () => {
|
|
test('payment create-invoice is rate limited', async ({ request }) => {
|
|
const responses: number[] = []
|
|
// Send 15 requests quickly (limit is 10/min)
|
|
for (let i = 0; i < 15; i++) {
|
|
const res = await request.post(`${API_BASE}/api/payments/create-invoice`, {
|
|
data: { botId: `test-${i}` },
|
|
})
|
|
responses.push(res.status())
|
|
}
|
|
// At least some should be 429 (rate limited)
|
|
expect(responses.some(s => s === 429)).toBe(true)
|
|
})
|
|
})
|
|
|
|
test.describe('API security headers', () => {
|
|
test('responses include security headers', async ({ request }) => {
|
|
const res = await request.get(`${API_BASE}/api/health`)
|
|
const headers = res.headers()
|
|
expect(headers['x-content-type-options']).toBe('nosniff')
|
|
expect(headers['x-frame-options']).toBe('DENY')
|
|
})
|
|
})
|