package kvm import ( "context" "errors" "fmt" "io" "reflect" "testing" libvirt "github.com/digitalocean/go-libvirt" "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider" ) func lvErr(code libvirt.ErrorNumber) error { return libvirt.Error{Code: uint32(code), Message: code.String()} } func TestClassify(t *testing.T) { t.Parallel() tests := []struct { name string err error want error }{ {"no domain", lvErr(libvirt.ErrNoDomain), provider.ErrNotFound}, {"no storage vol", lvErr(libvirt.ErrNoStorageVol), provider.ErrNotFound}, {"no storage pool", lvErr(libvirt.ErrNoStoragePool), provider.ErrPermanent}, {"xml error", lvErr(libvirt.ErrXMLError), provider.ErrPermanent}, {"xml detail", lvErr(libvirt.ErrXMLDetail), provider.ErrPermanent}, {"config unsupported", lvErr(libvirt.ErrConfigUnsupported), provider.ErrPermanent}, {"no support", lvErr(libvirt.ErrNoSupport), provider.ErrPermanent}, {"invalid arg", lvErr(libvirt.ErrInvalidArg), provider.ErrPermanent}, {"auth failed", lvErr(libvirt.ErrAuthFailed), provider.ErrPermanent}, {"access denied", lvErr(libvirt.ErrAccessDenied), provider.ErrPermanent}, {"operation invalid", lvErr(libvirt.ErrOperationInvalid), provider.ErrTransient}, {"operation failed", lvErr(libvirt.ErrOperationFailed), provider.ErrTransient}, {"internal error", lvErr(libvirt.ErrInternalError), provider.ErrTransient}, {"storage vol exists", lvErr(libvirt.ErrStorageVolExist), provider.ErrTransient}, {"wrapped libvirt error", fmt.Errorf("defining: %w", lvErr(libvirt.ErrNoDomain)), provider.ErrNotFound}, {"pre-tagged permanent", fmt.Errorf("%w: image returned HTTP 404", provider.ErrPermanent), provider.ErrPermanent}, {"pre-tagged transient wrapping libvirt not-found", fmt.Errorf("%w: %w", provider.ErrTransient, lvErr(libvirt.ErrNoDomain)), provider.ErrTransient}, {"transport eof", io.EOF, provider.ErrTransient}, {"context cancelled", context.Canceled, provider.ErrTransient}, {"plain error", errors.New("ssh: handshake failed"), provider.ErrTransient}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { t.Parallel() if got := classify(tc.err); !errors.Is(got, tc.want) { t.Errorf("classify(%v) = %v, want %v", tc.err, got, tc.want) } }) } } func TestHasCode(t *testing.T) { t.Parallel() err := fmt.Errorf("lookup: %w", lvErr(libvirt.ErrNoDomain)) if !hasCode(err, libvirt.ErrNoDomain) { t.Error("hasCode(ErrNoDomain) = false through a wrap") } if hasCode(err, libvirt.ErrNoStorageVol) { t.Error("hasCode(ErrNoStorageVol) = true for an ErrNoDomain error") } if hasCode(io.EOF, libvirt.ErrNoDomain) || isLibvirtError(io.EOF) { t.Error("io.EOF classified as a libvirt error") } } func TestLibvirtErrorFields(t *testing.T) { t.Parallel() tests := []struct { name string err error want []any }{ {"libvirt error", lvErr(libvirt.ErrNoDomain), []any{"libvirtError", "ErrNoDomain", "libvirtCode", uint32(42)}}, {"wrapped libvirt error", fmt.Errorf("looking up: %w", lvErr(libvirt.ErrNoDomain)), []any{"libvirtError", "ErrNoDomain", "libvirtCode", uint32(42)}}, {"transport error", io.EOF, nil}, {"cancelled", context.Canceled, nil}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { t.Parallel() got := libvirtErrorFields(tc.err) if !reflect.DeepEqual(got, tc.want) { t.Errorf("libvirtErrorFields() = %#v, want %#v", got, tc.want) } }) } }