- IntakeHandler with IntakeOrchestrator/IntakeNetBoxClient/IntakeCatalogUpdater/IntakeWAQ interfaces - Validates 1-3 photos, base64-encodes, calls Analyze, allocates HW-ID - Quick-add mode: confidence >= threshold skips review, creates NetBox record immediately - WAQ enqueue on NetBox failure returns 202 with queued=true - nil WAQ + NetBox down returns 503 - Six unit tests: reject-0, reject-4, high-confidence, low-confidence, quick-add, netbox-down - [Rule 1 - Bug] PatchCustomFields signature changed int -> int64 to match NetBoxOpsClient interface - [Rule 1 - Bug] UpdateCatalogStatus signature changed int -> int64 for consistency with CreateDevice return type
105 lines
3 KiB
Go
105 lines
3 KiB
Go
package netbox_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
|
|
"git.georgsen.dk/hwlab/internal/netbox"
|
|
)
|
|
|
|
func TestParseCustomFieldsNil(t *testing.T) {
|
|
cf := netbox.ParseCustomFields(nil)
|
|
if cf.HWID != "" {
|
|
t.Error("expected empty HWID for nil map")
|
|
}
|
|
}
|
|
|
|
func TestParseCustomFieldsHWID(t *testing.T) {
|
|
raw := map[string]interface{}{
|
|
"hw_id": "HW-00001",
|
|
"catalog_status": "draft",
|
|
}
|
|
cf := netbox.ParseCustomFields(raw)
|
|
if cf.HWID != "HW-00001" {
|
|
t.Errorf("want HW-00001, got %s", cf.HWID)
|
|
}
|
|
if cf.CatalogStatus != "draft" {
|
|
t.Errorf("want draft, got %s", cf.CatalogStatus)
|
|
}
|
|
}
|
|
|
|
func TestParseCustomFieldsPhotoURLs(t *testing.T) {
|
|
raw := map[string]interface{}{
|
|
"photo_urls": []interface{}{"http://a.com/1.jpg", "http://a.com/2.jpg"},
|
|
}
|
|
cf := netbox.ParseCustomFields(raw)
|
|
if len(cf.PhotoURLs) != 2 {
|
|
t.Errorf("want 2 photo urls, got %d", len(cf.PhotoURLs))
|
|
}
|
|
}
|
|
|
|
func TestBuildCustomFieldsPatch(t *testing.T) {
|
|
patch := netbox.BuildCustomFieldsPatch("HW-00001", "draft", nil)
|
|
if patch["hw_id"] != "HW-00001" {
|
|
t.Errorf("hw_id: want HW-00001, got %v", patch["hw_id"])
|
|
}
|
|
if patch["catalog_status"] != "draft" {
|
|
t.Errorf("catalog_status: want draft, got %v", patch["catalog_status"])
|
|
}
|
|
if _, ok := patch["photo_urls"]; ok {
|
|
t.Error("photo_urls should not be present when nil passed")
|
|
}
|
|
}
|
|
|
|
func TestBuildCustomFieldsPatchWithURLs(t *testing.T) {
|
|
patch := netbox.BuildCustomFieldsPatch("HW-00001", "indexed", []string{"http://a.com/1.jpg"})
|
|
urls, ok := patch["photo_urls"].([]string)
|
|
if !ok {
|
|
t.Fatal("photo_urls should be []string")
|
|
}
|
|
if len(urls) != 1 {
|
|
t.Errorf("want 1 url, got %d", len(urls))
|
|
}
|
|
}
|
|
|
|
// TestPatchCustomFieldsRoundTrip is an integration test that writes and reads back custom fields.
|
|
// It requires a real NetBox token and a pre-existing device with a known ID.
|
|
func TestPatchCustomFieldsRoundTrip(t *testing.T) {
|
|
token := os.Getenv("HWLAB_NETBOX_TOKEN")
|
|
if len(token) != 40 {
|
|
t.Skip("HWLAB_NETBOX_TOKEN is not a real 40-char token — skipping integration test")
|
|
}
|
|
|
|
deviceIDStr := os.Getenv("HWLAB_TEST_DEVICE_ID")
|
|
if deviceIDStr == "" {
|
|
t.Skip("HWLAB_TEST_DEVICE_ID not set — skipping round-trip integration test")
|
|
}
|
|
|
|
var deviceID int64
|
|
if _, err := fmt.Sscanf(deviceIDStr, "%d", &deviceID); err != nil {
|
|
t.Fatalf("HWLAB_TEST_DEVICE_ID must be an integer: %v", err)
|
|
}
|
|
|
|
c, err := netbox.NewClient("http://10.5.0.130:8000/api", token)
|
|
if err != nil {
|
|
t.Fatalf("NewClient: %v", err)
|
|
}
|
|
|
|
patch := netbox.BuildCustomFieldsPatch("HW-99999", "draft", nil)
|
|
if err := c.PatchCustomFields(context.Background(), deviceID, patch); err != nil {
|
|
t.Fatalf("PatchCustomFields: %v", err)
|
|
}
|
|
|
|
device, err := c.GetDevice(context.Background(), int(deviceID))
|
|
if err != nil {
|
|
t.Fatalf("GetDevice: %v", err)
|
|
}
|
|
if device.CustomFields.HWID != "HW-99999" {
|
|
t.Errorf("round-trip hw_id: want HW-99999, got %q", device.CustomFields.HWID)
|
|
}
|
|
if device.CustomFields.CatalogStatus != "draft" {
|
|
t.Errorf("round-trip catalog_status: want draft, got %q", device.CustomFields.CatalogStatus)
|
|
}
|
|
}
|