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>
192 lines
6.5 KiB
TypeScript
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();
|
|
});
|
|
});
|