licence-server/logger/registry_test.go
Nenad Djukic 3480766c3e Prijemnik dijagnostike sa TERMINIA uredjaja (logger/)
- UDP prijemnik (podrazumevano 46465, NE 46464 gde uredjaji slusaju discovery):
  dnevnik rada sa zaglavljem #TERMINIA, heartbeat JSON, sve ostalo se odbacuje
- paket bez zaglavlja (FW < 0.13.4) se vodi kao "nepoznat uredjaj" po izvornoj
  IP adresi i tako je i oznacen u prikazu
- dnevnik u fajlove po uredjaju i danu, rotacija po velicini + tri kvote
  (starost, po uredjaju, ukupno); otvoren fajl se nikad ne brise
- heartbeat u memoriji; interval se meri iz razmaka javljanja, "tih" = 3x interval
- web stranica (podrazumevano 127.0.0.1:8081): spisak uredjaja, crveno za tihe,
  poslednjih N linija sa pretragom, /dnevnik.txt i /api/uredjaji
- bez baze i bez spoljnih zavisnosti; ne dira licencni servis, njegovu bazu,
  rute ni nginx
- sanitizacija svega iz paketa (duzine, kontrolni znaci, ime foldera) — bez
  izlaska iz foldera dnevnika
- systemd unit + primer env fajla + README sa uputstvom za pustanje na server
- testovi: parsiranje (zaglavlje/JSON/bez zaglavlja/smece), registar, rotacija
  i kvote, web rute

Nije pusteno na server — puštanje radi vlasnik po logger/README.md.
2026-07-25 17:40:27 +02:00

114 lines
3.3 KiB
Go

package logger
import (
"fmt"
"testing"
"time"
)
func hbPaket(mac string) Packet {
return ParsePacket([]byte(fmt.Sprintf(
`{"app":"TERMINIA","name":"TERM","mac":"%s","ip":"192.168.0.20","fw":"0.13.4","hb":1}`, mac)),
"192.168.0.20")
}
func TestRegistryMeriIntervalHeartbeata(t *testing.T) {
r := NewRegistry(10, 60)
t0 := time.Date(2026, 7, 25, 12, 0, 0, 0, time.UTC)
r.Update(hbPaket("1C:DB:D4:A6:A4:60"), t0)
key, _ := r.Update(hbPaket("1C:DB:D4:A6:A4:60"), t0.Add(30*time.Second))
d, ok := r.Get(key)
if !ok {
t.Fatal("uredjaj nije u registru")
}
if d.IntervalSec != 30 {
t.Errorf("interval = %d, ocekivano 30", d.IntervalSec)
}
if d.Heartbeats != 2 {
t.Errorf("heartbeata = %d", d.Heartbeats)
}
// 3x30 s = 90 s praga (poslednji paket je stigao u t0+30 s)
if r.Stale(&d, t0.Add(150*time.Second)) == false {
t.Error("posle 90 s tisine uredjaj mora biti oznacen kao tih")
}
if r.Stale(&d, t0.Add(40*time.Second)) {
t.Error("uredjaj koji se javio pre 10 s ne sme biti tih")
}
}
func TestRegistryBezHeartbeataKoristiPodrazumevaniInterval(t *testing.T) {
r := NewRegistry(10, 60)
t0 := time.Now()
key, _ := r.Update(ParsePacket([]byte("#TERMINIA T 1C:DB:D4:A6:A4:60 0.13.4\nred\n"), "1.2.3.4"), t0)
d, _ := r.Get(key)
if got := r.Interval(&d); got != 60 {
t.Errorf("interval = %d, ocekivano 60 (podrazumevani)", got)
}
if !r.Stale(&d, t0.Add(200*time.Second)) {
t.Error("posle 180 s mora biti tih")
}
}
func TestRegistryOgranicavaBrojUredjaja(t *testing.T) {
r := NewRegistry(3, 60)
t0 := time.Now()
for i := 0; i < 50; i++ {
p := ParsePacket([]byte("neka linija\n"), fmt.Sprintf("10.0.0.%d", i))
r.Update(p, t0.Add(time.Duration(i)*time.Second))
}
if n := r.Count(); n != 3 {
t.Errorf("uredjaja u registru = %d, ocekivano 3 (granica)", n)
}
// Najstariji mora ispasti, najnoviji ostati.
if _, ok := r.Get("nepoznat-10_0_0_49"); !ok {
t.Error("poslednji uredjaj mora ostati u registru")
}
if _, ok := r.Get("nepoznat-10_0_0_0"); ok {
t.Error("najstariji uredjaj je morao biti izbacen")
}
}
func TestRegistryZadrzavaIdentitetIzZaglavlja(t *testing.T) {
r := NewRegistry(10, 60)
t0 := time.Now()
key, _ := r.Update(ParsePacket([]byte("#TERMINIA TERM-001 1C:DB:D4:A6:A4:60 0.13.4\nprvi red\n"), "1.2.3.4"), t0)
// Kasniji paket bez zaglavlja sa istim MAC-om ne postoji, ali heartbeat
// dopunjuje IP i serijski.
r.Update(hbPaket("1C:DB:D4:A6:A4:60"), t0.Add(time.Second))
d, _ := r.Get(key)
if !d.Identified || d.MAC != "1C:DB:D4:A6:A4:60" || d.IP != "192.168.0.20" {
t.Errorf("uredjaj = %+v", d)
}
if d.LogLines != 1 || d.LastLine != "prvi red" {
t.Errorf("linije = %d / %q", d.LogLines, d.LastLine)
}
}
func TestRegistrySnapshotStavljaTiheNaVrh(t *testing.T) {
r := NewRegistry(10, 60)
t0 := time.Date(2026, 7, 25, 12, 0, 0, 0, time.UTC)
r.Update(hbPaket("AA:AA:AA:AA:AA:AA"), t0.Add(-time.Hour))
r.Update(hbPaket("BB:BB:BB:BB:BB:BB"), t0)
snap := r.Snapshot(t0)
if len(snap) != 2 {
t.Fatalf("snapshot = %d", len(snap))
}
if snap[0].MAC != "AA:AA:AA:AA:AA:AA" {
t.Errorf("tih uredjaj mora biti prvi, dobijeno %s", snap[0].MAC)
}
}
func TestRegistryIgnorisSmece(t *testing.T) {
r := NewRegistry(10, 60)
if _, ok := r.Update(ParsePacket([]byte("{"), "1.2.3.4"), time.Now()); ok {
t.Error("smece ne sme uci u registar")
}
if r.Count() != 0 {
t.Errorf("registar nije prazan: %d", r.Count())
}
}