mirror of
https://github.com/navidrome/navidrome.git
synced 2025-07-13 23:21:21 +03:00
Improved the error handling logic in the checkErr function to map specific error strings to their corresponding API error constants. This change ensures that errors from plugins are correctly identified and returned, enhancing the robustness of error reporting. Signed-off-by: Deluan <deluan@navidrome.org>
286 lines
7.7 KiB
Go
286 lines
7.7 KiB
Go
package plugins
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/navidrome/navidrome/plugins/api"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
type nilInstance struct{}
|
|
|
|
var _ = Describe("baseCapability", func() {
|
|
var ctx = context.Background()
|
|
|
|
It("should load instance using loadFunc", func() {
|
|
called := false
|
|
plugin := &baseCapability[*nilInstance, any]{
|
|
wasmPath: "",
|
|
id: "test",
|
|
capability: "test",
|
|
loadFunc: func(ctx context.Context, _ any, path string) (*nilInstance, error) {
|
|
called = true
|
|
return &nilInstance{}, nil
|
|
},
|
|
}
|
|
inst, done, err := plugin.getInstance(ctx, "test")
|
|
defer done()
|
|
Expect(err).To(BeNil())
|
|
Expect(inst).ToNot(BeNil())
|
|
Expect(called).To(BeTrue())
|
|
})
|
|
})
|
|
|
|
var _ = Describe("checkErr", func() {
|
|
Context("when resp is nil", func() {
|
|
It("should return nil error when both resp and err are nil", func() {
|
|
var resp *testErrorResponse
|
|
|
|
result, err := checkErr(resp, nil)
|
|
|
|
Expect(result).To(BeNil())
|
|
Expect(err).To(BeNil())
|
|
})
|
|
|
|
It("should return original error unchanged for non-API errors", func() {
|
|
var resp *testErrorResponse
|
|
originalErr := errors.New("original error")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(BeNil())
|
|
Expect(err).To(Equal(originalErr))
|
|
})
|
|
|
|
It("should return mapped API error for ErrNotImplemented", func() {
|
|
var resp *testErrorResponse
|
|
err := errors.New("plugin:not_implemented")
|
|
|
|
result, mappedErr := checkErr(resp, err)
|
|
|
|
Expect(result).To(BeNil())
|
|
Expect(mappedErr).To(Equal(api.ErrNotImplemented))
|
|
})
|
|
|
|
It("should return mapped API error for ErrNotFound", func() {
|
|
var resp *testErrorResponse
|
|
err := errors.New("plugin:not_found")
|
|
|
|
result, mappedErr := checkErr(resp, err)
|
|
|
|
Expect(result).To(BeNil())
|
|
Expect(mappedErr).To(Equal(api.ErrNotFound))
|
|
})
|
|
})
|
|
|
|
Context("when resp is a typed nil that implements errorResponse", func() {
|
|
It("should not panic and return original error", func() {
|
|
var resp *testErrorResponse // typed nil
|
|
originalErr := errors.New("original error")
|
|
|
|
// This should not panic
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(BeNil())
|
|
Expect(err).To(Equal(originalErr))
|
|
})
|
|
|
|
It("should handle typed nil with nil error gracefully", func() {
|
|
var resp *testErrorResponse // typed nil
|
|
|
|
// This should not panic
|
|
result, err := checkErr(resp, nil)
|
|
|
|
Expect(result).To(BeNil())
|
|
Expect(err).To(BeNil())
|
|
})
|
|
})
|
|
|
|
Context("when resp implements errorResponse with non-empty error", func() {
|
|
It("should create new error when original error is nil", func() {
|
|
resp := &testErrorResponse{errorMsg: "plugin error"}
|
|
|
|
result, err := checkErr(resp, nil)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError("plugin error"))
|
|
})
|
|
|
|
It("should wrap original error when both exist", func() {
|
|
resp := &testErrorResponse{errorMsg: "plugin error"}
|
|
originalErr := errors.New("original error")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(HaveOccurred())
|
|
// Check that both error messages are present in the joined error
|
|
errStr := err.Error()
|
|
Expect(errStr).To(ContainSubstring("plugin error"))
|
|
Expect(errStr).To(ContainSubstring("original error"))
|
|
})
|
|
|
|
It("should return mapped API error for ErrNotImplemented when no original error", func() {
|
|
resp := &testErrorResponse{errorMsg: "plugin:not_implemented"}
|
|
|
|
result, err := checkErr(resp, nil)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError(api.ErrNotImplemented))
|
|
})
|
|
|
|
It("should return mapped API error for ErrNotFound when no original error", func() {
|
|
resp := &testErrorResponse{errorMsg: "plugin:not_found"}
|
|
|
|
result, err := checkErr(resp, nil)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError(api.ErrNotFound))
|
|
})
|
|
|
|
It("should return mapped API error for ErrNotImplemented even with original error", func() {
|
|
resp := &testErrorResponse{errorMsg: "plugin:not_implemented"}
|
|
originalErr := errors.New("original error")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError(api.ErrNotImplemented))
|
|
})
|
|
|
|
It("should return mapped API error for ErrNotFound even with original error", func() {
|
|
resp := &testErrorResponse{errorMsg: "plugin:not_found"}
|
|
originalErr := errors.New("original error")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError(api.ErrNotFound))
|
|
})
|
|
})
|
|
|
|
Context("when resp implements errorResponse with empty error", func() {
|
|
It("should return original error unchanged", func() {
|
|
resp := &testErrorResponse{errorMsg: ""}
|
|
originalErr := errors.New("original error")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError(originalErr))
|
|
})
|
|
|
|
It("should return nil error when both are empty/nil", func() {
|
|
resp := &testErrorResponse{errorMsg: ""}
|
|
|
|
result, err := checkErr(resp, nil)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(BeNil())
|
|
})
|
|
|
|
It("should map original API error when response error is empty", func() {
|
|
resp := &testErrorResponse{errorMsg: ""}
|
|
originalErr := errors.New("plugin:not_implemented")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError(api.ErrNotImplemented))
|
|
})
|
|
})
|
|
|
|
Context("when resp does not implement errorResponse", func() {
|
|
It("should return original error unchanged", func() {
|
|
resp := &testNonErrorResponse{data: "some data"}
|
|
originalErr := errors.New("original error")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(Equal(originalErr))
|
|
})
|
|
|
|
It("should return nil error when original error is nil", func() {
|
|
resp := &testNonErrorResponse{data: "some data"}
|
|
|
|
result, err := checkErr(resp, nil)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(BeNil())
|
|
})
|
|
|
|
It("should map original API error when response doesn't implement errorResponse", func() {
|
|
resp := &testNonErrorResponse{data: "some data"}
|
|
originalErr := errors.New("plugin:not_found")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError(api.ErrNotFound))
|
|
})
|
|
})
|
|
|
|
Context("when resp is a value type (not pointer)", func() {
|
|
It("should handle value types that implement errorResponse", func() {
|
|
resp := testValueErrorResponse{errorMsg: "value error"}
|
|
originalErr := errors.New("original error")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(HaveOccurred())
|
|
// Check that both error messages are present in the joined error
|
|
errStr := err.Error()
|
|
Expect(errStr).To(ContainSubstring("value error"))
|
|
Expect(errStr).To(ContainSubstring("original error"))
|
|
})
|
|
|
|
It("should handle value types with empty error", func() {
|
|
resp := testValueErrorResponse{errorMsg: ""}
|
|
originalErr := errors.New("original error")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError(originalErr))
|
|
})
|
|
|
|
It("should handle value types with API error", func() {
|
|
resp := testValueErrorResponse{errorMsg: "plugin:not_implemented"}
|
|
originalErr := errors.New("original error")
|
|
|
|
result, err := checkErr(resp, originalErr)
|
|
|
|
Expect(result).To(Equal(resp))
|
|
Expect(err).To(MatchError(api.ErrNotImplemented))
|
|
})
|
|
})
|
|
})
|
|
|
|
// Test helper types
|
|
type testErrorResponse struct {
|
|
errorMsg string
|
|
}
|
|
|
|
func (t *testErrorResponse) GetError() string {
|
|
if t == nil {
|
|
return "" // This is what would typically happen with a typed nil
|
|
}
|
|
return t.errorMsg
|
|
}
|
|
|
|
type testNonErrorResponse struct {
|
|
data string
|
|
}
|
|
|
|
type testValueErrorResponse struct {
|
|
errorMsg string
|
|
}
|
|
|
|
func (t testValueErrorResponse) GetError() string {
|
|
return t.errorMsg
|
|
}
|