-
-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Migrate to urfave v3 #34510
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+1,718
−783
Merged
Migrate to urfave v3 #34510
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
d281b11
migrate to urfave v3
TheFox0x7 df9c7f2
inline flags for ldap commands
TheFox0x7 d29b785
align tests
TheFox0x7 ffd04d0
add tests for smtp command
TheFox0x7 553b43a
add oauth coverage
TheFox0x7 69cde4d
fix formatting
TheFox0x7 4ae57e8
install signals in main
TheFox0x7 eba1408
add coverage for cert subcommand
TheFox0x7 173ad6c
add coverage for admin user delete command
TheFox0x7 13b9d6e
add coverage for change password
TheFox0x7 02497c0
add coverage for must change password
TheFox0x7 8e2333f
drop argsSet in favor of required
TheFox0x7 97589cf
Merge branch 'main' into migrate-to-urfave-v3
TheFox0x7 23dd90f
fix
wxiaoguang 70deea2
Merge branch 'main' into migrate-to-urfave-v3
wxiaoguang dfd47ce
fix
wxiaoguang cda23a7
fix
wxiaoguang c47d666
fix
wxiaoguang 9441550
fix name, fix lint
wxiaoguang b2d5244
fix
wxiaoguang ba990d0
revert ldap flag rework
TheFox0x7 40fb2ae
correct docs root
TheFox0x7 a8701d4
wrap errors in cert
TheFox0x7 8461c8e
make errors conformant to golangs standard
TheFox0x7 aa6277f
account for custom path in cert generation
TheFox0x7 d4a27cb
same but for errors
TheFox0x7 38933ea
don't call newAuthService in init stage
wxiaoguang 3fcc07a
Merge pull request #2 from wxiaoguang/migrate-to-urfave-v3
TheFox0x7 43443c1
Merge branch 'main' into migrate-to-urfave-v3
wxiaoguang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,333 @@ | ||
// Copyright 2025 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package cmd | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
auth_model "code.gitea.io/gitea/models/auth" | ||
"code.gitea.io/gitea/services/auth/source/oauth2" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/urfave/cli/v3" | ||
) | ||
|
||
func TestAddOauth(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
args []string | ||
source *auth_model.Source | ||
errMsg string | ||
}{ | ||
{ | ||
name: "valid config", | ||
args: []string{ | ||
"--name", "test", | ||
"--provider", "github", | ||
"--key", "some_key", | ||
"--secret", "some_secret", | ||
}, | ||
source: &auth_model.Source{ | ||
Type: auth_model.OAuth2, | ||
Name: "test", | ||
IsActive: true, | ||
Cfg: &oauth2.Source{ | ||
Scopes: []string{}, | ||
Provider: "github", | ||
ClientID: "some_key", | ||
ClientSecret: "some_secret", | ||
}, | ||
TwoFactorPolicy: "", | ||
}, | ||
}, | ||
{ | ||
name: "valid config with openid connect", | ||
args: []string{ | ||
"--name", "test", | ||
"--provider", "openidConnect", | ||
"--key", "some_key", | ||
"--secret", "some_secret", | ||
"--auto-discover-url", "https://example.com", | ||
}, | ||
source: &auth_model.Source{ | ||
Type: auth_model.OAuth2, | ||
Name: "test", | ||
IsActive: true, | ||
Cfg: &oauth2.Source{ | ||
Scopes: []string{}, | ||
Provider: "openidConnect", | ||
ClientID: "some_key", | ||
ClientSecret: "some_secret", | ||
OpenIDConnectAutoDiscoveryURL: "https://example.com", | ||
}, | ||
TwoFactorPolicy: "", | ||
}, | ||
}, | ||
{ | ||
name: "valid config with options", | ||
args: []string{ | ||
"--name", "test", | ||
"--provider", "gitlab", | ||
"--key", "some_key", | ||
"--secret", "some_secret", | ||
"--use-custom-urls", "true", | ||
"--custom-token-url", "https://example.com/token", | ||
"--custom-auth-url", "https://example.com/auth", | ||
"--custom-profile-url", "https://example.com/profile", | ||
"--custom-email-url", "https://example.com/email", | ||
"--custom-tenant-id", "some_tenant", | ||
"--icon-url", "https://example.com/icon", | ||
"--scopes", "scope1,scope2", | ||
"--skip-local-2fa", "true", | ||
"--required-claim-name", "claim_name", | ||
"--required-claim-value", "claim_value", | ||
"--group-claim-name", "group_name", | ||
"--admin-group", "admin", | ||
"--restricted-group", "restricted", | ||
"--group-team-map", `{"group1": [1,2]}`, | ||
"--group-team-map-removal=true", | ||
}, | ||
source: &auth_model.Source{ | ||
Type: auth_model.OAuth2, | ||
Name: "test", | ||
IsActive: true, | ||
Cfg: &oauth2.Source{ | ||
Provider: "gitlab", | ||
ClientID: "some_key", | ||
ClientSecret: "some_secret", | ||
CustomURLMapping: &oauth2.CustomURLMapping{ | ||
TokenURL: "https://example.com/token", | ||
AuthURL: "https://example.com/auth", | ||
ProfileURL: "https://example.com/profile", | ||
EmailURL: "https://example.com/email", | ||
Tenant: "some_tenant", | ||
}, | ||
IconURL: "https://example.com/icon", | ||
Scopes: []string{"scope1", "scope2"}, | ||
RequiredClaimName: "claim_name", | ||
RequiredClaimValue: "claim_value", | ||
GroupClaimName: "group_name", | ||
AdminGroup: "admin", | ||
RestrictedGroup: "restricted", | ||
GroupTeamMap: `{"group1": [1,2]}`, | ||
GroupTeamMapRemoval: true, | ||
}, | ||
TwoFactorPolicy: "skip", | ||
}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
var createdSource *auth_model.Source | ||
a := &authService{ | ||
initDB: func(ctx context.Context) error { | ||
return nil | ||
}, | ||
createAuthSource: func(ctx context.Context, source *auth_model.Source) error { | ||
createdSource = source | ||
return nil | ||
}, | ||
} | ||
|
||
app := &cli.Command{ | ||
Flags: microcmdAuthAddOauth().Flags, | ||
Action: a.runAddOauth, | ||
} | ||
|
||
args := []string{"oauth-test"} | ||
args = append(args, tc.args...) | ||
|
||
err := app.Run(t.Context(), args) | ||
|
||
if tc.errMsg != "" { | ||
assert.EqualError(t, err, tc.errMsg) | ||
} else { | ||
assert.NoError(t, err) | ||
assert.Equal(t, tc.source, createdSource) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestUpdateOauth(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
args []string | ||
id int64 | ||
existingAuthSource *auth_model.Source | ||
authSource *auth_model.Source | ||
errMsg string | ||
}{ | ||
{ | ||
name: "missing id", | ||
args: []string{ | ||
"--name", "test", | ||
}, | ||
errMsg: "--id flag is missing", | ||
}, | ||
{ | ||
name: "valid config", | ||
id: 1, | ||
existingAuthSource: &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.OAuth2, | ||
Name: "old name", | ||
IsActive: true, | ||
Cfg: &oauth2.Source{ | ||
Provider: "github", | ||
ClientID: "old_key", | ||
ClientSecret: "old_secret", | ||
}, | ||
TwoFactorPolicy: "", | ||
}, | ||
args: []string{ | ||
"--id", "1", | ||
"--name", "test", | ||
"--provider", "gitlab", | ||
"--key", "new_key", | ||
"--secret", "new_secret", | ||
}, | ||
authSource: &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.OAuth2, | ||
Name: "test", | ||
IsActive: true, | ||
Cfg: &oauth2.Source{ | ||
Provider: "gitlab", | ||
ClientID: "new_key", | ||
ClientSecret: "new_secret", | ||
CustomURLMapping: &oauth2.CustomURLMapping{}, | ||
}, | ||
TwoFactorPolicy: "", | ||
}, | ||
}, | ||
{ | ||
name: "valid config with options", | ||
id: 1, | ||
existingAuthSource: &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.OAuth2, | ||
Name: "old name", | ||
IsActive: true, | ||
Cfg: &oauth2.Source{ | ||
Provider: "gitlab", | ||
ClientID: "old_key", | ||
ClientSecret: "old_secret", | ||
CustomURLMapping: &oauth2.CustomURLMapping{ | ||
TokenURL: "https://old.example.com/token", | ||
AuthURL: "https://old.example.com/auth", | ||
ProfileURL: "https://old.example.com/profile", | ||
EmailURL: "https://old.example.com/email", | ||
Tenant: "old_tenant", | ||
}, | ||
IconURL: "https://old.example.com/icon", | ||
Scopes: []string{"old_scope1", "old_scope2"}, | ||
RequiredClaimName: "old_claim_name", | ||
RequiredClaimValue: "old_claim_value", | ||
GroupClaimName: "old_group_name", | ||
AdminGroup: "old_admin", | ||
RestrictedGroup: "old_restricted", | ||
GroupTeamMap: `{"old_group1": [1,2]}`, | ||
GroupTeamMapRemoval: true, | ||
}, | ||
TwoFactorPolicy: "", | ||
}, | ||
args: []string{ | ||
"--id", "1", | ||
"--name", "test", | ||
"--provider", "github", | ||
"--key", "new_key", | ||
"--secret", "new_secret", | ||
"--use-custom-urls", "true", | ||
"--custom-token-url", "https://example.com/token", | ||
"--custom-auth-url", "https://example.com/auth", | ||
"--custom-profile-url", "https://example.com/profile", | ||
"--custom-email-url", "https://example.com/email", | ||
"--custom-tenant-id", "new_tenant", | ||
"--icon-url", "https://example.com/icon", | ||
"--scopes", "scope1,scope2", | ||
"--skip-local-2fa=true", | ||
"--required-claim-name", "claim_name", | ||
"--required-claim-value", "claim_value", | ||
"--group-claim-name", "group_name", | ||
"--admin-group", "admin", | ||
"--restricted-group", "restricted", | ||
"--group-team-map", `{"group1": [1,2]}`, | ||
"--group-team-map-removal=false", | ||
}, | ||
authSource: &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.OAuth2, | ||
Name: "test", | ||
IsActive: true, | ||
Cfg: &oauth2.Source{ | ||
Provider: "github", | ||
ClientID: "new_key", | ||
ClientSecret: "new_secret", | ||
CustomURLMapping: &oauth2.CustomURLMapping{ | ||
TokenURL: "https://example.com/token", | ||
AuthURL: "https://example.com/auth", | ||
ProfileURL: "https://example.com/profile", | ||
EmailURL: "https://example.com/email", | ||
Tenant: "new_tenant", | ||
}, | ||
IconURL: "https://example.com/icon", | ||
Scopes: []string{"scope1", "scope2"}, | ||
RequiredClaimName: "claim_name", | ||
RequiredClaimValue: "claim_value", | ||
GroupClaimName: "group_name", | ||
AdminGroup: "admin", | ||
RestrictedGroup: "restricted", | ||
GroupTeamMap: `{"group1": [1,2]}`, | ||
GroupTeamMapRemoval: false, | ||
}, | ||
TwoFactorPolicy: "skip", | ||
}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
a := &authService{ | ||
initDB: func(ctx context.Context) error { | ||
return nil | ||
}, | ||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) { | ||
return &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.OAuth2, | ||
Name: "test", | ||
IsActive: true, | ||
Cfg: &oauth2.Source{ | ||
CustomURLMapping: &oauth2.CustomURLMapping{}, | ||
}, | ||
TwoFactorPolicy: "skip", | ||
}, nil | ||
}, | ||
updateAuthSource: func(ctx context.Context, source *auth_model.Source) error { | ||
assert.Equal(t, tc.authSource, source) | ||
return nil | ||
}, | ||
} | ||
|
||
app := &cli.Command{ | ||
Flags: microcmdAuthUpdateOauth().Flags, | ||
Action: a.runUpdateOauth, | ||
} | ||
|
||
args := []string{"oauth-test"} | ||
args = append(args, tc.args...) | ||
|
||
err := app.Run(t.Context(), args) | ||
|
||
if tc.errMsg != "" { | ||
assert.EqualError(t, err, tc.errMsg) | ||
} else { | ||
assert.NoError(t, err) | ||
} | ||
}) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,285 @@ | ||
// Copyright 2025 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package cmd | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
auth_model "code.gitea.io/gitea/models/auth" | ||
"code.gitea.io/gitea/services/auth/source/smtp" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/urfave/cli/v3" | ||
) | ||
|
||
func TestAddSMTP(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
args []string | ||
source *auth_model.Source | ||
errMsg string | ||
}{ | ||
{ | ||
name: "missing name", | ||
args: []string{ | ||
"--host", "localhost", | ||
"--port", "25", | ||
}, | ||
errMsg: "name must be set", | ||
}, | ||
{ | ||
name: "missing host", | ||
args: []string{ | ||
"--name", "test", | ||
"--port", "25", | ||
}, | ||
errMsg: "host must be set", | ||
}, | ||
{ | ||
name: "missing port", | ||
args: []string{ | ||
"--name", "test", | ||
"--host", "localhost", | ||
}, | ||
errMsg: "port must be set", | ||
}, | ||
{ | ||
name: "valid config", | ||
args: []string{ | ||
"--name", "test", | ||
"--host", "localhost", | ||
"--port", "25", | ||
}, | ||
source: &auth_model.Source{ | ||
Type: auth_model.SMTP, | ||
Name: "test", | ||
IsActive: true, | ||
Cfg: &smtp.Source{ | ||
Auth: "PLAIN", | ||
Host: "localhost", | ||
Port: 25, | ||
// ForceSMTPS: true, | ||
// SkipVerify: true, | ||
}, | ||
TwoFactorPolicy: "skip", | ||
}, | ||
}, | ||
{ | ||
name: "valid config with options", | ||
args: []string{ | ||
"--name", "test", | ||
"--host", "localhost", | ||
"--port", "25", | ||
"--auth-type", "LOGIN", | ||
"--force-smtps=false", | ||
"--skip-verify=false", | ||
"--helo-hostname", "example.com", | ||
"--disable-helo=false", | ||
"--allowed-domains", "example.com,example.org", | ||
"--skip-local-2fa=false", | ||
"--active=false", | ||
}, | ||
source: &auth_model.Source{ | ||
Type: auth_model.SMTP, | ||
Name: "test", | ||
IsActive: false, | ||
Cfg: &smtp.Source{ | ||
Auth: "LOGIN", | ||
Host: "localhost", | ||
Port: 25, | ||
ForceSMTPS: false, | ||
SkipVerify: false, | ||
HeloHostname: "example.com", | ||
DisableHelo: false, | ||
AllowedDomains: "example.com,example.org", | ||
}, | ||
TwoFactorPolicy: "", | ||
}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
a := &authService{ | ||
initDB: func(ctx context.Context) error { | ||
return nil | ||
}, | ||
createAuthSource: func(ctx context.Context, source *auth_model.Source) error { | ||
assert.Equal(t, tc.source, source) | ||
return nil | ||
}, | ||
} | ||
|
||
cmd := &cli.Command{ | ||
Flags: microcmdAuthAddSMTP().Flags, | ||
Action: a.runAddSMTP, | ||
} | ||
|
||
args := []string{"smtp-test"} | ||
args = append(args, tc.args...) | ||
|
||
t.Log(args) | ||
err := cmd.Run(t.Context(), args) | ||
|
||
if tc.errMsg != "" { | ||
assert.EqualError(t, err, tc.errMsg) | ||
} else { | ||
assert.NoError(t, err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestUpdateSMTP(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
args []string | ||
existingAuthSource *auth_model.Source | ||
authSource *auth_model.Source | ||
errMsg string | ||
}{ | ||
{ | ||
name: "missing id", | ||
args: []string{ | ||
"--name", "test", | ||
"--host", "localhost", | ||
"--port", "25", | ||
}, | ||
errMsg: "--id flag is missing", | ||
}, | ||
{ | ||
name: "valid config", | ||
existingAuthSource: &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.SMTP, | ||
Name: "old name", | ||
IsActive: true, | ||
Cfg: &smtp.Source{ | ||
Auth: "PLAIN", | ||
Host: "old host", | ||
Port: 26, | ||
ForceSMTPS: true, | ||
SkipVerify: true, | ||
}, | ||
TwoFactorPolicy: "", | ||
}, | ||
args: []string{ | ||
"--id", "1", | ||
"--name", "test", | ||
"--host", "localhost", | ||
"--port", "25", | ||
}, | ||
authSource: &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.SMTP, | ||
Name: "test", | ||
IsActive: true, | ||
Cfg: &smtp.Source{ | ||
Auth: "PLAIN", | ||
Host: "localhost", | ||
Port: 25, | ||
ForceSMTPS: true, | ||
SkipVerify: true, | ||
}, | ||
TwoFactorPolicy: "skip", | ||
}, | ||
}, | ||
{ | ||
name: "valid config with options", | ||
existingAuthSource: &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.SMTP, | ||
Name: "old name", | ||
IsActive: true, | ||
Cfg: &smtp.Source{ | ||
Auth: "PLAIN", | ||
Host: "old host", | ||
Port: 26, | ||
ForceSMTPS: true, | ||
SkipVerify: true, | ||
HeloHostname: "old.example.com", | ||
DisableHelo: false, | ||
AllowedDomains: "old.example.com", | ||
}, | ||
TwoFactorPolicy: "", | ||
}, | ||
args: []string{ | ||
"--id", "1", | ||
"--name", "test", | ||
"--host", "localhost", | ||
"--port", "25", | ||
"--auth-type", "LOGIN", | ||
"--force-smtps=false", | ||
"--skip-verify=false", | ||
"--helo-hostname", "example.com", | ||
"--disable-helo=true", | ||
"--allowed-domains", "example.com,example.org", | ||
"--skip-local-2fa=true", | ||
"--active=false", | ||
}, | ||
authSource: &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.SMTP, | ||
Name: "test", | ||
IsActive: false, | ||
Cfg: &smtp.Source{ | ||
Auth: "LOGIN", | ||
Host: "localhost", | ||
Port: 25, | ||
ForceSMTPS: false, | ||
SkipVerify: false, | ||
HeloHostname: "example.com", | ||
DisableHelo: true, | ||
AllowedDomains: "example.com,example.org", | ||
}, | ||
TwoFactorPolicy: "skip", | ||
}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
a := &authService{ | ||
initDB: func(ctx context.Context) error { | ||
return nil | ||
}, | ||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) { | ||
return &auth_model.Source{ | ||
ID: 1, | ||
Type: auth_model.SMTP, | ||
Name: "test", | ||
IsActive: true, | ||
Cfg: &smtp.Source{ | ||
Auth: "PLAIN", | ||
SkipVerify: true, | ||
ForceSMTPS: true, | ||
}, | ||
TwoFactorPolicy: "skip", | ||
}, nil | ||
}, | ||
|
||
updateAuthSource: func(ctx context.Context, source *auth_model.Source) error { | ||
assert.Equal(t, tc.authSource, source) | ||
return nil | ||
}, | ||
} | ||
|
||
app := &cli.Command{ | ||
Flags: microcmdAuthUpdateSMTP().Flags, | ||
Action: a.runUpdateSMTP, | ||
} | ||
args := []string{"smtp-tests"} | ||
args = append(args, tc.args...) | ||
|
||
err := app.Run(t.Context(), args) | ||
|
||
if tc.errMsg != "" { | ||
assert.EqualError(t, err, tc.errMsg) | ||
} else { | ||
assert.NoError(t, err) | ||
} | ||
}) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// Copyright 2025 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package cmd | ||
|
||
import ( | ||
"testing" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
"code.gitea.io/gitea/models/unittest" | ||
user_model "code.gitea.io/gitea/models/user" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestChangePasswordCommand(t *testing.T) { | ||
ctx := t.Context() | ||
|
||
defer func() { | ||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{})) | ||
}() | ||
|
||
t.Run("change password successfully", func(t *testing.T) { | ||
// defer func() { | ||
// require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{})) | ||
// }() | ||
// Prepare test user | ||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) | ||
err := microcmdUserCreate().Run(ctx, []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"}) | ||
require.NoError(t, err) | ||
|
||
// load test user | ||
userBase := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
|
||
// Change the password | ||
err = microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "newpassword"}) | ||
require.NoError(t, err) | ||
|
||
// Verify the password has been changed | ||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
assert.NotEqual(t, userBase.Passwd, user.Passwd) | ||
assert.NotEqual(t, userBase.Salt, user.Salt) | ||
|
||
// Additional check for must-change-password flag | ||
require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "anotherpassword", "--must-change-password=false"})) | ||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
assert.False(t, user.MustChangePassword) | ||
|
||
require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "yetanotherpassword", "--must-change-password"})) | ||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
assert.True(t, user.MustChangePassword) | ||
}) | ||
|
||
t.Run("failure cases", func(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
args []string | ||
expectedErr string | ||
}{ | ||
{ | ||
name: "user does not exist", | ||
args: []string{"change-password", "--username", "nonexistentuser", "--password", "newpassword"}, | ||
expectedErr: "user does not exist", | ||
}, | ||
{ | ||
name: "missing username", | ||
args: []string{"change-password", "--password", "newpassword"}, | ||
expectedErr: `"username" not set`, | ||
}, | ||
{ | ||
name: "missing password", | ||
args: []string{"change-password", "--username", "testuser"}, | ||
expectedErr: `"password" not set`, | ||
}, | ||
{ | ||
name: "too short password", | ||
args: []string{"change-password", "--username", "testuser", "--password", "1"}, | ||
expectedErr: "password is not long enough", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
err := microcmdUserChangePassword().Run(ctx, tc.args) | ||
require.Error(t, err) | ||
require.Contains(t, err.Error(), tc.expectedErr) | ||
}) | ||
} | ||
}) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// Copyright 2025 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package cmd | ||
|
||
import ( | ||
"strconv" | ||
"strings" | ||
"testing" | ||
|
||
auth_model "code.gitea.io/gitea/models/auth" | ||
"code.gitea.io/gitea/models/db" | ||
"code.gitea.io/gitea/models/unittest" | ||
user_model "code.gitea.io/gitea/models/user" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestAdminUserDelete(t *testing.T) { | ||
ctx := t.Context() | ||
defer func() { | ||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{})) | ||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{})) | ||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{})) | ||
}() | ||
|
||
setupTestUser := func(t *testing.T) { | ||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) | ||
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"}) | ||
require.NoError(t, err) | ||
} | ||
|
||
t.Run("delete user by id", func(t *testing.T) { | ||
setupTestUser(t) | ||
|
||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--id", strconv.FormatInt(u.ID, 10)}) | ||
require.NoError(t, err) | ||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) | ||
}) | ||
t.Run("delete user by username", func(t *testing.T) { | ||
setupTestUser(t) | ||
|
||
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--username", "testuser"}) | ||
require.NoError(t, err) | ||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) | ||
}) | ||
t.Run("delete user by email", func(t *testing.T) { | ||
setupTestUser(t) | ||
|
||
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--email", "testuser@gitea.local"}) | ||
require.NoError(t, err) | ||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) | ||
}) | ||
t.Run("delete user by all 3 attributes", func(t *testing.T) { | ||
setupTestUser(t) | ||
|
||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
err := microcmdUserDelete().Run(ctx, []string{"delete", "--id", strconv.FormatInt(u.ID, 10), "--username", "testuser", "--email", "testuser@gitea.local"}) | ||
require.NoError(t, err) | ||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) | ||
}) | ||
} | ||
|
||
func TestAdminUserDeleteFailure(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
args []string | ||
expectedErr string | ||
}{ | ||
{ | ||
name: "no user to delete", | ||
args: []string{"delete", "--username", "nonexistentuser"}, | ||
expectedErr: "user does not exist", | ||
}, | ||
{ | ||
name: "user exists but provided username does not match", | ||
args: []string{"delete", "--email", "testuser@gitea.local", "--username", "wrongusername"}, | ||
expectedErr: "the user testuser who has email testuser@gitea.local does not match the provided username wrongusername", | ||
}, | ||
{ | ||
name: "user exists but provided id does not match", | ||
args: []string{"delete", "--username", "testuser", "--id", "999"}, | ||
expectedErr: "the user testuser does not match the provided id 999", | ||
}, | ||
{ | ||
name: "no required flags are provided", | ||
args: []string{"delete"}, | ||
expectedErr: "You must provide the id, username or email of a user to delete", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
ctx := t.Context() | ||
if strings.Contains(tc.name, "user exists") { | ||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) | ||
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"}) | ||
require.NoError(t, err) | ||
} | ||
|
||
err := microcmdUserDelete().Run(ctx, tc.args) | ||
require.Error(t, err) | ||
require.Contains(t, err.Error(), tc.expectedErr) | ||
}) | ||
|
||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{})) | ||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{})) | ||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{})) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// Copyright 2025 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package cmd | ||
|
||
import ( | ||
"testing" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
"code.gitea.io/gitea/models/unittest" | ||
user_model "code.gitea.io/gitea/models/user" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestMustChangePassword(t *testing.T) { | ||
defer func() { | ||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{})) | ||
}() | ||
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"}) | ||
require.NoError(t, err) | ||
err = microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuserexclude", "--email", "testuserexclude@gitea.local", "--random-password"}) | ||
require.NoError(t, err) | ||
// Reset password change flag | ||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset"}) | ||
require.NoError(t, err) | ||
|
||
testUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
assert.False(t, testUser.MustChangePassword) | ||
testUserExclude := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) | ||
assert.False(t, testUserExclude.MustChangePassword) | ||
|
||
// Make all users change password | ||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all"}) | ||
require.NoError(t, err) | ||
|
||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
assert.True(t, testUser.MustChangePassword) | ||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) | ||
assert.True(t, testUserExclude.MustChangePassword) | ||
|
||
// Reset password change flag but exclude all tested users | ||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"}) | ||
require.NoError(t, err) | ||
|
||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
assert.True(t, testUser.MustChangePassword) | ||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) | ||
assert.True(t, testUserExclude.MustChangePassword) | ||
|
||
// Reset password change flag by listing multiple users | ||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser", "testuserexclude"}) | ||
require.NoError(t, err) | ||
|
||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
assert.False(t, testUser.MustChangePassword) | ||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) | ||
assert.False(t, testUserExclude.MustChangePassword) | ||
|
||
// Exclude a user from all user | ||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--exclude", "testuserexclude"}) | ||
require.NoError(t, err) | ||
|
||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
assert.True(t, testUser.MustChangePassword) | ||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) | ||
assert.False(t, testUserExclude.MustChangePassword) | ||
|
||
// Unset a flag for single user | ||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser"}) | ||
require.NoError(t, err) | ||
|
||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) | ||
assert.False(t, testUser.MustChangePassword) | ||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) | ||
assert.False(t, testUserExclude.MustChangePassword) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright 2025 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package cmd | ||
|
||
import ( | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestCertCommand(t *testing.T) { | ||
cases := []struct { | ||
name string | ||
args []string | ||
}{ | ||
{ | ||
name: "RSA cert generation", | ||
args: []string{ | ||
"cert-test", | ||
"--host", "localhost", | ||
"--rsa-bits", "2048", | ||
"--duration", "1h", | ||
"--start-date", "Jan 1 00:00:00 2024", | ||
}, | ||
}, | ||
{ | ||
name: "ECDSA cert generation", | ||
args: []string{ | ||
"cert-test", | ||
"--host", "localhost", | ||
"--ecdsa-curve", "P256", | ||
"--duration", "1h", | ||
"--start-date", "Jan 1 00:00:00 2024", | ||
}, | ||
}, | ||
{ | ||
name: "mixed host, certificate authority", | ||
args: []string{ | ||
"cert-test", | ||
"--host", "localhost,127.0.0.1", | ||
"--duration", "1h", | ||
"--start-date", "Jan 1 00:00:00 2024", | ||
}, | ||
}, | ||
} | ||
|
||
for _, c := range cases { | ||
t.Run(c.name, func(t *testing.T) { | ||
app := cmdCert() | ||
tempDir := t.TempDir() | ||
|
||
certFile := filepath.Join(tempDir, "cert.pem") | ||
keyFile := filepath.Join(tempDir, "key.pem") | ||
|
||
err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile)) | ||
require.NoError(t, err) | ||
|
||
assert.FileExists(t, certFile) | ||
assert.FileExists(t, keyFile) | ||
}) | ||
} | ||
} | ||
|
||
func TestCertCommandFailures(t *testing.T) { | ||
cases := []struct { | ||
name string | ||
args []string | ||
errMsg string | ||
}{ | ||
{ | ||
name: "Start Date Parsing failure", | ||
args: []string{ | ||
"cert-test", | ||
"--host", "localhost", | ||
"--start-date", "invalid-date", | ||
}, | ||
errMsg: "parsing time", | ||
}, | ||
{ | ||
name: "Unknown curve", | ||
args: []string{ | ||
"cert-test", | ||
"--host", "localhost", | ||
"--ecdsa-curve", "invalid-curve", | ||
}, | ||
errMsg: "unrecognized elliptic curve", | ||
}, | ||
{ | ||
name: "Key generation failure", | ||
args: []string{ | ||
"cert-test", | ||
"--host", "localhost", | ||
"--rsa-bits", "invalid-bits", | ||
}, | ||
}, | ||
{ | ||
name: "Missing parameters", | ||
args: []string{ | ||
"cert-test", | ||
}, | ||
errMsg: `"host" not set`, | ||
}, | ||
} | ||
for _, c := range cases { | ||
t.Run(c.name, func(t *testing.T) { | ||
app := cmdCert() | ||
tempDir := t.TempDir() | ||
|
||
certFile := filepath.Join(tempDir, "cert.pem") | ||
keyFile := filepath.Join(tempDir, "key.pem") | ||
err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile)) | ||
require.Error(t, err) | ||
if c.errMsg != "" { | ||
assert.ErrorContains(t, err, c.errMsg) | ||
} | ||
assert.NoFileExists(t, certFile) | ||
assert.NoFileExists(t, keyFile) | ||
}) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,11 +4,13 @@ | |
package cmd | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
"github.com/urfave/cli/v2" | ||
cli_docs "github.com/urfave/cli-docs/v3" | ||
"github.com/urfave/cli/v3" | ||
) | ||
|
||
// CmdDocs represents the available docs sub-command. | ||
|
@@ -30,16 +32,16 @@ var CmdDocs = &cli.Command{ | |
}, | ||
} | ||
|
||
func runDocs(ctx *cli.Context) error { | ||
docs, err := ctx.App.ToMarkdown() | ||
if ctx.Bool("man") { | ||
docs, err = ctx.App.ToMan() | ||
func runDocs(_ context.Context, cmd *cli.Command) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For reviewers: Double check if the docs are correct as the generation is weird, but it might not have the same issue as with tea due to custom help. |
||
docs, err := cli_docs.ToMarkdown(cmd.Root()) | ||
if cmd.Bool("man") { | ||
docs, err = cli_docs.ToMan(cmd.Root()) | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if !ctx.Bool("man") { | ||
if !cmd.Bool("man") { | ||
// Clean up markdown. The following bug was fixed in v2, but is present in v1. | ||
// It affects markdown output (even though the issue is referring to man pages) | ||
// https://github.com/urfave/cli/issues/1040 | ||
|
@@ -51,8 +53,8 @@ func runDocs(ctx *cli.Context) error { | |
} | ||
|
||
out := os.Stdout | ||
if ctx.String("output") != "" { | ||
fi, err := os.Create(ctx.String("output")) | ||
if cmd.String("output") != "" { | ||
fi, err := os.Create(cmd.String("output")) | ||
if err != nil { | ||
return err | ||
} | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.