- 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.
114 lines
3.3 KiB
Go
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())
|
|
}
|
|
}
|