Skip to content

tests: Added unit tests for user list command and user view#685

Open
rkcoder101 wants to merge 2 commits intogoharbor:mainfrom
rkcoder101:test/user-list
Open

tests: Added unit tests for user list command and user view#685
rkcoder101 wants to merge 2 commits intogoharbor:mainfrom
rkcoder101:test/user-list

Conversation

@rkcoder101
Copy link
Contributor

Description

This PR introduces unit tests for the user list command (cmd/harbor/root/user/list.go) and user view (pkg/views/user)

Relates to #591

Is a sub-part of #653

Note

No changes have been done to the core logic of the application, only a little refactoring for the purpose of writing some nice and clean test code :)

…ctored pkg/views/user/list/view.go and wrote unit tests for the same

Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
@rkcoder101 rkcoder101 marked this pull request as ready for review February 7, 2026 09:08
Copilot AI review requested due to automatic review settings February 7, 2026 09:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves unit test coverage around the harbor user list command flow and the user list view rendering, with small refactors to make the logic more testable.

Changes:

  • Added unit tests for cmd/harbor/root/user/list.go (command flags, pagination behavior, and output modes).
  • Refactored the user list view to extract row-building into MakeUserRows and made ListUsers return an error instead of exiting.
  • Refactored user listing logic into GetUsers/PrintUsers and introduced a UserLister interface to allow mocking in tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
pkg/views/user/list/view.go Extracts row creation (MakeUserRows) and makes ListUsers return an error.
pkg/views/user/list/view_test.go Adds tests for MakeUserRows.
cmd/harbor/root/user/list.go Refactors command to separate fetching vs printing and adds UserLister for testability.
cmd/harbor/root/user/list_test.go Adds unit tests for printing, pagination behavior, and command flag wiring.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to 21
"github.com/goharbor/go-client/pkg/sdk/v2.0/client/user"
"github.com/goharbor/go-client/pkg/sdk/v2.0/models"
"github.com/goharbor/harbor-cli/pkg/api"
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this file’s package user, importing github.com/goharbor/go-client/pkg/sdk/v2.0/client/user as user is very easy to confuse with the current package name and makes types like *user.ListUsersOK hard to read. Consider aliasing the import (e.g., clientuser) and updating the interface return type accordingly.

Copilot uses AI. Check for mistakes.
if formatFlag != "" {
err := utils.PrintFormat(allUsers, formatFlag)
if err != nil {
log.Error(err)
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PrintUsers returns an error, but in the formatted-output branch a utils.PrintFormat failure is only logged and the function still returns nil. This makes the command exit successfully even though output failed; return the formatting error instead of swallowing it.

Suggested change
log.Error(err)
log.Error(err)
return err

Copilot uses AI. Check for mistakes.
Comment on lines 52 to 56
if isUnauthorizedError(err) {
return nil, fmt.Errorf("Permission denied: Admin privileges are required to execute this command.")
}
} else {
response, err := api.ListUsers(opts)
if err != nil {
if isUnauthorizedError(err) {
return fmt.Errorf("Permission denied: Admin privileges are required to execute this command.")
}
return fmt.Errorf("failed to list users: %v", err)
}
allUsers = response.Payload
return nil, fmt.Errorf("failed to list users: %v", err)
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error wrapping uses %v, which prevents callers from unwrapping/inspecting the underlying error. Prefer %w when re-wrapping (e.g., fmt.Errorf("failed to list users: %w", err)).

Copilot uses AI. Check for mistakes.
Comment on lines 68 to 72
if isUnauthorizedError(err) {
return nil, fmt.Errorf("Permission denied: Admin privileges are required to execute this command.")
}
formatFlag := viper.GetString("output-format")
if formatFlag != "" {
err := utils.PrintFormat(allUsers, formatFlag)
if err != nil {
log.Error(err)
}
} else {
list.ListUsers(allUsers)
return nil, fmt.Errorf("failed to list users: %v", err)
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: use %w when re-wrapping err so it can be unwrapped by callers.

Copilot uses AI. Check for mistakes.
Comment on lines 30 to 33
expectedTimeStr, err := utils.FormatCreatedTime(dateStr)
if err != nil {
t.Fatalf("failed to format created time %q: %v", dateStr, err)
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test asserts an exact string produced by FormatCreatedTime (which is based on time.Since). Because that value changes over time (and can flip at a day boundary), the assertion can be flaky. Prefer asserting the time column is non-empty or matches a suffix/pattern (e.g., contains "day ago").

Copilot uses AI. Check for mistakes.
origStdout := os.Stdout
defer func() { os.Stdout = origStdout }()

r, w, _ := os.Pipe()
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

os.Pipe()’s error return is ignored here. Even in tests, handle the error (and fail the test) so the helper doesn’t panic or behave unpredictably if pipe creation fails.

Suggested change
r, w, _ := os.Pipe()
r, w, err := os.Pipe()
if err != nil {
return "", err
}

Copilot uses AI. Check for mistakes.
Comment on lines 42 to 49
r, w, _ := os.Pipe()
os.Stdout = w

if err := f(); err != nil {
return "", err
}

w.Close()
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If f() returns an error, this helper returns early without closing the pipe writer/reader, which can leak file descriptors across test cases. Use defer to close w/r (and restore stdout) so cleanup happens on all paths.

Suggested change
r, w, _ := os.Pipe()
os.Stdout = w
if err := f(); err != nil {
return "", err
}
w.Close()
r, w, err := os.Pipe()
if err != nil {
return "", err
}
defer func() {
_ = w.Close()
_ = r.Close()
}()
os.Stdout = w
if err := f(); err != nil {
return "", err
}
if err := w.Close(); err != nil {
return "", err
}

Copilot uses AI. Check for mistakes.
return buf.String(), nil
}
func TestPrintUsers(t *testing.T) {
testDate, _ := strfmt.ParseDateTime("2023-01-01T12:00:00Z")
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The strfmt.ParseDateTime error is ignored here. Check err and t.Fatalf on failure to avoid masking unexpected parsing issues.

Suggested change
testDate, _ := strfmt.ParseDateTime("2023-01-01T12:00:00Z")
testDate, err := strfmt.ParseDateTime("2023-01-01T12:00:00Z")
if err != nil {
t.Fatalf("failed to parse test date: %v", err)
}

Copilot uses AI. Check for mistakes.
@rkcoder101 rkcoder101 mentioned this pull request Feb 7, 2026
5 tasks
Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant