-
Notifications
You must be signed in to change notification settings - Fork 39
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
[OSC-829] Refactoring unit test #1631
Conversation
@@ -31,12 +31,36 @@ import ( | |||
semconv "go.opentelemetry.io/collector/semconv/v1.18.0" | |||
) | |||
|
|||
func TestNewOpampAgent(t *testing.T) { | |||
cfg := createDefaultConfig() | |||
func setupOpampAgent(t *testing.T) (*opampAgent, *Config) { |
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 believe the goal here is to convert this to be parameterized tests in go - https://stackoverflow.com/questions/72856565/go-golang-test-parameterized
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.
Yes, we should follow the manner:
sumologic-otel-collector/pkg/exporter/sumologicexporter/config_test.go
Lines 12 to 111 in c7417c6
func TestInitExporterInvalidConfiguration(t *testing.T) { | |
testcases := []struct { | |
name string | |
cfg *Config | |
expectedError error | |
}{ | |
{ | |
name: "unexpected log format", | |
expectedError: errors.New("unexpected log format: test_format"), | |
cfg: &Config{ | |
LogFormat: "test_format", | |
MetricFormat: "otlp", | |
TraceFormat: "otlp", | |
ClientConfig: confighttp.ClientConfig{ | |
Timeout: defaultTimeout, | |
Endpoint: "test_endpoint", | |
}, | |
}, | |
}, | |
{ | |
name: "unexpected metric format", | |
expectedError: errors.New("unexpected metric format: test_format"), | |
cfg: &Config{ | |
LogFormat: "json", | |
MetricFormat: "test_format", | |
ClientConfig: confighttp.ClientConfig{ | |
Timeout: defaultTimeout, | |
Endpoint: "test_endpoint", | |
Compression: "gzip", | |
}, | |
}, | |
}, | |
{ | |
name: "unsupported Carbon2 metrics format", | |
expectedError: errors.New("support for the carbon2 metric format was removed, please use prometheus or otlp instead"), | |
cfg: &Config{ | |
LogFormat: "json", | |
MetricFormat: "carbon2", | |
ClientConfig: confighttp.ClientConfig{ | |
Timeout: defaultTimeout, | |
Endpoint: "test_endpoint", | |
Compression: "gzip", | |
}, | |
}, | |
}, | |
{ | |
name: "unsupported Graphite metrics format", | |
expectedError: errors.New("support for the graphite metric format was removed, please use prometheus or otlp instead"), | |
cfg: &Config{ | |
LogFormat: "json", | |
MetricFormat: "graphite", | |
ClientConfig: confighttp.ClientConfig{ | |
Timeout: defaultTimeout, | |
Endpoint: "test_endpoint", | |
Compression: "gzip", | |
}, | |
}, | |
}, | |
{ | |
name: "unexpected trace format", | |
expectedError: errors.New("unexpected trace format: text"), | |
cfg: &Config{ | |
LogFormat: "json", | |
MetricFormat: "otlp", | |
TraceFormat: "text", | |
ClientConfig: confighttp.ClientConfig{ | |
Timeout: defaultTimeout, | |
Endpoint: "test_endpoint", | |
Compression: "gzip", | |
}, | |
}, | |
}, | |
{ | |
name: "no endpoint and no auth extension specified", | |
expectedError: errors.New("no endpoint and no auth extension specified"), | |
cfg: &Config{ | |
LogFormat: "json", | |
MetricFormat: "otlp", | |
TraceFormat: "otlp", | |
ClientConfig: confighttp.ClientConfig{ | |
Timeout: defaultTimeout, | |
Compression: "gzip", | |
}, | |
}, | |
}, | |
} | |
for _, tc := range testcases { | |
tc := tc | |
t.Run(tc.name, func(t *testing.T) { | |
err := component.ValidateConfig(tc.cfg) | |
if tc.expectedError != nil { | |
assert.EqualError(t, err, tc.expectedError.Error()) | |
} else { | |
assert.NoError(t, err) | |
} | |
}) | |
} | |
} |
so, we have the testcases
struct with parameters and we iterate over it, so instead of having 5 different Test
functions, we have just 5-element list
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.
addressed
setup: func() (*Config, extension.Settings) { | ||
cfg := createDefaultConfig() | ||
set := extensiontest.NewNopSettings() | ||
set.BuildInfo = component.BuildInfo{Version: "test version", Command: "otelcoltest"} | ||
return cfg.(*Config), set | ||
}, |
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 see the setup is the same or almost the same across many tests? Can we unify it, so we put it into test body and not as test argument?
If there are more groups (2) we can split them to two tests
In general we shouldn't repeat any code if thats possible
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.
addressed
I am have an impression that you are trying to put all tests into one function, but not sure if thats possible. The aim of the issue was to group the tests such way, we avoid copying code. In current shape, we still copy code, just we do it in the struct instead of having separate functions |
rc := remoteConfig(t, path) | ||
|
||
changed, err := o.applyRemoteConfig(rc) | ||
assert.NoError(t, err) | ||
assert.True(t, changed) | ||
assert.NotEqual(t, len(o.effectiveConfig), 0) | ||
|
||
o.cfg.AcceptsRemoteConfiguration = false | ||
changed, err = o.applyRemoteConfig(rc) | ||
assert.False(t, changed) | ||
assert.Error(t, err) | ||
assert.Equal(t, "OpAMP agent does not accept remote configuration", err.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.
seems like there're more than 2 duplicates, should this be some type of function? 🤔
should this be broken down into multiple (2) PRs? kinda hard to see whats being changed/refactored here 🤔 |
assert.NotEqual(t, len(o.effectiveConfig), 0) | ||
|
||
o.cfg.AcceptsRemoteConfiguration = false | ||
changed, err = o.applyRemoteConfig(rc) |
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.
looks like alot of the ApplyRemote tests are repeated. i feel like the test should look something like this (didn't include all the different config yaml files and a little more adjustments)
func TestApplyRemoteConfig(t *testing.T) {
tests := []struct {
name string
file string
acceptsRemote bool
expectChanged bool
expectError bool
errorMessage string
}{
{"Valid Config", "testdata/opamp.d/opamp-remote-config.yaml", true, true, false, ""},
{"Apache Config", "testdata/opamp.d/opamp-apache-config.yaml", false, false, true, "OpAMP agent does not accept remote configuration"},
{"Invalid Config", "testdata/opamp.d/opamp-invalid-remote-config.yaml", true, false, true, "'max_elapsed_time' must be non-negative"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d, err := os.MkdirTemp("", "opamp.d")
assert.NoError(t, err)
defer os.RemoveAll(d)
cfg := createDefaultConfig().(*Config)
cfg.RemoteConfigurationDirectory = d
set := extensiontest.NewNopSettings()
o, err := newOpampAgent(cfg, set.Logger, set.BuildInfo, set.Resource)
assert.NoError(t, err)
cfg.AcceptsRemoteConfiguration = tt.acceptsRemote
path := filepath.Join(tt.file)
rb, err := os.ReadFile(path)
assert.NoError(t, err)
rc := &protobufs.AgentRemoteConfig{
Config: &protobufs.AgentConfigMap{
ConfigMap: map[string]*protobufs.AgentConfigFile{
"default": {
Body: rb,
},
},
},
ConfigHash: []byte("b2b1e3e7f45d564db1c0b621bbf67008"),
}
changed, err := o.applyRemoteConfig(rc)
if tt.expectError {
assert.Error(t, err)
assert.ErrorContains(t, err, tt.errorMessage)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectChanged, changed)
})
}
}
so now the code isn't as repeated :D
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.
Nice!! Thanks, updated
}{ | ||
{ | ||
name: "GetAgentCapabilities", | ||
validate: func(o *opampAgent, 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.
we shouldn't have validate as a input (I see the rest of the changes have this).
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.
as discussed offline, we reduced 15-20 lines by using defaultSetup
for all the testcases in TestDefaultOpampAgent
hence not touching 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.
I mentioned the defaultSetup
was good for refactoring. I feel these tests doesn't need to be inside a table test since most of them (if any) in here doesn't have repeated code. In my opinion, validate shouldn't be an input, but in this specific test case, it isn't WRONG to leave it like this.
What does the team think about this?
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 tend to agree, validations/assertions are typically not seen as a test input.
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.
addressed
cfg := createDefaultConfig().(*Config) | ||
cfg.RemoteConfigurationDirectory = d | ||
set := extensiontest.NewNopSettings() | ||
o, err := newOpampAgent(cfg, set.Logger, set.BuildInfo, set.Resource) |
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.
might be super nit, but 2 small things:
- this line
newOpampAgent
seems to be repeated in a bunch of tests, should this be refactored as well? - i see that this test uses
createDefaultConfig()
, a bunch of other tests also used that before but is now replaced withdefaultSetup()
, should this also use that? just want to keep things as consistent if possible
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.
done!
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.
newOpampAgent
is repeated in about 5+ tests
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.
addressed
cfg := createDefaultConfig().(*Config) | ||
cfg.RemoteConfigurationDirectory = d | ||
set := extensiontest.NewNopSettings() | ||
o, err := newOpampAgent(cfg, set.Logger, set.BuildInfo, set.Resource) |
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.
newOpampAgent
is repeated in about 5+ tests
semconv "go.opentelemetry.io/collector/semconv/v1.18.0" | ||
) | ||
|
||
func TestNewOpampAgent(t *testing.T) { | ||
cfg := createDefaultConfig() | ||
const errMsgRemoteConfigNotAccepted = "OpAMP agent does not accept remote configuration" |
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.
more than 1 const should in a block.
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.
addressed
@@ -565,14 +231,13 @@ func TestHackSetEndpoint(t *testing.T) { | |||
name: "prod sumologic url", | |||
url: "https://open-collectors.sumologic.com/api/v1", | |||
wantEndpoint: "wss://opamp-collectors.sumologic.com/v1/opamp", | |||
}, | |||
{ | |||
}, { |
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.
typo?
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.
addressed
return cfg, set | ||
} | ||
|
||
func setupWithBuildInfo(version, command string) (*Config, extension.Settings) { |
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.
can't this line be in defaultSetup
?
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 line was being used in the test setupWithBuildInfo("test version", "otelcoltest")
, shouldn't this be in the setup function?
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.
some functions use set.BuildInfo = component.BuildInfo{Version: version, Command: command}
while some do not, so in such places we use either defaultSetup or setupWithBuilderInfo depending upon the testcase
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 agree with that, then you can use a blank identifier to make it cleaner. so that way instead of writing smaller functions, it can all be in one function instead.
Refactoring unit test. Link to the ticket https://sumologic.atlassian.net/browse/OSC-829