- CatalogStatus type with forward-only state machine (draft→indexed→…→complete) - Transition() enforces valid transitions, returns error with 'invalid transition' message - ParseCatalogStatus() validates known status strings - HardwareRecord domain type composing netbox.Device with quality gate state - CatalogUpdater.UpdateCatalogStatus() validates transition then PatchCustomFields - SyncTags() normalizes tags (slug form) and ensures they exist in NetBox - normalizeTags deduplicates across case/whitespace/space-vs-hyphen variants - ensureTag uses go-netbox v4 NewTagRequest(name, slug) / ExtrasTagsCreate - All 12 state machine table-driven tests pass
71 lines
2.2 KiB
Go
71 lines
2.2 KiB
Go
package inventory_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.georgsen.dk/hwlab/internal/inventory"
|
|
)
|
|
|
|
func TestCanTransitionTo(t *testing.T) {
|
|
tests := []struct {
|
|
from inventory.CatalogStatus
|
|
to inventory.CatalogStatus
|
|
allowed bool
|
|
}{
|
|
{inventory.StatusDraft, inventory.StatusIndexed, true},
|
|
{inventory.StatusDraft, inventory.StatusComplete, false},
|
|
{inventory.StatusDraft, inventory.StatusDraft, false},
|
|
{inventory.StatusIndexed, inventory.StatusNeedsResearch, true},
|
|
{inventory.StatusIndexed, inventory.StatusResearched, true},
|
|
{inventory.StatusIndexed, inventory.StatusDraft, false},
|
|
{inventory.StatusNeedsResearch, inventory.StatusResearched, true},
|
|
{inventory.StatusNeedsResearch, inventory.StatusIndexed, false},
|
|
{inventory.StatusResearched, inventory.StatusComplete, true},
|
|
{inventory.StatusResearched, inventory.StatusDraft, false},
|
|
{inventory.StatusComplete, inventory.StatusDraft, false},
|
|
{inventory.StatusComplete, inventory.StatusResearched, false},
|
|
}
|
|
for _, tt := range tests {
|
|
got := tt.from.CanTransitionTo(tt.to)
|
|
if got != tt.allowed {
|
|
t.Errorf("%s → %s: want %v, got %v", tt.from, tt.to, tt.allowed, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTransitionValid(t *testing.T) {
|
|
got, err := inventory.Transition(inventory.StatusDraft, inventory.StatusIndexed)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != inventory.StatusIndexed {
|
|
t.Errorf("want indexed, got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestTransitionInvalid(t *testing.T) {
|
|
_, err := inventory.Transition(inventory.StatusDraft, inventory.StatusComplete)
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid transition")
|
|
}
|
|
if !strings.Contains(err.Error(), "invalid transition") {
|
|
t.Errorf("error should mention 'invalid transition', got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseCatalogStatus(t *testing.T) {
|
|
for _, s := range []string{"draft", "indexed", "needs_research", "researched", "complete"} {
|
|
cs, err := inventory.ParseCatalogStatus(s)
|
|
if err != nil {
|
|
t.Errorf("ParseCatalogStatus(%q): unexpected error: %v", s, err)
|
|
}
|
|
if string(cs) != s {
|
|
t.Errorf("ParseCatalogStatus(%q) = %q, want %q", s, cs, s)
|
|
}
|
|
}
|
|
_, err := inventory.ParseCatalogStatus("unknown_status")
|
|
if err == nil {
|
|
t.Error("expected error for unknown status")
|
|
}
|
|
}
|