-
Notifications
You must be signed in to change notification settings - Fork 55
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
Extract config logic into own package #743
Conversation
de49adc
to
18973ca
Compare
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #743 +/- ##
==========================================
+ Coverage 71.17% 71.63% +0.45%
==========================================
Files 87 89 +2
Lines 11162 11159 -3
==========================================
+ Hits 7945 7994 +49
+ Misses 2700 2661 -39
+ Partials 517 504 -13
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report in Codecov by Sentry. |
2be0048
to
8efc96d
Compare
8efc96d
to
86f24cd
Compare
86f24cd
to
8423370
Compare
} | ||
|
||
// RegenerateAccessToken regenerates the access token for the tenant. | ||
func (t *Tenant) RegenerateAccessToken(ctx context.Context) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only func that doesn't have tests, unfortunately it's extremely hard to test this through unit tests due to lack of sensible abstractions.
Ideally we would refactor the auth and keyring packages next and the entire logic below we could replace with a strategy pattern delegating the actual logic to the auth pkg through an interface that we could mock.
For now I kept this as it was.
} | ||
|
||
if err := checkInstallID(cli); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is now part of the AddTenant
func on the Config
.
var ErrNoAuthenticatedTenants = errors.New("Not logged in. Try `auth0 login`.") | ||
|
||
// Config holds cli configuration settings. | ||
type Config struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we expose an interface with Config
's methods, and then use the interface in the cli
struct, so that the config can be mocked? It would be useful for testing functions that use config methods like GetTenant()
, such as this one: https://github.com/auth0/auth0-cli/blob/main/internal/cli/apps.go#L882
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO it's unnecessary as right now we can simply inject an in-memory Config struct with any values we might need for testing.
So the setup to test for example the appPickerOptions
func would be similar to:
auth0-cli/internal/config/config_test.go
Lines 206 to 234 in de87857
tempFile := createTempConfigFile(t, []byte(` | |
{ | |
"install_id": "3998b053-dd7f-4bfe-bb10-c4f3a96a0180", | |
"default_tenant": "auth0-cli.eu.auth0.com", | |
"tenants": { | |
"auth0-cli.eu.auth0.com": { | |
"name": "auth0-cli", | |
"domain": "auth0-cli.eu.auth0.com", | |
"access_token": "eyfSaswe", | |
"expires_at": "2023-04-18T11:18:07.998809Z", | |
"client_id": "secret" | |
} | |
} | |
} | |
`)) | |
expectedTenant := Tenant{ | |
Name: "auth0-cli", | |
Domain: "auth0-cli.eu.auth0.com", | |
AccessToken: "eyfSaswe", | |
ExpiresAt: time.Date(2023, time.April, 18, 11, 18, 7, 998809000, time.UTC), | |
ClientID: "secret", | |
} | |
config := &Config{Path: tempFile} | |
actualTenant, err := config.GetTenant("auth0-cli.eu.auth0.com") | |
assert.NoError(t, err) | |
assert.Equal(t, expectedTenant, actualTenant) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That involves creating an actual temp config file. But it certainly does the trick.
internal/cli/apps.go
Outdated
@@ -479,7 +479,7 @@ func createAppCmd(cli *cli) *cobra.Command { | |||
return fmt.Errorf("Unable to create application: %v", err) | |||
} | |||
|
|||
if err := cli.setDefaultAppID(a.GetClientID()); err != nil { | |||
if err := cli.Config.SaveNewDefaultAppIDForTenant(cli.tenant, a.GetClientID()); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe SetDefaultAppForTenant()
? Seems a bit verbose. Totally a nit, though. Not a blocker.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I like that to be honest, I'll do the same with SetDefaultTenant. 👍🏻 Thanks @Widcket
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
954f34b
(#743) I also realized we can set the path to private instead of having it public.
"auth0-mega-cli.eu.auth0.com": { | ||
"name": "auth0-mega-cli", | ||
"domain": "auth0-mega-cli.eu.auth0.com", | ||
"access_token": "eyfSaswe", | ||
"expires_at": "2023-04-18T11:18:07.998809Z", | ||
"client_id": "secret" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to add a third tenant here to make it clear what happens if there are multiple "fallbacks".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Go, maps are unordered collections of key/value pairs. When you range over a map, the order in which the key/value pairs are returned is not guaranteed and can vary between different runs of the program. We can add logic to have a consistent sorting and always set the default tenant to a more predictable one but I'd avoid it IMO as it feels unnecessary. Wdyt?
Reference: https://go.dev/blog/maps Iteration order
} | ||
|
||
func TestConfig_SaveNewDefaultTenant(t *testing.T) { | ||
t.Run("it can successfully save a new tenant default", func(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder: Test case for validating that default tenant actually exists in config.
}) | ||
} | ||
|
||
func TestConfig_IsLoggedInWithTenant(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder: Needs test case for access token in keyring.
internal/config/config_test.go
Outdated
}`)) | ||
|
||
config := &Config{path: tempFile} | ||
assert.False(t, config.IsLoggedInWithTenant("")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix:
assert.False(t, config.IsLoggedInWithTenant("")) | |
assert.False(t, config.IsLoggedInWithTenant("auth0-cli.eu.auth0.com")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solid improvement 👍
🔧 Changes
The main focus of this PR is to extract and contain the entire config logic into one package that is fully testable through unit tests, as right now the config logic is spread through multiple places.
Along the way several minor bugs were also fixed:
It is advised to review this PR in 3 steps (commit by commit):
📚 References
🔬 Testing
📝 Checklist