-
Notifications
You must be signed in to change notification settings - Fork 24
refactor(config): destructify config.GetProjectDirPath(fs, os) #130
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
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #130 +/- ##
==========================================
+ Coverage 63.46% 63.48% +0.01%
==========================================
Files 212 212
Lines 22345 22345
==========================================
+ Hits 14182 14185 +3
+ Misses 7080 7078 -2
+ Partials 1083 1082 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| ReadProjectConfigFile(ctx context.Context) (ProjectConfig, error) | ||
| WriteProjectConfigFile(ctx context.Context, projectConfig ProjectConfig) (string, error) | ||
| ProjectConfigJSONFileExists(projectDirPath string) bool | ||
| GetProjectDirPath() (string, 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.
note: Removing GetProjectDirPath from the interface.
|
|
||
| // GetProjectDirPath returns the path to the project directory or an error if not a Slack project | ||
| // TODO(@mbrooks) Standardize the definition of a validate project directory and merge with `cmdutil.ValidProjectDirectoryOrExit` | ||
| func (c *ProjectConfig) GetProjectDirPath() (string, 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.
note: Changing GetProjectDirPath from a method on the struct to a standalone function. Now the dependencies are injected as arguments instead of accessed as private struct fields.
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.
Huge unlock in making it clear what's needed for these functions to complete! I like that "standard" dependencies are no longer kept within a struct here.
Organizing related functions into packages still seems ideal for separating most concerns. Perhaps a returned configuration object has attached methods? But I'm so open to going back and forth until a good balance is found 🙏 ✨
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.
Good to know that this is resonating with you as well @zimeg! There's definitely some ergonomics that we'll need to figure out, whether returning a struct or better naming conventions. But at least the general direction sounds positive!
| func (m *ProjectConfigMock) GetProjectDirPath() (string, error) { | ||
| args := m.Called() | ||
| return args.String(0), args.Error(1) | ||
| } | ||
|
|
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.
note: Removing the mock. We don't use this mock, but for some future methods, it will require us set the correct data in the memory-based file system and then read it back to confirm it.
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.
@mwbrooks This is an excellent change from testing "mocks" to "fixtures" where we can exercise all of the logic with a certain set of known files! 🧪
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.
Oooo, I like how you phrased that. From testing "mocks" to testing "fixtures." The fixtures feel more real, even though they require more setup and the unit tests run deeper down the stack.
|
|
||
| // MockWorkingDirectory is the default returned by Getwd(). | ||
| const MockWorkingDirectory = "/Users/user.name/app" | ||
| const MockWorkingDirectory = "/Users/user.name/project" |
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.
note: I changed this to /project because it was a little weird to read in test output. We're creating a new project, not a Slack app. So it makes sense to have the directory called project.
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.
🗣️ Past readings have made me a believer in mock data matching what might be expected in production! Nice change once more.
| m.On("UserHomeDir").Return(MockHomeDirectory, nil) | ||
| m.On("GetExecutionDir").Return(MockHomeDirectory, nil) | ||
| m.On("SetExecutionDir", mock.Anything) | ||
| m.On("IsNotExist", nil).Return(false) |
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.
note: In order for tests to pass, we need our mock for os.IsNotExist(err) to return false when err = nil. In most cases, we don't need a mock here and could use the real IsNotExist 🤔
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 most cases, we don't need a mock here and could use the real
IsNotExist
Heh I had this exact same though! Now that we're moving towards fixtures that might be a more stable change to avoid mocking strangeness from odd defaults. No blocker for these changes though! 🚢
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.
Great point that as we use more fixtures instead of mocks, we may not need to mock a IsNotExist scenario. Instead, we can setup the fixtures to satisfy that edge-case.
zimeg
left a comment
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.
@mwbrooks Thanks so much for prototyping these refactors 🎁
At a glance this change makes a lot of sense and I'm glad to find fewer hidden dependencies of this function.
I'm thinking about which methods we do want to keep in a structure, perhaps those with stateful data, but also am open to discussing this here or in a later change 👾
Feel free to merge when the time is right! I'm open to reviewing similar changes of course!
|
|
||
| // GetProjectDirPath returns the path to the project directory or an error if not a Slack project | ||
| // TODO(@mbrooks) Standardize the definition of a validate project directory and merge with `cmdutil.ValidProjectDirectoryOrExit` | ||
| func (c *ProjectConfig) GetProjectDirPath() (string, 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.
Huge unlock in making it clear what's needed for these functions to complete! I like that "standard" dependencies are no longer kept within a struct here.
Organizing related functions into packages still seems ideal for separating most concerns. Perhaps a returned configuration object has attached methods? But I'm so open to going back and forth until a good balance is found 🙏 ✨
| func (m *ProjectConfigMock) GetProjectDirPath() (string, error) { | ||
| args := m.Called() | ||
| return args.String(0), args.Error(1) | ||
| } | ||
|
|
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.
@mwbrooks This is an excellent change from testing "mocks" to "fixtures" where we can exercise all of the logic with a certain set of known files! 🧪
| err = afero.WriteFile(fs, GetProjectHooksJSONFilePath(slackdeps.MockWorkingDirectory), []byte("{}\n"), 0600) | ||
| err = afero.WriteFile(fs, GetProjectHooksJSONFilePath(slackdeps.MockWorkingDirectory), []byte("{}\n"), 0644) |
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.
👁️🗨️ ✨
|
|
||
| // MockWorkingDirectory is the default returned by Getwd(). | ||
| const MockWorkingDirectory = "/Users/user.name/app" | ||
| const MockWorkingDirectory = "/Users/user.name/project" |
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.
🗣️ Past readings have made me a believer in mock data matching what might be expected in production! Nice change once more.
| m.On("UserHomeDir").Return(MockHomeDirectory, nil) | ||
| m.On("GetExecutionDir").Return(MockHomeDirectory, nil) | ||
| m.On("SetExecutionDir", mock.Anything) | ||
| m.On("IsNotExist", nil).Return(false) |
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 most cases, we don't need a mock here and could use the real
IsNotExist
Heh I had this exact same though! Now that we're moving towards fixtures that might be a more stable change to avoid mocking strangeness from odd defaults. No blocker for these changes though! 🚢
|
Thanks for the review @zimeg! It's good to hear that this is a positive step forward in your mind. So, I'll go ahead and merge it then follow-up with a wider refactor for other functions.
We'll be forced to think about this soon, I think. There are a few functions that aren't as straight forward, such as |
Summary
Proposal to refactor the
package configfrom structs to standalone functions.This pull request refactors the function
GetProjectDirPathinpackage configfrom a struct to a standalone function. This is a small example to showcase the changes involve in the factor and explain the benefits. If we like this direction, then I'll follow-up with large pull requests that make the changes for all ofinternal/config/project.go.Before:
After:
Benefits:
fs) from behaviour (the function)clientsstruct overallDrawbacks:
fs.MemMapFs) instead of the methods (projectConfigMock.On("GetManifestSource", mock.Anything,)...)package configmethods, such asCache()andSetSurveyConfigRequirements