Files
egress-proxies-operator/internal/provider/kvm/errors_test.go
kacerrandClaude Fable 5.1 5a6c889531 Make the libvirt trace compact: libvirt API names, one error field, terse test output
The first live run showed the trace working but noisy: testr printed
ts/level/msg and the per-run constants on every line, the error text
appeared twice, host appeared twice on image-cache lines, and the Go
method name was less useful than the libvirt function it maps to.

The tracer now logs rpc=virDomainLookupByName (go-libvirt's names are the
public API names without the vir prefix), error plus libvirtError (code
name) and libvirtCode, and image.go stops re-adding host. The integration
test replaces testr with a compact logr sink: time, logger name, the rpc
as headline, then fields, with provider/host/name omitted since the first
line of the run states them. Docs and execution log updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 19:13:51 +00:00

94 lines
3.4 KiB
Go

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)
}
})
}
}