licence-server/tests/e2e/audit.spec.ts
Nenad Djukic 1abe7176ec Preimenovanje: dal-license-server -> licence-server (+ debrendiranje)
Vlasnik izlazi iz DAL price za ovaj projekat - postaje samostalan proizvod.
Preimenovanje je mehanicko (import putanje, imena fajlova/baze/UI naslova),
NIJEDNA linija poslovne logike u internal/handler ili internal/service nije
dirana - go build, go vet i go test ./... prolaze cisto pod novim imenom.

- go.mod: module dal-license-server -> licence-server; 28 import linija u
  13 .go fajlova usaglaseno
- internal/config/config.go (+test): default DB_NAME licence_db
- HTML sabloni (7 stranica) + 7 Playwright e2e spec fajlova: "DAL License
  Server" -> "Licence Server" u title/nav-brand, testovi usaglaseni u istom
  prolazu (nista ne moze da se razdvoji)
- package.json/package-lock.json/.gitignore/.claude/project.json: ime
  projekta/binarnog fajla/work_dir
- Usput: package.json je u repository.url nosio OTVORENU Gitea lozinku u
  cistom tekstu - uklonjena (URL sad bez kredencijala). Lozinka je i dalje
  u staroj git istoriji - preporuka: rotirati je posebno.
- CLAUDE.md/README/API/TESTING/docs/SPEC/ARCHITECTURE/SETUP: naslovi, ASCII
  dijagrami, git clone URL, mysqldump primer, systemd predlozak u SETUP.md
  zamenjen stvarnim (obrazac terminia.service - journal log + graceful
  shutdown, ne stari minimalni predlozak)
- Debrendiranje: "Univerzalni licencni server za sve DAL proizvode" ->
  "za vise proizvoda i klijenata"; potpis "Nenad Djukic / DAL d.o.o." ->
  "Nenad Djukic". NE dirano: nazivi ESIR/ARV/LIGHT_TICKET (tudji proizvodi,
  ne DAL brend), "DAL" kao stvaran naziv org-a u RBAC modelu
  (docs/loggerservice/README.md) - to je podatak, ne branding ovog servera.
- docs/asp-terminia/, docs/loggerservice/: prozni pomeni imena servera

Baza (dal_license_db -> licence_db) i deploy na 151 idu u posebnom koraku,
posle preimenovanja Gitea repoa - vidi CLAUDE.md istorijsku belesku na dnu.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 07:55:26 +02:00

192 lines
6.5 KiB
TypeScript

import { test, expect, Page } from '@playwright/test';
const BASE = 'http://localhost:8090';
const API_KEY = 'dev-api-key-minimum-32-characters-long';
async function login(page: Page) {
await page.goto('/login');
await page.fill('input[name="username"]', 'admin');
await page.fill('input[name="password"]', 'admin123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL(/\/dashboard/);
}
test.describe('Audit Log stranica', () => {
test.beforeEach(async ({ page }) => {
await login(page);
});
test('prikazuje naslov Audit Log', async ({ page }) => {
await page.goto('/audit');
await expect(page.locator('h1')).toHaveText('Audit Log');
});
test('ima ispravan title', async ({ page }) => {
await page.goto('/audit');
await expect(page).toHaveTitle('Audit Log - Licence Server');
});
test('prikazuje tabelu', async ({ page }) => {
await page.goto('/audit');
await expect(page.locator('table')).toBeVisible();
});
test('tabela ima 5 kolona', async ({ page }) => {
await page.goto('/audit');
const headers = page.locator('thead th');
await expect(headers).toHaveCount(5);
await expect(headers.nth(0)).toHaveText('Vreme');
await expect(headers.nth(1)).toHaveText('Akcija');
await expect(headers.nth(2)).toHaveText('Licenca');
await expect(headers.nth(3)).toHaveText('IP');
await expect(headers.nth(4)).toHaveText('Detalji');
});
test('navbar audit link je aktivan', async ({ page }) => {
await page.goto('/audit');
const link = page.locator('a[href="/audit"]');
await expect(link).toHaveClass(/active/);
});
test('navigacija iz navbara radi', async ({ page }) => {
await page.click('a[href="/audit"]');
await expect(page).toHaveURL(/\/audit/);
});
test('kreiranje licence generise CREATE zapis u audit-u', async ({ page }) => {
// Kreiraj licencu
await page.goto('/licenses/new');
const productSelect = page.locator('select[name="product_id"]');
const options = productSelect.locator('option');
const count = await options.count();
for (let i = 0; i < count; i++) {
const text = await options.nth(i).textContent();
if (text && text.includes('LIGHT_TICKET')) {
await productSelect.selectOption({ index: i });
break;
}
}
await page.selectOption('select[name="license_type"]', 'MONTHLY');
await page.fill('input[name="customer_name"]', `Audit Create ${Date.now()}`);
await page.click('button:has-text("Kreiraj licencu")');
await expect(page).toHaveURL(/\/licenses\/\d+/);
// Proveri audit
await page.goto('/audit');
await expect(page.locator('.badge', { hasText: 'CREATE' }).first()).toBeVisible();
});
test('audit prikazuje licencni kljuc', async ({ page }) => {
await page.goto('/audit');
// Audit zapisi koji imaju licencu prikazuju code element
const codeElements = page.locator('tbody code');
const count = await codeElements.count();
if (count > 0) {
const firstKey = await codeElements.first().textContent();
expect(firstKey).toMatch(/^(LT|ARV|ESIR)-/);
}
});
test('audit prikazuje IP adresu', async ({ page }) => {
await page.goto('/audit');
// Mora biti bar jedan zapis sa IP adresom
const rows = page.locator('tbody tr');
const count = await rows.count();
expect(count).toBeGreaterThan(0);
});
test('audit prikazuje vreme u ispravnom formatu', async ({ page }) => {
await page.goto('/audit');
const firstTimeCell = page.locator('tbody td').first();
const text = await firstTimeCell.textContent();
// Format: DD.MM.YYYY HH:MM
if (text && text.trim() !== 'Nema podataka') {
expect(text).toMatch(/\d{2}\.\d{2}\.\d{4}/);
}
});
test('audit prikazuje detalje kao JSON', async ({ page }) => {
await page.goto('/audit');
const details = page.locator('.audit-details');
const count = await details.count();
if (count > 0) {
await expect(details.first()).toBeVisible();
}
});
});
test.describe('Audit Log — API zapisi', () => {
test('aktivacija generise ACTIVATE zapis', async ({ request, page }) => {
// Kreiraj licencu
const createRes = 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: 'Audit Activate Test',
},
});
const created = await createRes.json();
// Aktiviraj
await request.post(`${BASE}/api/v1/activate`, {
data: {
license_key: created.license_key,
machine_fingerprint: 'sha256:audit-activate-test',
app_version: '1.0.0',
os: 'linux',
hostname: 'AUDIT-PC',
},
});
// Proveri audit na UI
await login(page);
await page.goto('/audit');
await expect(page.locator('.badge', { hasText: 'ACTIVATE' }).first()).toBeVisible();
});
test('deaktivacija generise DEACTIVATE zapis', async ({ request, page }) => {
const createRes = 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: 'Audit Deactivate Test',
},
});
const created = await createRes.json();
const fp = 'sha256:audit-deactivate-test';
await request.post(`${BASE}/api/v1/activate`, {
data: { license_key: created.license_key, machine_fingerprint: fp, app_version: '1.0.0', os: 'linux', hostname: 'PC' },
});
await request.post(`${BASE}/api/v1/deactivate`, {
data: { license_key: created.license_key, machine_fingerprint: fp },
});
await login(page);
await page.goto('/audit');
await expect(page.locator('.badge', { hasText: 'DEACTIVATE' }).first()).toBeVisible();
});
test('validacija generise VALIDATE zapis', async ({ request, page }) => {
const createRes = 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: 'Audit Validate Test',
},
});
const created = await createRes.json();
await request.post(`${BASE}/api/v1/validate`, {
data: { license_key: created.license_key, machine_fingerprint: 'sha256:validate-test' },
});
await login(page);
await page.goto('/audit');
await expect(page.locator('.badge', { hasText: 'VALIDATE' }).first()).toBeVisible();
});
});