chore: add tests for admin auth subcommands (#8433)

This PR is adds almost all tests in `cmd/admin_auth_oauth_test.go` with a bit of refactoring beforehand to make the tests easier to write. These should be legitimate refactors where the implementation changes but the public API/behavior does not change. All of the changes in this PR are done to align with how tests are written in `cmd/admin_auth_ldap_test.go`. Since `cmd/admin_auth_ldap.go` is a sibling file to `cmd/admin_auth_oauth.go`, it seems like their test files should also be aligned.

There are some tests added that show the current behavior as not ideal. E.g. not being able to update certain fields, or being able to set fields that are ultimately ignored. These are added so that the behavior is at least shown a bit more visibly. There should likely be a follow-up to fix some of these issues. But that will almost certainly be a breaking change that I'd rather avoid in this PR.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8433
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: joneshf <jones3.hardy@gmail.com>
Co-committed-by: joneshf <jones3.hardy@gmail.com>
This commit is contained in:
joneshf 2025-07-09 20:55:10 +02:00 committed by Gusted
parent 32e8610b20
commit 48035bbd4e
4 changed files with 732 additions and 28 deletions

View file

@ -17,6 +17,15 @@ import (
"github.com/urfave/cli/v3"
)
type (
authService struct {
initDB func(ctx context.Context) error
createAuthSource func(context.Context, *auth_model.Source) error
updateAuthSource func(context.Context, *auth_model.Source) error
getAuthSourceByID func(ctx context.Context, id int64) (*auth_model.Source, error)
}
)
func microcmdAuthDelete() *cli.Command {
return &cli.Command{
Name: "delete",
@ -60,6 +69,16 @@ func microcmdAuthList() *cli.Command {
}
}
// newAuthService creates a service with default functions.
func newAuthService() *authService {
return &authService{
initDB: initDB,
createAuthSource: auth_model.CreateSource,
updateAuthSource: auth_model.UpdateSource,
getAuthSourceByID: auth_model.GetSourceByID,
}
}
func runListAuth(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()

View file

@ -14,15 +14,6 @@ import (
"github.com/urfave/cli/v3"
)
type (
authService struct {
initDB func(ctx context.Context) error
createAuthSource func(context.Context, *auth.Source) error
updateAuthSource func(context.Context, *auth.Source) error
getAuthSourceByID func(ctx context.Context, id int64) (*auth.Source, error)
}
)
func commonLdapCLIFlags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
@ -184,16 +175,6 @@ func microcmdAuthUpdateLdapSimpleAuth() *cli.Command {
}
}
// newAuthService creates a service with default functions.
func newAuthService() *authService {
return &authService{
initDB: initDB,
createAuthSource: auth.CreateSource,
updateAuthSource: auth.UpdateSource,
getAuthSourceByID: auth.GetSourceByID,
}
}
// parseAuthSource assigns values on authSource according to command line flags.
func parseAuthSource(c *cli.Command, authSource *auth.Source) {
if c.IsSet("name") {

View file

@ -132,7 +132,7 @@ func microcmdAuthAddOauth() *cli.Command {
return &cli.Command{
Name: "add-oauth",
Usage: "Add new Oauth authentication source",
Action: runAddOauth,
Action: newAuthService().addOauth,
Flags: oauthCLIFlags(),
}
}
@ -141,7 +141,7 @@ func microcmdAuthUpdateOauth() *cli.Command {
return &cli.Command{
Name: "update-oauth",
Usage: "Update existing Oauth authentication source",
Action: runUpdateOauth,
Action: newAuthService().updateOauth,
Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{idFlag()}, oauthCLIFlags()[1:]...)...),
}
}
@ -179,11 +179,11 @@ func parseOAuth2Config(_ context.Context, c *cli.Command) *oauth2.Source {
}
}
func runAddOauth(ctx context.Context, c *cli.Command) error {
func (a *authService) addOauth(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
if err := a.initDB(ctx); err != nil {
return err
}
@ -195,7 +195,7 @@ func runAddOauth(ctx context.Context, c *cli.Command) error {
}
}
return auth_model.CreateSource(ctx, &auth_model.Source{
return a.createAuthSource(ctx, &auth_model.Source{
Type: auth_model.OAuth2,
Name: c.String("name"),
IsActive: true,
@ -203,7 +203,7 @@ func runAddOauth(ctx context.Context, c *cli.Command) error {
})
}
func runUpdateOauth(ctx context.Context, c *cli.Command) error {
func (a *authService) updateOauth(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
@ -211,11 +211,11 @@ func runUpdateOauth(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
if err := a.initDB(ctx); err != nil {
return err
}
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
@ -310,5 +310,5 @@ func runUpdateOauth(ctx context.Context, c *cli.Command) error {
oAuth2Config.CustomURLMapping = customURLMapping
source.Cfg = oAuth2Config
return auth_model.UpdateSource(ctx, source)
return a.updateAuthSource(ctx, source)
}

View file

@ -0,0 +1,704 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package cmd
import (
"context"
"testing"
"forgejo.org/models/auth"
"forgejo.org/modules/test"
"forgejo.org/services/auth/source/oauth2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v3"
)
func TestAddOauth(t *testing.T) {
// Mock cli functions to do not exit on error
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
args []string
source *auth.Source
errMsg string
}{
// case 0
{
args: []string{
"oauth-test",
"--name", "oauth2 (via openidConnect) source full",
"--provider", "openidConnect",
"--key", "client id",
"--secret", "client secret",
"--auto-discover-url", "https://example.com/.well-known/openid-configuration",
"--use-custom-urls", "",
"--custom-tenant-id", "tenant id",
"--custom-auth-url", "https://example.com/auth",
"--custom-token-url", "https://example.com/token",
"--custom-profile-url", "https://example.com/profile",
"--custom-email-url", "https://example.com/email",
"--icon-url", "https://example.com/icon.svg",
"--skip-local-2fa",
"--scopes", "address",
"--scopes", "email",
"--scopes", "phone",
"--scopes", "profile",
"--attribute-ssh-public-key", "ssh_public_key",
"--required-claim-name", "can_access",
"--required-claim-value", "yes",
"--group-claim-name", "groups",
"--admin-group", "admin",
"--restricted-group", "restricted",
"--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,
"--group-team-map-removal",
},
source: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source full",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "openidConnect",
ClientID: "client id",
ClientSecret: "client secret",
OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",
CustomURLMapping: &oauth2.CustomURLMapping{
AuthURL: "https://example.com/auth",
TokenURL: "https://example.com/token",
ProfileURL: "https://example.com/profile",
EmailURL: "https://example.com/email",
Tenant: "tenant id",
},
IconURL: "https://example.com/icon.svg",
Scopes: []string{"address", "email", "phone", "profile"},
AttributeSSHPublicKey: "ssh_public_key",
RequiredClaimName: "can_access",
RequiredClaimValue: "yes",
GroupClaimName: "groups",
AdminGroup: "admin",
GroupTeamMap: `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,
GroupTeamMapRemoval: true,
RestrictedGroup: "restricted",
SkipLocalTwoFA: true,
},
},
},
// case 1
{
args: []string{
"oauth-test",
"--name", "oauth2 (via openidConnect) source min",
"--provider", "openidConnect",
"--auto-discover-url", "https://example.com/.well-known/openid-configuration",
},
source: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source min",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "openidConnect",
OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",
Scopes: []string{},
},
},
},
// case 2
{
args: []string{
"oauth-test",
"--name", "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags",
"--custom-tenant-id", "tenant id",
"--custom-auth-url", "https://example.com/auth",
"--custom-token-url", "https://example.com/token",
"--custom-profile-url", "https://example.com/profile",
"--custom-email-url", "https://example.com/email",
},
source: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags",
IsActive: true,
Cfg: &oauth2.Source{
Scopes: []string{},
},
},
},
// case 3
{
args: []string{
"oauth-test",
"--name", "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses",
"--provider", "openidConnect",
"--auto-discover-url", "https://example.com/.well-known/openid-configuration",
"--scopes", "address",
"--scopes", "email",
"--scopes", "phone",
"--scopes", "profile",
},
source: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "openidConnect",
OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",
Scopes: []string{"address", "email", "phone", "profile"},
},
},
},
// case 4
{
args: []string{
"oauth-test",
"--name", "oauth2 (via openidConnect) source `--scopes` supports commas as separators",
"--provider", "openidConnect",
"--auto-discover-url", "https://example.com/.well-known/openid-configuration",
"--scopes", "address,email,phone,profile",
},
source: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source `--scopes` supports commas as separators",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "openidConnect",
OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",
Scopes: []string{"address", "email", "phone", "profile"},
},
},
},
// case 5
{
args: []string{
"oauth-test",
"--name", "oauth2 (via openidConnect) source",
"--provider", "openidConnect",
},
errMsg: "invalid Auto Discovery URL: (this must be a valid URL starting with http:// or https://)",
},
// case 6
{
args: []string{
"oauth-test",
"--name", "oauth2 (via openidConnect) source",
"--provider", "openidConnect",
"--auto-discover-url", "example.com",
},
errMsg: "invalid Auto Discovery URL: example.com (this must be a valid URL starting with http:// or https://)",
},
}
for n, c := range cases {
// Mock functions.
var createdAuthSource *auth.Source
service := &authService{
initDB: func(context.Context) error {
return nil
},
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
createdAuthSource = authSource
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
assert.FailNow(t, "should not call updateAuthSource", "case: %d", n)
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
assert.FailNow(t, "should not call getAuthSourceByID", "case: %d", n)
return nil, nil
},
}
// Create a copy of command to test
app := cli.Command{}
app.Flags = microcmdAuthAddOauth().Flags
app.Action = service.addOauth
// Run it
err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
require.NoError(t, err, "case %d: should have no errors", n)
assert.Equal(t, c.source, createdAuthSource, "case %d: wrong authSource", n)
}
}
}
func TestUpdateOauth(t *testing.T) {
// Mock cli functions to do not exit on error
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
args []string
id int64
existingAuthSource *auth.Source
authSource *auth.Source
errMsg string
}{
// case 0
{
args: []string{
"oauth-test",
"--id", "23",
"--name", "oauth2 (via openidConnect) source full",
"--provider", "openidConnect",
"--key", "client id",
"--secret", "client secret",
"--auto-discover-url", "https://example.com/.well-known/openid-configuration",
"--use-custom-urls", "",
"--custom-tenant-id", "tenant id",
"--custom-auth-url", "https://example.com/auth",
"--custom-token-url", "https://example.com/token",
"--custom-profile-url", "https://example.com/profile",
"--custom-email-url", "https://example.com/email",
"--icon-url", "https://example.com/icon.svg",
"--skip-local-2fa",
"--scopes", "address",
"--scopes", "email",
"--scopes", "phone",
"--scopes", "profile",
"--attribute-ssh-public-key", "ssh_public_key",
"--required-claim-name", "can_access",
"--required-claim-value", "yes",
"--group-claim-name", "groups",
"--admin-group", "admin",
"--restricted-group", "restricted",
"--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,
"--group-team-map-removal",
},
id: 23,
existingAuthSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{},
},
authSource: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source full",
Cfg: &oauth2.Source{
Provider: "openidConnect",
ClientID: "client id",
ClientSecret: "client secret",
OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",
CustomURLMapping: &oauth2.CustomURLMapping{
AuthURL: "https://example.com/auth",
TokenURL: "https://example.com/token",
ProfileURL: "https://example.com/profile",
EmailURL: "https://example.com/email",
Tenant: "tenant id",
},
IconURL: "https://example.com/icon.svg",
Scopes: []string{"address", "email", "phone", "profile"},
AttributeSSHPublicKey: "ssh_public_key",
RequiredClaimName: "can_access",
RequiredClaimValue: "yes",
GroupClaimName: "groups",
AdminGroup: "admin",
GroupTeamMap: `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,
GroupTeamMapRemoval: true,
RestrictedGroup: "restricted",
// `--skip-local-2fa` is currently ignored.
// SkipLocalTwoFA: true,
},
},
},
// case 1
{
args: []string{
"oauth-test",
"--id", "1",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
},
},
},
// case 2
{
args: []string{
"oauth-test",
"--id", "1",
"--name", "oauth2 (via openidConnect) source full",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source full",
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
},
},
},
// case 3
{
args: []string{
"oauth-test",
"--id", "1",
"--provider", "openidConnect",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
Provider: "openidConnect",
CustomURLMapping: &oauth2.CustomURLMapping{},
},
},
},
// case 4
{
args: []string{
"oauth-test",
"--id", "1",
"--key", "client id",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
ClientID: "client id",
CustomURLMapping: &oauth2.CustomURLMapping{},
},
},
},
// case 5
{
args: []string{
"oauth-test",
"--id", "1",
"--secret", "client secret",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
ClientSecret: "client secret",
CustomURLMapping: &oauth2.CustomURLMapping{},
},
},
},
// case 6
{
args: []string{
"oauth-test",
"--id", "1",
"--auto-discover-url", "https://example.com/.well-known/openid-configuration",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration",
CustomURLMapping: &oauth2.CustomURLMapping{},
},
},
},
// case 7
{
args: []string{
"oauth-test",
"--id", "1",
"--use-custom-urls", "",
"--custom-tenant-id", "tenant id",
"--custom-auth-url", "https://example.com/auth",
"--custom-token-url", "https://example.com/token",
"--custom-profile-url", "https://example.com/profile",
"--custom-email-url", "https://example.com/email",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{
AuthURL: "https://example.com/auth",
TokenURL: "https://example.com/token",
ProfileURL: "https://example.com/profile",
EmailURL: "https://example.com/email",
Tenant: "tenant id",
},
},
},
},
// case 8
{
args: []string{
"oauth-test",
"--id", "1",
"--name", "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags",
"--custom-tenant-id", "tenant id",
"--custom-auth-url", "https://example.com/auth",
"--custom-token-url", "https://example.com/token",
"--custom-profile-url", "https://example.com/profile",
"--custom-email-url", "https://example.com/email",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags",
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
},
},
},
// case 9
{
args: []string{
"oauth-test",
"--id", "1",
"--icon-url", "https://example.com/icon.svg",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
IconURL: "https://example.com/icon.svg",
},
},
},
// case 10
{
args: []string{
"oauth-test",
"--id", "1",
"--name", "oauth2 (via openidConnect) source `--skip-local-2fa` is currently ignored",
"--skip-local-2fa",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source `--skip-local-2fa` is currently ignored",
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
// `--skip-local-2fa` is currently ignored.
// SkipLocalTwoFA: true,
},
},
},
// case 11
{
args: []string{
"oauth-test",
"--id", "1",
"--name", "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses",
"--scopes", "address",
"--scopes", "email",
"--scopes", "phone",
"--scopes", "profile",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses",
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
Scopes: []string{"address", "email", "phone", "profile"},
},
},
},
// case 12
{
args: []string{
"oauth-test",
"--id", "1",
"--name", "oauth2 (via openidConnect) source `--scopes` supports commas as separators",
"--scopes", "address,email,phone,profile",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Name: "oauth2 (via openidConnect) source `--scopes` supports commas as separators",
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
Scopes: []string{"address", "email", "phone", "profile"},
},
},
},
// case 13
{
args: []string{
"oauth-test",
"--id", "1",
"--attribute-ssh-public-key", "ssh_public_key",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
AttributeSSHPublicKey: "ssh_public_key",
},
},
},
// case 14
{
args: []string{
"oauth-test",
"--id", "1",
"--required-claim-name", "can_access",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
RequiredClaimName: "can_access",
},
},
},
// case 15
{
args: []string{
"oauth-test",
"--id", "1",
"--required-claim-value", "yes",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
RequiredClaimValue: "yes",
},
},
},
// case 16
{
args: []string{
"oauth-test",
"--id", "1",
"--group-claim-name", "groups",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
GroupClaimName: "groups",
},
},
},
// case 17
{
args: []string{
"oauth-test",
"--id", "1",
"--admin-group", "admin",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
AdminGroup: "admin",
},
},
},
// case 18
{
args: []string{
"oauth-test",
"--id", "1",
"--restricted-group", "restricted",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
RestrictedGroup: "restricted",
},
},
},
// case 19
{
args: []string{
"oauth-test",
"--id", "1",
"--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
GroupTeamMap: `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`,
},
},
},
// case 20
{
args: []string{
"oauth-test",
"--id", "1",
"--group-team-map-removal",
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
GroupTeamMapRemoval: true,
},
},
},
// case 21
{
args: []string{
"oauth-test",
"--id", "23",
"--group-team-map-removal=false",
},
id: 23,
existingAuthSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
GroupTeamMapRemoval: true,
},
},
authSource: &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
GroupTeamMapRemoval: false,
},
},
},
// case 22
{
args: []string{
"oauth-test",
},
errMsg: "--id flag is missing",
},
}
for n, c := range cases {
// Mock functions.
var updatedAuthSource *auth.Source
service := &authService{
initDB: func(context.Context) error {
return nil
},
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
assert.FailNow(t, "should not call createAuthSource", "case: %d", n)
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
updatedAuthSource = authSource
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
if c.id != 0 {
assert.Equal(t, c.id, id, "case %d: wrong id", n)
}
if c.existingAuthSource != nil {
return c.existingAuthSource, nil
}
return &auth.Source{
Type: auth.OAuth2,
Cfg: &oauth2.Source{},
}, nil
},
}
// Create a copy of command to test
app := cli.Command{}
app.Flags = microcmdAuthUpdateOauth().Flags
app.Action = service.updateOauth
// Run it
err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
require.NoError(t, err, "case %d: should have no errors", n)
assert.Equal(t, c.authSource, updatedAuthSource, "case %d: wrong authSource", n)
}
}
}