import { test, expect } from '@playwright/test'; const BASE = 'http://localhost:8090'; const API_KEY = 'dev-api-key-minimum-32-characters-long'; test.describe('Klijentski API', () => { let licenseKey: string; test.beforeAll(async ({ request }) => { // Kreiraj licencu za testove const res = await request.post(`${BASE}/api/v1/admin/licenses`, { headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }, data: { product_id: 3, // LIGHT_TICKET license_type: 'MONTHLY', customer_name: 'API Test Firma', customer_email: 'api@test.rs', limits: { max_operators: 3 }, features: ['TICKET_VALIDATION', 'REPORTS'], grace_days: 30, }, }); expect(res.status()).toBe(201); const body = await res.json(); licenseKey = body.license_key; expect(licenseKey).toMatch(/^LT-/); }); test('aktivacija licence', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/activate`, { data: { license_key: licenseKey, machine_fingerprint: 'sha256:e2e-test-fingerprint-001', app_version: '1.0.0', os: 'linux', hostname: 'E2E-TEST-PC', }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body.license).toBeDefined(); expect(body.signature).toBeDefined(); expect(body.signature).toMatch(/^RSA-SHA256:/); expect(body.license.product).toBe('LIGHT_TICKET'); expect(body.license.license_type).toBe('MONTHLY'); expect(body.license.customer.name).toBe('API Test Firma'); }); test('ponovna aktivacija sa istim fingerprint-om (refresh)', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/activate`, { data: { license_key: licenseKey, machine_fingerprint: 'sha256:e2e-test-fingerprint-001', app_version: '1.0.0', os: 'linux', hostname: 'E2E-TEST-PC', }, }); expect(res.status()).toBe(200); }); test('aktivacija sa drugog racunara — odbijeno', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/activate`, { data: { license_key: licenseKey, machine_fingerprint: 'sha256:drugi-racunar-002', app_version: '1.0.0', os: 'windows', hostname: 'DRUGI-PC', }, }); expect(res.status()).toBe(400); const body = await res.json(); expect(body.error.code).toBe('ALREADY_ACTIVATED'); }); test('validacija aktivne licence', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/validate`, { data: { license_key: licenseKey, machine_fingerprint: 'sha256:e2e-test-fingerprint-001', }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body.valid).toBe(true); expect(body.revoked).toBe(false); }); test('deaktivacija licence', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/deactivate`, { data: { license_key: licenseKey, machine_fingerprint: 'sha256:e2e-test-fingerprint-001', }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body.can_reactivate).toBe(true); }); test('re-aktivacija sa novog racunara posle deaktivacije', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/activate`, { data: { license_key: licenseKey, machine_fingerprint: 'sha256:novi-racunar-003', app_version: '1.0.1', os: 'windows', hostname: 'NOVI-PC', }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body.license.machine_fingerprint).toBe('sha256:novi-racunar-003'); }); test('aktivacija sa nepostojecim kljucem', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/activate`, { data: { license_key: 'LT-FAKE-KEY1-KEY2-KEY3', machine_fingerprint: 'sha256:test', app_version: '1.0.0', os: 'linux', hostname: 'TEST', }, }); expect(res.status()).toBe(400); const body = await res.json(); expect(body.error.code).toBe('INVALID_KEY'); }); test('validacija nepostojeceg kljuca', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/validate`, { data: { license_key: 'LT-NEMA-OVOG-KLUC-AAAA', machine_fingerprint: 'sha256:test', }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body.valid).toBe(false); }); }); test.describe('Admin API autentifikacija', () => { test('bez API kljuca — 401', async ({ request }) => { const res = await request.get(`${BASE}/api/v1/admin/licenses`); expect(res.status()).toBe(401); }); test('pogresan API kljuc — 401', async ({ request }) => { const res = await request.get(`${BASE}/api/v1/admin/licenses`, { headers: { 'X-API-Key': 'pogresan-kljuc' }, }); expect(res.status()).toBe(401); }); test('ispravan API kljuc — 200', async ({ request }) => { const res = await request.get(`${BASE}/api/v1/admin/licenses`, { headers: { 'X-API-Key': API_KEY }, }); expect(res.status()).toBe(200); }); test('products endpoint', async ({ request }) => { const res = await request.get(`${BASE}/api/v1/admin/products`, { headers: { 'X-API-Key': API_KEY }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(Array.isArray(body)).toBe(true); expect(body.length).toBeGreaterThanOrEqual(3); }); test('stats endpoint', async ({ request }) => { const res = await request.get(`${BASE}/api/v1/admin/stats`, { headers: { 'X-API-Key': API_KEY }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body.total_licenses).toBeDefined(); expect(body.by_product).toBeDefined(); }); test('audit endpoint', async ({ request }) => { const res = await request.get(`${BASE}/api/v1/admin/audit`, { headers: { 'X-API-Key': API_KEY }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(Array.isArray(body)).toBe(true); }); test('health endpoint', async ({ request }) => { const res = await request.get(`${BASE}/api/v1/health`); expect(res.status()).toBe(200); const body = await res.json(); expect(body.status).toBe('ok'); }); }); test.describe('Admin API — Revoke flow', () => { let licenseId: number; let licenseKey: string; test.beforeAll(async ({ request }) => { const res = await request.post(`${BASE}/api/v1/admin/licenses`, { headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }, data: { product_id: 3, license_type: 'MONTHLY', customer_name: 'Revoke Flow Test', grace_days: 30, }, }); const body = await res.json(); licenseId = body.id; licenseKey = body.license_key; }); test('revoke licence', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/admin/licenses/${licenseId}/revoke`, { headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }, data: { reason: 'E2E test revoke' }, }); expect(res.status()).toBe(200); }); test('aktivacija opozvane licence — odbijeno', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/activate`, { data: { license_key: licenseKey, machine_fingerprint: 'sha256:revoke-test', app_version: '1.0.0', os: 'linux', hostname: 'TEST', }, }); expect(res.status()).toBe(400); const body = await res.json(); expect(body.error.code).toBe('KEY_REVOKED'); }); test('validacija opozvane licence', async ({ request }) => { const res = await request.post(`${BASE}/api/v1/validate`, { data: { license_key: licenseKey, machine_fingerprint: 'sha256:revoke-test', }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body.valid).toBe(false); expect(body.revoked).toBe(true); }); });