Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v5) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v6) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v7) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to GHCR (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to Docker Hub (push) Has been cancelled
Pipeline: Test, Lint, Build / Cleanup digest artifacts (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
POEditor import / update-translations (push) Has been cancelled
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
|
|
}
|