- 7 E2E test fajlova (54 testa ukupno): - login.spec.ts: prijava, pogresna lozinka, redirect bez sesije - dashboard.spec.ts: statistike, navbar, odjava, root redirect - licenses.spec.ts: tabela, filteri, pretraga - license-crud.spec.ts: forma, kreiranje LT/ARV/ESIR licence - license-detail.spec.ts: informacije, aktivacije, audit, revoke, force release - audit.spec.ts: audit log stranica, kolone, generisanje zapisa - api-client.spec.ts: activate, deactivate, validate, revoke flow, API key auth - CLAUDE.md: dodato pravilo o rigoroznom testiranju Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
257 lines
8.0 KiB
TypeScript
257 lines
8.0 KiB
TypeScript
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);
|
|
});
|
|
});
|