-
Notifications
You must be signed in to change notification settings - Fork 79
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
Add tests for runtime manager #1555
base: main
Are you sure you want to change the base?
Conversation
ec34013
to
99997c4
Compare
@zedGGs To answer your question about mocking, one of the goals of this work is to test out the ManagerImpl functions in |
@sjberman sounds good, |
@sjberman apologies for not being on time with an update, current test cases for thank you in advance ! if you want you can take current changes and I can add separated pr for good test cases, issue happens to be:
with |
b84583c
to
b36adc6
Compare
@zedGGs Thanks for the update. The issue you're seeing is that the nginx client is attempting to reach a real server, and fails. For unit tests we obviously do not want to use a real server. This means we probably need to create interfaces in https://github.com/nginxinc/nginx-plus-go-client, so we can provide a mock client in our own unit tests that won't try to contact a real server. |
@sjberman here's an update, I have added thanks |
4663750
to
0b42d66
Compare
It would be nice if we could also find a way to test the |
@kate-osborn sure, I am going to try add tests for that too |
f7e7508
to
6532721
Compare
6532721
to
06c586f
Compare
This PR is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 14 days. |
This PR is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 14 days. |
This PR was closed because it has been stalled for 14 days with no activity. |
Reopening after discussion with Zedd! |
a172d77
to
8d57c25
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1555 +/- ##
==========================================
+ Coverage 87.04% 87.30% +0.26%
==========================================
Files 89 89
Lines 6096 6103 +7
Branches 50 50
==========================================
+ Hits 5306 5328 +22
+ Misses 737 716 -21
- Partials 53 59 +6 ☔ View full report in Codecov by Sentry. |
type verifyClient struct { | ||
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . verifyClient | ||
|
||
type verifyClient interface { |
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.
type verifyClient interface { | |
type nginxConfigVerifier interface { |
Then we can keep the struct names verifyClient
and avoid adding Impl
to the name, which is technically an anti-pattern.
@@ -134,7 +137,7 @@ func StartManager(cfg config.Config) 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.
Let's create processHandler
here right before it's used.
@@ -17,18 +17,36 @@ import ( | |||
) | |||
|
|||
const ( | |||
pidFile = "/var/run/nginx/nginx.pid" | |||
PidFile = "/var/run/nginx/nginx.pid" |
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.
Let's add comments to all the newly exported variables and types.
|
||
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . ProcessHandler | ||
|
||
type ProcessHandler interface { |
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.
I think we can simplify this interface.
type ProcessHandler interface {
FindMainProcess(ctx context.Context, timeout time.Duration) (int, error)
ReadFile(file string) ([]byte, error)
Kill(pid int) error
}
type ProcessHandlerImpl struct {
readFile ReadFileFunc
checkFile CheckFileFunc
}
func NewProcessHandlerImpl(readFileFunc ReadFileFunc, checkFileFunc CheckFileFunc) *ProcessHandlerImpl {
return &ProcessHandlerImpl{readFile: readFileFunc, checkFile: checkFileFunc}
}
func (p *ProcessHandlerImpl) ReadFile(file string) ([]byte, error) {
return p.readFile(file)
}
func (p *ProcessHandlerImpl) Kill(pid int) error {
return syscall.Kill(pid, syscall.SIGHUP)
}
...
Other changes:
FindMainProcess
will use the readFile and checkFile funcs that are stored on ProcessHandlerImpl.EnsureNginxRunning
method is removed. Instatic/manager.go
replace call toEnsureNginxRunning
withprocessHandler.FindMainProcess
.
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.
I also don't think ProcessHandler needs to be exported since it's not used outside of this file.
Expect(process.KillCallCount()).To(Equal(1)) | ||
Expect(metrics.IncReloadCountCallCount()).To(Equal(1)) | ||
Expect(verifyClient.WaitForCorrectVersionCallCount()).To(Equal(1)) | ||
Expect(metrics.ObserveLastReloadTimeCallCount()).To(Equal(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.
let's also check that the IncReloadErrorsCallCount
is 0.
}) | ||
|
||
When("NGINX configuration reload is not successful", func() { | ||
It("should panic if MetricsCollector not enabled", func() { |
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.
Let's use the following structure when testing for panics:
When("MetricsCollector is nil")
It("panics")
Expect(metrics.ObserveLastReloadTimeCallCount()).To(Equal(1)) | ||
}) | ||
|
||
When("NGINX configuration reload is not successful", func() { |
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.
I think this is a different test case from the panics in the blocks below. We really want to test reload failures, which happens in the following cases:
- failed to find main process
- failed to read file
- failed to send kill signal
- timed out waiting for correct version
I think we want a test for each one of these cases. You can configure the mocks to return errors to trigger these cases.
Expect(manager.UpdateHTTPServers("test", upstreamServers)).To(Succeed()) | ||
}) | ||
|
||
It("returns no upstreams from NGINX Plus API", func() { |
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.
We are missing a test case here. This test covers the case where the upstreams are nil, but we still need to cover the case where the ngxPlusClient returns an error for GetUpstreams
:
upstreams, err := m.ngxPlusClient.GetUpstreams() |
You can do this by configuring the mock to return an error for the method GetUpstreams
It("successfully updates HTTP server upstream", func() { | ||
Expect(manager.UpdateHTTPServers("test", upstreamServers)).To(Succeed()) | ||
}) | ||
|
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.
I think we also need a test for the success case. Let's add upstream servers to the mock and test that they are returned by manager.GetUpstreams
}) | ||
|
||
It("successfully updates HTTP server upstream", func() { | ||
Expect(manager.UpdateHTTPServers("test", upstreamServers)).To(Succeed()) |
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.
Let's also check that we called the mock's UpdateHTTPServers
method here with the input "test"
) | ||
|
||
var childProcPathFmt = "/proc/%[1]v/task/%[1]v/children" | ||
|
||
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . nginxPlusClient |
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.
#1990 was just merged that changes how we specify these generation statements now, take a look.
Proposed changes
Problem: Missing tests on runtime manager
Im adding this init pr with few tests, if this goes well I can add another pr with more tests
gentle ping for @sjberman
Solution: Added tests and tested locally
Testing: running go test locally
optional: additional questions to clarify mocking and provide more details
Is practice for mocking runtime manager, more to use current usage with:
mgr = NewManagerImpl(&ngxclient.NginxClient{}, nil, logr.New(GinkgoLogr.GetSink()))
or I can use already created mock from
runtimefakes
:runtimefakes.FakeManager
as mocked struct, even tho its mocked interface ?or its required to actually mock
MetricsCollector
inruntimefakes
by creatingruntimefakes.FakeMetricsCollector
with counterfeiter and then use that one, inNewManagerImpl(&ngxclient.NginxClient{}, &runtmefakes.FakeMetricCollector{}, logr.New(GinkgoLogr.GetSink()))
or in
mockedManager := &runtimefakes.FakeManager{}
any advice on this is appreciated a lot, thank you in advance
Closes #1498
Checklist
Before creating a PR, run through this checklist and mark each as complete.