From 2072a70bed85fd1ae40e26dca1d5977416af1b2f Mon Sep 17 00:00:00 2001 From: DivyanshuVorrtex Date: Sun, 3 May 2026 04:08:00 +0000 Subject: [PATCH] fix(utils): return all harbor API error messages instead of just the first one Signed-off-by: DivyanshuVorrtex --- pkg/utils/error.go | 6 +- pkg/utils/error_test.go | 139 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 pkg/utils/error_test.go diff --git a/pkg/utils/error.go b/pkg/utils/error.go index e7cda1c76..b85c9b2f7 100644 --- a/pkg/utils/error.go +++ b/pkg/utils/error.go @@ -45,7 +45,11 @@ func ParseHarborErrorMsg(err error) string { var harborErr HarborErrorPayload if unmarshalErr := json.Unmarshal(jsonBytes, &harborErr); unmarshalErr == nil { if len(harborErr.Errors) > 0 { - return harborErr.Errors[0].Message + var messages []string + for _, e := range harborErr.Errors { + messages = append(messages, e.Message) + } + return strings.Join(messages, "; ") } } } diff --git a/pkg/utils/error_test.go b/pkg/utils/error_test.go new file mode 100644 index 000000000..7ead37e9e --- /dev/null +++ b/pkg/utils/error_test.go @@ -0,0 +1,139 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package utils_test + +import ( + "errors" + "testing" + + "github.com/goharbor/harbor-cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +type mockPayload struct { + Payload interface{} +} + +func (m *mockPayload) Error() string { + return "mock error" +} + +func TestParseHarborErrorMsg(t *testing.T) { + tests := []struct { + name string + err error + expected string + }{ + { + "nil error", + nil, + "", + }, + { + "plain error", + errors.New("simple error"), + "simple error", + }, + { + "single harbor error", + &mockPayload{ + Payload: utils.HarborErrorPayload{ + Errors: []struct { + Code string `json:"code"` + Message string `json:"message"` + }{ + {Code: "400", Message: "first error"}, + }, + }, + }, + "first error", + }, + { + "multiple harbor errors", + &mockPayload{ + Payload: utils.HarborErrorPayload{ + Errors: []struct { + Code string `json:"code"` + Message string `json:"message"` + }{ + {Code: "400", Message: "first error"}, + {Code: "401", Message: "second error"}, + }, + }, + }, + "first error; second error", + }, + { + "invalid payload type", + &mockPayload{ + Payload: make(chan int), + }, + "mock error", + }, + { + "invalid json format", + &mockPayload{ + Payload: "this is a string, not a struct", + }, + "mock error", + }, + { + "empty errors slice", + &mockPayload{ + Payload: utils.HarborErrorPayload{ + Errors: nil, + }, + }, + "mock error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := utils.ParseHarborErrorMsg(tt.err) + assert.Equal(t, tt.expected, got) + }) + } +} + +func TestParseHarborErrorCode(t *testing.T) { + tests := []struct { + name string + err error + expected string + }{ + { + "bracket format", + errors.New("[GET /projects][404] Not Found"), + "404", + }, + { + "status format", + errors.New("request failed (status 401) Unauthorized"), + "401", + }, + { + "no code", + errors.New("generic error"), + "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := utils.ParseHarborErrorCode(tt.err) + assert.Equal(t, tt.expected, got) + }) + } +}