-
Notifications
You must be signed in to change notification settings - Fork 38
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
Mock NTP client #217
Mock NTP client #217
Changes from 6 commits
3d6eb14
93f85e2
cf634f2
621c0f9
fe3e7c0
a865c46
42794f6
9cacbc8
de817f1
59f1ee0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,9 +16,39 @@ | |
package ntpmonitor | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
"time" | ||
|
||
"github.com/beevik/ntp" | ||
) | ||
|
||
type MockNTPClient struct { | ||
ignoredServers map[string]string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is ignoredServers? Maybe a comment? Edit: Ah, used for which servers to fail on. Yea, a comment would be helpful! |
||
} | ||
|
||
func (c MockNTPClient) QueryWithOptions(srv string, opts ntp.QueryOptions) (*ntp.Response, error) { | ||
if _, ok := c.ignoredServers[srv]; ok { | ||
return nil, errors.New("failed to query NTP server") | ||
} | ||
|
||
return &ntp.Response{ | ||
ClockOffset: 1, | ||
Time: time.Now(), | ||
}, nil | ||
} | ||
|
||
type driftedTimeNTPClient struct { | ||
driftedOffset time.Duration | ||
} | ||
|
||
func (c driftedTimeNTPClient) QueryWithOptions(srv string, opts ntp.QueryOptions) (*ntp.Response, error) { | ||
return &ntp.Response{ | ||
ClockOffset: c.driftedOffset, | ||
Time: time.Now(), | ||
}, nil | ||
} | ||
|
||
func TestNewFromConfig(t *testing.T) { | ||
var cfg Config | ||
var nm *NTPMonitor | ||
|
@@ -33,7 +63,7 @@ func TestNewFromConfig(t *testing.T) { | |
t.Errorf("expected error %s got %s", ErrTooFewServers, err) | ||
} | ||
|
||
// Number of servers are smaller than requsted | ||
// Number of servers are smaller than requested | ||
cfg.Servers = append(cfg.Servers, "foo.bar") | ||
cfg.NumServers = 2 | ||
nm, err = NewFromConfig(&cfg) | ||
|
@@ -99,3 +129,149 @@ func TestNewFromConfig(t *testing.T) { | |
t.Errorf("unexpected error %s", err) | ||
} | ||
} | ||
|
||
func TestNTPMonitorQueryNTPServer(t *testing.T) { | ||
mockNTP := MockNTPClient{} | ||
failNTP := MockNTPClient{ | ||
ignoredServers: map[string]string{ | ||
"s1": "", | ||
}, | ||
} | ||
|
||
testCases := []struct { | ||
name string | ||
client MockNTPClient | ||
requestAttempts int | ||
expectTestToPass bool | ||
}{ | ||
{ | ||
name: "Successfully query NTP server", | ||
client: mockNTP, | ||
requestAttempts: 3, | ||
expectTestToPass: true, | ||
}, | ||
{ | ||
name: "Fail to query NTP server", | ||
client: failNTP, | ||
requestAttempts: 1, | ||
expectTestToPass: false, | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
monitor, err := NewFromConfig(&Config{ | ||
Servers: []string{"s1"}, | ||
NumServers: 1, | ||
RequestAttempts: tc.requestAttempts, | ||
ServerThreshold: 1, | ||
RequestTimeout: 1, | ||
MaxTimeDelta: 1, | ||
}) | ||
if err != nil { | ||
t.Fatalf("unexpectedly failed to create NTP monitor: %v", err) | ||
} | ||
monitor.ntpClient = tc.client | ||
|
||
resp, err := monitor.QueryNTPServer("s1") | ||
if tc.expectTestToPass && err != nil { | ||
t.Errorf("test '%s' unexpectedly failed with non-nil error: %v", tc.name, err) | ||
} | ||
if tc.expectTestToPass && resp == nil { | ||
t.Errorf("test '%s' unexpectedly failed with nil ntp.Response", tc.name) | ||
} | ||
if !tc.expectTestToPass && err == nil { | ||
t.Errorf("test '%s' unexpectedly passed with a nil error", tc.name) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we also test metrics output? (I'm not sure!) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll look into this. |
||
} | ||
} | ||
|
||
func TestNTPMonitorQueryServers(t *testing.T) { | ||
mockNTP := MockNTPClient{} | ||
failNTP := MockNTPClient{ | ||
ignoredServers: map[string]string{ | ||
"s1": "", | ||
"s2": "", | ||
"s3": "", | ||
}, | ||
} | ||
partialFailNTP := MockNTPClient{ | ||
ignoredServers: map[string]string{ | ||
"s2": "", | ||
"s3": "", | ||
}, | ||
} | ||
|
||
offsetDuration, err := time.ParseDuration("2s") | ||
if err != nil { | ||
t.Fatalf("unexpected failed to parse duration: %v", err) | ||
} | ||
|
||
driftedNTP := driftedTimeNTPClient{ | ||
driftedOffset: offsetDuration, | ||
} | ||
|
||
testCases := []struct { | ||
name string | ||
client NTPClient | ||
serverThreshold int | ||
maxTimeDelta int | ||
expectEnoughServerResponse bool | ||
expectValidServerResponse bool | ||
}{ | ||
{ | ||
name: "Successfully query all NTP servers", | ||
client: mockNTP, | ||
serverThreshold: 3, | ||
maxTimeDelta: 3, | ||
expectEnoughServerResponse: true, | ||
expectValidServerResponse: true, | ||
}, | ||
{ | ||
name: "Receive too few server responses", | ||
client: partialFailNTP, | ||
serverThreshold: 2, | ||
maxTimeDelta: 5, | ||
expectEnoughServerResponse: false, | ||
expectValidServerResponse: true, | ||
}, | ||
{ | ||
name: "Receive too many drifted time responses", | ||
client: driftedNTP, | ||
serverThreshold: 2, | ||
maxTimeDelta: 4, | ||
expectEnoughServerResponse: true, | ||
expectValidServerResponse: false, | ||
}, | ||
{ | ||
name: "Fail to receive any responses", | ||
client: failNTP, | ||
serverThreshold: 1, | ||
maxTimeDelta: 4, | ||
expectEnoughServerResponse: false, | ||
expectValidServerResponse: false, | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
monitor, err := NewFromConfig(&Config{ | ||
Servers: []string{"s1", "s2", "s3", "s4", "s5", "s6"}, | ||
NumServers: 3, | ||
Period: 1, | ||
RequestAttempts: 1, | ||
ServerThreshold: tc.serverThreshold, | ||
RequestTimeout: 1, | ||
MaxTimeDelta: tc.maxTimeDelta, | ||
}) | ||
if err != nil { | ||
t.Fatalf("unexpectedly failed to create NTP monitor: %v", err) | ||
} | ||
monitor.ntpClient = tc.client | ||
|
||
delta := time.Duration(5) * time.Second | ||
responses := monitor.queryServers(delta) | ||
if tc.expectEnoughServerResponse && responses.tooFewServerResponses { | ||
t.Errorf("test '%s' unexpectedly failed with too few server responses", tc.name) | ||
} | ||
if tc.expectValidServerResponse && responses.tooManyInvalidResponses { | ||
t.Errorf("test '%s' unexpectedly failed with too many invalid responses", tc.name) | ||
} | ||
} | ||
} |
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.
nit: Should
QueryNTPServer
be private, because we don't call it elsewhere?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, looks like it isn't called anywhere else.