d922bd9afc
The objective is to make PR checks run much faster. See https://github.com/ooni/probe/issues/2113 for context. Regarding netxlite's tests: Checking for every commit on master or on a release branch is good enough and makes pull requests faster than one minute since netxlite for windows is now 1m slower than coverage. We're losing some coverage but coverage from integration tests is not so good anyway, so I'm not super sad about this loss.
774 lines
27 KiB
Go
774 lines
27 KiB
Go
package oonimkall
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
engine "github.com/ooni/probe-cli/v3/internal/engine"
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
|
)
|
|
|
|
func TestMeasurementSubmissionEventName(t *testing.T) {
|
|
if measurementSubmissionEventName(nil) != eventTypeStatusMeasurementSubmission {
|
|
t.Fatal("unexpected submission event name")
|
|
}
|
|
if measurementSubmissionEventName(errors.New("mocked error")) != eventTypeFailureMeasurementSubmission {
|
|
t.Fatal("unexpected submission event name")
|
|
}
|
|
}
|
|
|
|
func TestMeasurementSubmissionFailure(t *testing.T) {
|
|
if measurementSubmissionFailure(nil) != "" {
|
|
t.Fatal("unexpected submission failure")
|
|
}
|
|
if measurementSubmissionFailure(errors.New("mocked error")) != "mocked error" {
|
|
t.Fatal("unexpected submission failure")
|
|
}
|
|
}
|
|
|
|
func TestTaskRunnerRun(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
|
|
// newRunnerForTesting is a factory for creating a new
|
|
// runner that wraps newRunner and also sets a specific
|
|
// taskSessionBuilder for testing purposes.
|
|
newRunnerForTesting := func() (*runnerForTask, *CollectorTaskEmitter) {
|
|
settings := &settings{
|
|
Name: "Example",
|
|
Options: settingsOptions{
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "testdata/state",
|
|
Version: 1,
|
|
}
|
|
e := &CollectorTaskEmitter{}
|
|
r := newRunner(settings, e)
|
|
return r, e
|
|
}
|
|
|
|
// runAndCollectContext runs the task until completion
|
|
// and collects the emitted events. Remember that
|
|
// it's not race safe to modify the events.
|
|
runAndCollectContext := func(ctx context.Context, r taskRunner, e *CollectorTaskEmitter) []*event {
|
|
r.Run(ctx)
|
|
return e.Collect()
|
|
}
|
|
|
|
// runAndCollect is like runAndCollectContext
|
|
// but uses context.Background()
|
|
runAndCollect := func(r taskRunner, e *CollectorTaskEmitter) []*event {
|
|
return runAndCollectContext(context.Background(), r, e)
|
|
}
|
|
|
|
// countEventsByKey returns the number of events
|
|
// with the given key inside of the list.
|
|
countEventsByKey := func(events []*event, key string) (count int) {
|
|
for _, ev := range events {
|
|
if ev.Key == key {
|
|
count++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// assertCountEventsByKey fails is the number of events
|
|
// of the given type is not the expected one.
|
|
assertCountEventsByKey := func(events []*event, key string, count int) {
|
|
if countEventsByKey(events, key) != count {
|
|
t.Fatalf("unexpected number of '%s' events", key)
|
|
}
|
|
}
|
|
|
|
t.Run("with unsupported settings", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Version = 0 // force unsupported version
|
|
events := runAndCollect(runner, emitter)
|
|
assertCountEventsByKey(events, eventTypeFailureStartup, 1)
|
|
})
|
|
|
|
t.Run("with failure when creating a new kvstore", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
// override the kvstore builder to provoke an error
|
|
runner.kvStoreBuilder = &MockableKVStoreFSBuilder{
|
|
MockNewFS: func(path string) (model.KeyValueStore, error) {
|
|
return nil, errors.New("generic error")
|
|
},
|
|
}
|
|
events := runAndCollect(runner, emitter)
|
|
assertCountEventsByKey(events, eventTypeFailureStartup, 1)
|
|
})
|
|
|
|
t.Run("with unparsable proxyURL", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Proxy = "\t" // invalid proxy URL
|
|
events := runAndCollect(runner, emitter)
|
|
assertCountEventsByKey(events, eventTypeFailureStartup, 1)
|
|
})
|
|
|
|
t.Run("with a parsable proxyURL", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
// set a valid URL
|
|
runner.settings.Proxy = "https://127.0.0.1/"
|
|
// set a fake session builder that causes the startup to
|
|
// fail but records the config passed to NewSession
|
|
saver := &SessionBuilderConfigSaver{}
|
|
runner.sessionBuilder = saver
|
|
events := runAndCollect(runner, emitter)
|
|
assertCountEventsByKey(events, eventTypeFailureStartup, 1)
|
|
if saver.Config.ProxyURL.String() != runner.settings.Proxy {
|
|
t.Fatal("invalid proxy URL")
|
|
}
|
|
})
|
|
|
|
t.Run("with custom probe services URL", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
// set a probe services URL
|
|
runner.settings.Options.ProbeServicesBaseURL = "https://127.0.0.1"
|
|
// set a fake session builder that causes the startup to
|
|
// fail but records the config passed to NewSession
|
|
saver := &SessionBuilderConfigSaver{}
|
|
runner.sessionBuilder = saver
|
|
events := runAndCollect(runner, emitter)
|
|
assertCountEventsByKey(events, eventTypeFailureStartup, 1)
|
|
psu := saver.Config.AvailableProbeServices
|
|
if len(psu) != 1 {
|
|
t.Fatal("invalid length")
|
|
}
|
|
if psu[0].Type != "https" {
|
|
t.Fatal("invalid type")
|
|
}
|
|
if psu[0].Address != runner.settings.Options.ProbeServicesBaseURL {
|
|
t.Fatal("invalid address")
|
|
}
|
|
if psu[0].Front != "" {
|
|
t.Fatal("invalid front")
|
|
}
|
|
})
|
|
|
|
type eventKeyCount struct {
|
|
Key string
|
|
Count int
|
|
}
|
|
|
|
// reduceEventsKeysIgnoreLog reduces the list of event keys
|
|
// counting equal subsequent keys and ignoring log events
|
|
reduceEventsKeysIgnoreLog := func(events []*event) (out []eventKeyCount) {
|
|
var current eventKeyCount
|
|
for _, ev := range events {
|
|
if ev.Key == eventTypeLog {
|
|
continue
|
|
}
|
|
if current.Key == ev.Key {
|
|
current.Count++
|
|
continue
|
|
}
|
|
if current.Key != "" {
|
|
out = append(out, current)
|
|
}
|
|
current.Key = ev.Key
|
|
current.Count = 1
|
|
}
|
|
if current.Key != "" {
|
|
out = append(out, current)
|
|
}
|
|
return
|
|
}
|
|
|
|
// fakeSuccessfulRun returns a new set of dependencies that
|
|
// will perform a fully successful, but fake, run.
|
|
fakeSuccessfulRun := func() *MockableTaskRunnerDependencies {
|
|
return &MockableTaskRunnerDependencies{
|
|
MockableKibiBytesReceived: func() float64 {
|
|
return 10
|
|
},
|
|
MockableKibiBytesSent: func() float64 {
|
|
return 4
|
|
},
|
|
MockableOpenReportContext: func(ctx context.Context) error {
|
|
return nil
|
|
},
|
|
MockableReportID: func() string {
|
|
return "20211202T074907Z_example_IT_30722_n1_axDLHNUfJaV1IbuU"
|
|
},
|
|
MockableMeasureWithContext: func(ctx context.Context, input string) (*model.Measurement, error) {
|
|
return &model.Measurement{}, nil
|
|
},
|
|
MockableSubmitAndUpdateMeasurementContext: func(ctx context.Context, measurement *model.Measurement) error {
|
|
return nil
|
|
},
|
|
MockableSetCallbacks: func(callbacks model.ExperimentCallbacks) {
|
|
},
|
|
MockableInputPolicy: func() engine.InputPolicy {
|
|
return engine.InputNone
|
|
},
|
|
MockableInterruptible: func() bool {
|
|
return false
|
|
},
|
|
MockClose: func() error {
|
|
return nil
|
|
},
|
|
MockMaybeLookupBackendsContext: func(ctx context.Context) error {
|
|
return nil
|
|
},
|
|
MockMaybeLookupLocationContext: func(ctx context.Context) error {
|
|
return nil
|
|
},
|
|
MockProbeIP: func() string {
|
|
return "130.192.91.211"
|
|
},
|
|
MockProbeASNString: func() string {
|
|
return "AS137"
|
|
},
|
|
MockProbeCC: func() string {
|
|
return "IT"
|
|
},
|
|
MockProbeNetworkName: func() string {
|
|
return "GARR"
|
|
},
|
|
MockResolverASNString: func() string {
|
|
return "AS137"
|
|
},
|
|
MockResolverIP: func() string {
|
|
return "130.192.3.24"
|
|
},
|
|
MockResolverNetworkName: func() string {
|
|
return "GARR"
|
|
},
|
|
}
|
|
}
|
|
|
|
assertReducedEventsLike := func(t *testing.T, expected, got []eventKeyCount) {
|
|
if diff := cmp.Diff(expected, got); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
t.Run("with invalid experiment name", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockNewExperimentBuilderByName = func(name string) (taskExperimentBuilder, error) {
|
|
return nil, errors.New("invalid experiment name")
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeFailureStartup, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with error during backends lookup", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockMaybeLookupBackendsContext = func(ctx context.Context) error {
|
|
return errors.New("mocked error")
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeFailureStartup, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with error during location lookup", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockMaybeLookupLocationContext = func(ctx context.Context) error {
|
|
return errors.New("mocked error")
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeFailureIPLookup, Count: 1},
|
|
{Key: eventTypeFailureASNLookup, Count: 1},
|
|
{Key: eventTypeFailureCCLookup, Count: 1},
|
|
{Key: eventTypeFailureResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with missing input and InputOrQueryBackend policy", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputOrQueryBackend
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeFailureStartup, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with missing input and InputStrictlyRequired policy", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputStrictlyRequired
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeFailureStartup, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run(
|
|
"with InputOrStaticDefault policy and experiment with no static input",
|
|
func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Name = "Antani" // no input for this experiment
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputOrStaticDefault
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeFailureStartup, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with InputNone policy and provided input", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Inputs = append(runner.settings.Inputs, "https://x.org/")
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputNone
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeFailureStartup, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with failure opening report", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableOpenReportContext = func(ctx context.Context) error {
|
|
return errors.New("mocked error")
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeFailureReportCreate, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with success and InputNone policy", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputNone
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with measurement failure and InputNone policy", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputNone
|
|
}
|
|
fake.MockableMeasureWithContext = func(ctx context.Context, input string) (measurement *model.Measurement, err error) {
|
|
return nil, errors.New("preconditions error")
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeFailureMeasurement, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with success and InputStrictlyRequired", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Inputs = []string{"a", "b", "c", "d"}
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputStrictlyRequired
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with success and InputOptional and input", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Inputs = []string{"a", "b", "c", "d"}
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputOptional
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with success and InputOptional and no input", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputOptional
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with success and InputOrStaticDefault", func(t *testing.T) {
|
|
experimentName := "DNSCheck"
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Name = experimentName
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputOrStaticDefault
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
}
|
|
allEntries, err := engine.StaticBareInputForExperiment(experimentName)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// write the correct entries for each expected measurement.
|
|
for idx := 0; idx < len(allEntries); idx++ {
|
|
expect = append(expect, eventKeyCount{Key: eventTypeStatusMeasurementStart, Count: 1})
|
|
expect = append(expect, eventKeyCount{Key: eventTypeStatusProgress, Count: 1})
|
|
expect = append(expect, eventKeyCount{Key: eventTypeMeasurement, Count: 1})
|
|
expect = append(expect, eventKeyCount{Key: eventTypeStatusMeasurementSubmission, Count: 1})
|
|
expect = append(expect, eventKeyCount{Key: eventTypeStatusMeasurementDone, Count: 1})
|
|
}
|
|
expect = append(expect, eventKeyCount{Key: eventTypeStatusEnd, Count: 1})
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with succes and max runtime", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Inputs = []string{"a", "b", "c", "d"}
|
|
runner.settings.Options.MaxRuntime = 2
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputStrictlyRequired
|
|
}
|
|
fake.MockableMeasureWithContext = func(ctx context.Context, input string) (measurement *model.Measurement, err error) {
|
|
time.Sleep(1 * time.Second)
|
|
return &model.Measurement{}, nil
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with interrupted experiment", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Inputs = []string{"a", "b", "c", "d"}
|
|
runner.settings.Options.MaxRuntime = 2
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputStrictlyRequired
|
|
}
|
|
fake.MockableInterruptible = func() bool {
|
|
return true
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
fake.MockableMeasureWithContext = func(ctx context.Context, input string) (measurement *model.Measurement, err error) {
|
|
cancel()
|
|
return &model.Measurement{}, nil
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollectContext(ctx, runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with measurement submission failure", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
runner.settings.Inputs = []string{"a"}
|
|
fake := fakeSuccessfulRun()
|
|
fake.MockableInputPolicy = func() engine.InputPolicy {
|
|
return engine.InputStrictlyRequired
|
|
}
|
|
fake.MockableSubmitAndUpdateMeasurementContext = func(ctx context.Context, measurement *model.Measurement) error {
|
|
return errors.New("cannot submit")
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeFailureMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
//
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
|
|
t.Run("with success and progress", func(t *testing.T) {
|
|
runner, emitter := newRunnerForTesting()
|
|
fake := fakeSuccessfulRun()
|
|
var callbacks model.ExperimentCallbacks
|
|
fake.MockableSetCallbacks = func(cbs model.ExperimentCallbacks) {
|
|
callbacks = cbs
|
|
}
|
|
fake.MockableMeasureWithContext = func(ctx context.Context, input string) (measurement *model.Measurement, err error) {
|
|
callbacks.OnProgress(1, "hello from the fake experiment")
|
|
return &model.Measurement{}, nil
|
|
}
|
|
runner.sessionBuilder = fake
|
|
events := runAndCollect(runner, emitter)
|
|
reduced := reduceEventsKeysIgnoreLog(events)
|
|
expect := []eventKeyCount{
|
|
{Key: eventTypeStatusQueued, Count: 1},
|
|
{Key: eventTypeStatusStarted, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 3},
|
|
{Key: eventTypeStatusGeoIPLookup, Count: 1},
|
|
{Key: eventTypeStatusResolverLookup, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeStatusReportCreate, Count: 1},
|
|
{Key: eventTypeStatusMeasurementStart, Count: 1},
|
|
{Key: eventTypeStatusProgress, Count: 1},
|
|
{Key: eventTypeMeasurement, Count: 1},
|
|
{Key: eventTypeStatusMeasurementSubmission, Count: 1},
|
|
{Key: eventTypeStatusMeasurementDone, Count: 1},
|
|
{Key: eventTypeStatusEnd, Count: 1},
|
|
}
|
|
assertReducedEventsLike(t, expect, reduced)
|
|
})
|
|
}
|