Discovery re-matches unpinned devices every run and at startup; an admin can pin a
profile (PUT /devices/{slug} profile, GET /profiles, DeviceView profile_pinned and
profile_match). Rows on the device list, interface search and poller health navigate on
click and Enter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx9FNLG2CqeBbVGGd9bFsP
80 lines
2.7 KiB
Go
80 lines
2.7 KiB
Go
package api_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/v10networks/nmon/internal/api"
|
|
"github.com/v10networks/nmon/internal/profile"
|
|
"github.com/v10networks/nmon/internal/store"
|
|
)
|
|
|
|
func profileEnv(t *testing.T) *env {
|
|
t.Helper()
|
|
set, err := profile.Load("")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return newEnvDeps(t, 0, func(d *api.Deps) { d.Profiles = set })
|
|
}
|
|
|
|
func TestListProfiles(t *testing.T) {
|
|
e := profileEnv(t)
|
|
var got []api.ProfileView
|
|
e.getJSON(t, "/api/v1/profiles", &got)
|
|
by := map[string]api.ProfileView{}
|
|
for _, p := range got {
|
|
by[p.Name] = p
|
|
}
|
|
es, ok := by["ubiquiti-edgeswitch"]
|
|
if !ok || es.Source != "shipped" || es.Priority != 30 || len(es.Match.SysObjectID) != 1 || es.Extends != "generic" {
|
|
t.Fatalf("edgeswitch = %+v", es)
|
|
}
|
|
if _, ok := by["generic"]; !ok || len(by) < 15 {
|
|
t.Fatalf("%d profiles", len(by))
|
|
}
|
|
}
|
|
|
|
func TestPutDeviceProfile(t *testing.T) {
|
|
e := profileEnv(t)
|
|
d := e.device(t, "sw", "", "", func(d *store.Device) { d.SysObjectID, d.SysDescr = "1.3.6.1.4.1.4413", "EdgeSwitch 12 Fiber" })
|
|
var v api.DeviceView
|
|
e.getJSON(t, "/api/v1/devices/"+d.Slug, &v)
|
|
if v.ProfilePinned || v.ProfileMatch == nil || v.ProfileMatch.MatchedBy != "sysObjectID" {
|
|
t.Fatalf("view %+v %+v", v, v.ProfileMatch)
|
|
}
|
|
if rec := e.do(http.MethodPut, "/api/v1/devices/"+d.Slug, `{"profile":"nope"}`); rec.Code != http.StatusUnprocessableEntity {
|
|
t.Fatalf("unknown profile: %d %s", rec.Code, rec.Body)
|
|
}
|
|
if rec := e.do(http.MethodPut, "/api/v1/devices/"+d.Slug, `{"profile":"vyos"}`); rec.Code != http.StatusOK {
|
|
t.Fatalf("pin: %d %s", rec.Code, rec.Body)
|
|
}
|
|
e.getJSON(t, "/api/v1/devices/"+d.Slug, &v)
|
|
if v.Profile != "vyos" || !v.ProfilePinned || v.ProfileMatch.MatchedBy != "pinned" {
|
|
t.Fatalf("after pin %+v", v)
|
|
}
|
|
if rec := e.do(http.MethodPut, "/api/v1/devices/"+d.Slug, `{"profile":""}`); rec.Code != http.StatusOK {
|
|
t.Fatalf("auto: %d %s", rec.Code, rec.Body)
|
|
}
|
|
e.getJSON(t, "/api/v1/devices/"+d.Slug, &v)
|
|
if v.Profile != "ubiquiti-edgeswitch" || v.ProfilePinned {
|
|
t.Fatalf("after unpin %+v", v)
|
|
}
|
|
audits, _, err := e.st.ListAudit(context.Background(), store.Page{Limit: 10})
|
|
if err != nil || len(audits) < 2 || audits[0].Action != "device.edit" {
|
|
t.Fatalf("audit rows %d, %v", len(audits), err)
|
|
}
|
|
}
|
|
|
|
func TestGenericDeviceExplainsWhy(t *testing.T) {
|
|
e := profileEnv(t)
|
|
d := e.device(t, "odd", "", "", func(d *store.Device) { d.SysObjectID, d.SysDescr = "1.3.6.1.4.1.99999.1", "mystery box" })
|
|
var v api.DeviceView
|
|
e.getJSON(t, "/api/v1/devices/"+d.Slug, &v)
|
|
if v.ProfileMatch == nil || v.ProfileMatch.MatchedBy != "default" ||
|
|
v.ProfileMatch.GenericReason != "no vendor profile matched sysObjectID 1.3.6.1.4.1.99999.1 or sysDescr" {
|
|
t.Fatalf("%+v", v.ProfileMatch)
|
|
}
|
|
}
|