98 lines
3.8 KiB
Go
98 lines
3.8 KiB
Go
package osupdate
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-agent/internal/hub"
|
|
)
|
|
|
|
// ---- the security-profile refresh (R-1388, R-1301, `11` §5.12) ----
|
|
|
|
func profileCalls(w *fakeWrapper) []map[string]any {
|
|
var m []map[string]any
|
|
for _, p := range w.plans {
|
|
if p["mode"] == "profile-refresh" {
|
|
m = append(m, p)
|
|
}
|
|
}
|
|
return m
|
|
}
|
|
|
|
func refreshed() *WrapperReport {
|
|
return &WrapperReport{ProfileRefresh: json.RawMessage(`{"outcome":"refreshed","kernel":"7.0.14-28-pve","rewritten":["f6da0254.0/usr.sbin.chronyd"],` +
|
|
`"restarted":[{"unit":"chrony.service","program":"/usr/sbin/chronyd","rc":0,"active":true}],"seconds":0.5}`)}
|
|
}
|
|
|
|
// One call, a host-layer plan that names NOTHING, and nothing to the hub.
|
|
// COMPANION RED-PROOF (observed): add "units" to the plan → this test fails; drop the Appliance check → the BYO test fails.
|
|
func TestProfileRefresh_OneCallNoNamesNoHubReport(t *testing.T) {
|
|
w := &fakeWrapper{t: t, profileRep: refreshed()}
|
|
l, h := newLeg(t, w, &hub.WireOSUpdate{Ring: 1, Enabled: true})
|
|
if got := l.ProfileRefresh(context.Background(), "agent start", nil, 1, time.Second); got != "refreshed" {
|
|
t.Fatalf("outcome = %q", got)
|
|
}
|
|
pc := profileCalls(w)
|
|
if len(pc) != 1 {
|
|
t.Fatalf("calls = %d", len(pc))
|
|
}
|
|
if len(pc[0]) != 3 || pc[0]["layer"] != LayerHost || pc[0]["release_id"] != "profile-refresh" {
|
|
t.Fatalf("the plan carries more than release_id, layer and mode: %v", pc[0])
|
|
}
|
|
if len(h.reports) != 0 {
|
|
t.Fatalf("the hub got a report: %+v", h.reports)
|
|
}
|
|
}
|
|
|
|
func TestProfileRefresh_ByoHostIsNeverAsked(t *testing.T) {
|
|
w := &fakeWrapper{t: t, profileRep: refreshed()}
|
|
l, _ := newLeg(t, w, nil)
|
|
l.Appliance = false
|
|
if got := l.ProfileRefresh(context.Background(), "agent start", nil, 1, time.Second); got != "" || len(profileCalls(w)) != 0 {
|
|
t.Fatalf("a BYO host was asked: %q %v", got, profileCalls(w))
|
|
}
|
|
}
|
|
|
|
// The heavy-op gate: busy → wait and ask again; still busy after the tries → not run (the next start asks again).
|
|
// COMPANION RED-PROOF (observed): ignore `ok` from the gate → the "always busy" half fails.
|
|
func TestProfileRefresh_WaitsForTheHeavyOpGate(t *testing.T) {
|
|
w := &fakeWrapper{t: t, profileRep: refreshed()}
|
|
l, _ := newLeg(t, w, nil)
|
|
n, released := 0, 0
|
|
gate := func() (func(), string, bool) {
|
|
n++
|
|
if n < 3 {
|
|
return nil, "primary-backup", false
|
|
}
|
|
return func() { released++ }, "", true
|
|
}
|
|
if got := l.ProfileRefresh(context.Background(), "agent start", gate, 5, time.Minute); got != "refreshed" {
|
|
t.Fatalf("outcome = %q after %d gate tries", got, n)
|
|
}
|
|
if n != 3 || released != 1 || len(profileCalls(w)) != 1 {
|
|
t.Fatalf("gate tries %d, released %d, calls %d", n, released, len(profileCalls(w)))
|
|
}
|
|
w2 := &fakeWrapper{t: t, profileRep: refreshed()}
|
|
l2, _ := newLeg(t, w2, nil)
|
|
busy := func() (func(), string, bool) { return nil, "restore-test", false }
|
|
if got := l2.ProfileRefresh(context.Background(), "agent start", busy, 3, time.Minute); got != "" || len(profileCalls(w2)) != 0 {
|
|
t.Fatalf("ran beside a heavy operation: %q %d call(s)", got, len(profileCalls(w2)))
|
|
}
|
|
}
|
|
|
|
// An older wrapper refuses the mode: logged, nothing else. A failure is said as "failed".
|
|
func TestProfileRefresh_RefusedAndFailed(t *testing.T) {
|
|
w := &fakeWrapper{t: t, profileRep: &WrapperReport{Refused: json.RawMessage(`{"code":"R11","reason":"unknown mode 'profile-refresh'"}`)}}
|
|
l, _ := newLeg(t, w, nil)
|
|
if got := l.ProfileRefresh(context.Background(), "agent start", nil, 1, time.Second); got != "" {
|
|
t.Fatalf("refused → %q", got)
|
|
}
|
|
w2 := &fakeWrapper{t: t, profileRep: &WrapperReport{Failed: json.RawMessage(`{"rc":1,"step":"profile-refresh"}`)}}
|
|
l2, _ := newLeg(t, w2, nil)
|
|
if got := l2.ProfileRefresh(context.Background(), "agent start", nil, 1, time.Second); got != "failed" {
|
|
t.Fatalf("failed → %q", got)
|
|
}
|
|
}
|