Skip to content

Conversation

@rkcoder101
Copy link
Contributor

@rkcoder101 rkcoder101 commented Jan 27, 2026

Description

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

Relates to #591

Test Coverage Checklist

  • user list
  • user create
  • user delete
  • user elevate
  • user password

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

List of refactoring changes done till now (for writing unit tests)

  1. cmd/harbor/root/user/list.go: Separated PrintUser() and GetUsers() from UserListCmd()
  2. pkg/utils/helper.go: Added FPrintFormat() to support io.Writer injection. The original PrintFormat() remains unchanged, ensuring full backward compatibility with existing code.
  3. pkg/utils/utils.go: Added FPrintPayloadInJSONFormat() and FPrintPayloadInYAMLFormat() to support io.Writer. The original PrintPayloadInJSONFormat() and PrintPayloadInYAMLFormat() remain unchanged.
  4. pkg/views/user/list/view.go: Separated MakeUserRows() from ListUsers()
  5. cmd/harbor/root/user/create.go: Separated CreateUser() from UserCreateCmd()
  6. cmd/harbor/root/user/create.go: Removed the redundant createUserView() as it was just a wrapper which called two functions
  7. cmd/harbor/root/user/delete.go: Separated DeleteUser() from UserDeleteCmd()
  8. cmd/harbor/root/user/elevate.go: Separated ElevateUser() from ElevateUserCmd()
  9. cmd/harbor/root/user/elevate.go: Separated ChangePassword() from UserPasswordChangeCmd()

@codecov
Copy link

codecov bot commented Jan 27, 2026

Codecov Report

❌ Patch coverage is 60.19417% with 82 lines in your changes missing coverage. Please review.
✅ Project coverage is 8.37%. Comparing base (60ad0bd) to head (97ecd30).
⚠️ Report is 82 commits behind head on main.

Files with missing lines Patch % Lines
cmd/harbor/root/user/delete.go 53.48% 19 Missing and 1 partial ⚠️
cmd/harbor/root/user/password.go 50.00% 14 Missing and 2 partials ⚠️
cmd/harbor/root/user/list.go 73.58% 11 Missing and 3 partials ⚠️
cmd/harbor/root/user/elevate.go 63.88% 12 Missing and 1 partial ⚠️
pkg/views/user/list/view.go 18.18% 9 Missing ⚠️
cmd/harbor/root/user/create.go 66.66% 6 Missing ⚠️
pkg/utils/utils.go 50.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##             main    #653      +/-   ##
=========================================
- Coverage   10.99%   8.37%   -2.62%     
=========================================
  Files         173     260      +87     
  Lines        8671   12902    +4231     
=========================================
+ Hits          953    1080     +127     
- Misses       7612   11704    +4092     
- Partials      106     118      +12     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rkcoder101 rkcoder101 changed the title test/Unit tests for user list command and ListUser view test/Unit tests for user command and user view Jan 28, 2026
Refactored user list command (UserListCmd) to separate out PrintUsers and GetUsers logic, and wrote unit tests for PrintUsers.
Refactored user list view (ListUsers) to separate out MakeUserRows logic and wrote unit tests for it.
Made some minor refactory changes in pkg/utils/helper.go and pkg/utils/utils.go to enable writing clean test code

Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
…st commit) in cmd/harbor/root/user/list.go, setup interface for mocking the API

Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
…y been tested

Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
…unit tests for all the functions related to user create command. Refactored only where needed

Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
…ests for DeleteUser()

Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
…ts for it

Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
@rkcoder101 rkcoder101 changed the title test/Unit tests for user command and user view test/Unit tests for user command Jan 31, 2026
…nit tests

Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
@rkcoder101 rkcoder101 marked this pull request as ready for review January 31, 2026 05:59
Copilot AI review requested due to automatic review settings January 31, 2026 05:59
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

Adds unit tests around the harbor user CLI commands and refactors a few command/view/util functions to make them testable via dependency injection and io.Writer output control.

Changes:

  • Added unit tests for user subcommands (list/create/delete/elevate/password) and the user list view row builder.
  • Refactored user commands to extract core logic into functions with injectable interfaces (e.g., lister/creator/deleter/elevator/password changer).
  • Introduced writer-based formatting/output helpers to enable deterministic output assertions in tests.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
pkg/views/user/list/view_test.go Adds unit tests for MakeUserRows.
pkg/views/user/list/view.go Splits row-building from rendering; makes list rendering writer-based and returns errors.
pkg/utils/utils.go Adds FPrintPayloadInJSONFormat/YAMLFormat for writer injection; wraps old functions.
pkg/utils/helper.go Adds FPrintFormat for writer injection; wraps old PrintFormat.
cmd/harbor/root/user/password_test.go Adds unit tests for password reset behavior via injected dependency.
cmd/harbor/root/user/password.go Refactors password reset flow behind a UserPasswordChanger interface.
cmd/harbor/root/user/list_test.go Adds unit tests for user listing: output formats + paging/validation logic.
cmd/harbor/root/user/list.go Refactors listing into GetUsers/PrintUsers and injects a UserLister.
cmd/harbor/root/user/elevate_test.go Adds unit tests for elevation flow including confirm/unauthorized/error cases.
cmd/harbor/root/user/elevate.go Refactors elevation flow behind a UserElevator interface.
cmd/harbor/root/user/delete_test.go Adds unit tests for deletion behavior including concurrent deletes and error paths.
cmd/harbor/root/user/delete.go Refactors deletion flow behind a UserDeleter interface.
cmd/harbor/root/user/create_test.go Adds unit tests for creation flow including FillUser path and unauthorized handling.
cmd/harbor/root/user/create.go Refactors user creation flow behind a UserCreator interface; hardens isUnauthorizedError(nil).

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

Comment on lines 182 to 197
/*
[X] make a test opts, only Page and PageSize fields needs to be there
[X] make a helper to populate the users
[X] mock api.List
[X] mock authentication (no need, just make the api mock to pass an error with 403 in case you need to check for unauthenticated user)
[X] test error logs
[X] check if the users received are correct

*/
/*
Logic for mocking api.ListUsers
-> should return (*user.ListUsersOK, error) ; Payload should be populated with the users
-> shoud return the PageSize*(Page-1) th user till PageSize*(Page) -1 th user (0 based indexing)
Helper for populating users:
-> models.UserResp ko append krna hoga api.ListFlags type ke payload me
*/
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

This large commented-out block (including non-English notes) looks like scratchpad/TODO content rather than a maintained test explanation. Please remove it or replace it with a brief, clear English comment describing the mock paging behavior the test relies on.

Suggested change
/*
[X] make a test opts, only Page and PageSize fields needs to be there
[X] make a helper to populate the users
[X] mock api.List
[X] mock authentication (no need, just make the api mock to pass an error with 403 in case you need to check for unauthenticated user)
[X] test error logs
[X] check if the users received are correct
*/
/*
Logic for mocking api.ListUsers
-> should return (*user.ListUsersOK, error) ; Payload should be populated with the users
-> shoud return the PageSize*(Page-1) th user till PageSize*(Page) -1 th user (0 based indexing)
Helper for populating users:
-> models.UserResp ko append krna hoga api.ListFlags type ke payload me
*/
// MockUserLister simulates the ListUsers API with simple paging behavior.
// It generates a deterministic list of users with IDs from 1 to numberOfUsersforTesting
// and, in UserList, returns only the slice for the requested Page and PageSize.
// When expectAuthError is true, UserList returns a 403-like error instead of data.

Copilot uses AI. Check for mistakes.
switch {
case len(allUsers) == 0:
if !strings.Contains(logs, "No users found") {
t.Errorf(`Expected logs to contain "No user found" but got: %s`, logs)
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

The failure message says "No user found" but the code is checking for "No users found". Please align the message with the actual expected log text to avoid confusion when the assertion fails.

Suggested change
t.Errorf(`Expected logs to contain "No user found" but got: %s`, logs)
t.Errorf(`Expected logs to contain "No users found" but got: %s`, logs)

Copilot uses AI. Check for mistakes.
Comment on lines 26 to 27
testDate, _ := strfmt.ParseDateTime(dateStr)
expectedTimeStr, _ := utils.FormatCreatedTime(dateStr)
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

The errors from strfmt.ParseDateTime and utils.FormatCreatedTime are ignored in the test. If these ever fail, the test will silently use zero values and can produce misleading failures. Please assert/require that these calls return no error.

Suggested change
testDate, _ := strfmt.ParseDateTime(dateStr)
expectedTimeStr, _ := utils.FormatCreatedTime(dateStr)
testDate, err := strfmt.ParseDateTime(dateStr)
if err != nil {
t.Fatalf("failed to parse date %q: %v", dateStr, err)
}
expectedTimeStr, err := utils.FormatCreatedTime(dateStr)
if err != nil {
t.Fatalf("failed to format created time %q: %v", dateStr, err)
}

Copilot uses AI. Check for mistakes.
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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Signed-off-by: Rayyan Khan <rayyanrehman101@gmail.com>
@rkcoder101
Copy link
Contributor Author

Hi @bupd @Vad1mo @qcserestipy kindly review this work whenever you guys get the time. Thanks!

@qcserestipy qcserestipy self-requested a review February 1, 2026 12:09
Copy link
Collaborator

@qcserestipy qcserestipy left a comment

Choose a reason for hiding this comment

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

Thanks so much for putting this work together! The test coverage you've added is solid and the implementation looks good. This is already ready to merge.

That said, I wanted to flag that there's still some untested functionality in the user command package that we should track:

Untested functions across the module:

github.com/goharbor/harbor-cli/cmd/harbor/root/user/cmd.go:20:                          User                                            0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/create.go:32:                       FillUser                                        0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/create.go:36:                       UserCreate                                      0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/create.go:39:                       CreateUser                                      100.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/create.go:56:                       UserCreateCmd                                   81.8%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/create.go:78:                       isUnauthorizedError                             100.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/delete.go:32:                       UserDelete                                      0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/delete.go:35:                       GetUserIDByName                                 0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/delete.go:38:                       GetUserIDFromUser                               0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/delete.go:42:                       DeleteUser                                      84.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/delete.go:92:                       UserDeleteCmd                                   0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/elevate.go:33:                      GetUserIDByName                                 0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/elevate.go:37:                      GetUserIDFromUser                               0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/elevate.go:41:                      ConfirmElevation                                0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/elevate.go:45:                      ElevateUser                                     0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/elevate.go:49:                      ElevateUser                                     91.3%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/elevate.go:87:                      ElevateUserCmd                                  50.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/list.go:36:                         UserList                                        0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/list.go:40:                         GetUsers                                        91.3%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/list.go:79:                         PrintUsers                                      81.8%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/list.go:97:                         UserListCmd                                     61.5%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/password.go:34:                     GetUserIDByName                                 0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/password.go:38:                     GetUserIDFromUser                               0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/password.go:42:                     FillPasswordView                                0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/password.go:46:                     ResetPassword                                   0.0%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/password.go:50:                     ChangePassword                                  83.3%
github.com/goharbor/harbor-cli/cmd/harbor/root/user/password.go:81:                     UserPasswordChangeCmd                           0.0%

Would you be open to tackling these in a follow-up PR? You could also open a tracking issue if that helps keep this on the radar.

Thank you again for your contribution!

@rkcoder101
Copy link
Contributor Author

rkcoder101 commented Feb 1, 2026

Thanks for the review! It means a lot to me
I wanted to have some clarification:
Most of the untested functions are actually the wrappers containing the functions being mocked. For example:
github.com/goharbor/harbor-cli/cmd/harbor/root/user/create.go:36: UserCreate
is actually the method which wraps the API to create the user. We have to write some integration tests for covering these, right?
Yup sure I am ready to handle further coverage and I will create a tracker issue to continue this work once you affirm.
Thanks!

@rkcoder101
Copy link
Contributor Author

Also while writing the unit tests I found some bugs and other things which can be improved on in the user command, I will open an issue regarding that too. It would be more efficient to implement those fixes as follow-up PRs once this foundational refactoring is merged.

Copy link
Collaborator

@bupd bupd left a comment

Choose a reason for hiding this comment

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

I would suggest breaking this PR and basically focus on maybe like create / delete - like that.

added suggestions below for improvements please fix it. avoid using Fprint.

}

fmt.Println(string(jsonStr))
fmt.Fprint(w, string(jsonStr))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
fmt.Fprint(w, string(jsonStr))
fmt.Println(string(jsonStr))

revert this back - our usecase doesn't fit for Fprint

Copy link
Contributor Author

@rkcoder101 rkcoder101 Feb 3, 2026

Choose a reason for hiding this comment

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

Oops I realised Fprint does not give a new line character at the end (like Println). Can I use Fprintln which enables both writer injection (and helps in testing) as well as gives \n at the end?
Actually I saw that PrintFormat() function is being used in many commands (example), and it would enable easy testing if we had a 'F' version of the function, through which we can inject a writer/buffer to capture and test the output.


if err != nil {
if isUnauthorizedError(err) {
log.Error("Permission denied: Admin privileges are required to execute this command.")
Copy link
Collaborator

Choose a reason for hiding this comment

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

robot accounts can also create users - this is not limited to admins.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok I will correct this 👍

@rkcoder101
Copy link
Contributor Author

I would suggest breaking this PR and basically focus on maybe like create / delete - like that.

added suggestions below for improvements please fix it. avoid using Fprint.

Sure I'll split this into separate PRs (create, delete, list, etc.). Just let me know if I can use FPrintln instead of FPrint. Thanks!

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.

3 participants