feat(oonirun): improve tests (#915)
See https://github.com/ooni/probe/issues/2184 While there, rename `runtimex.PanicIfFalse` to `runtimex.Assert` (it was about time...)
This commit is contained in:
parent
a8a29cc0dd
commit
d0da224a2a
32 changed files with 1837 additions and 112 deletions
|
|
@ -2,13 +2,60 @@ package oonirun
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/kvstore"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/testingx"
|
||||
)
|
||||
|
||||
// TODO(bassosimone): it would be cool to write unit tests. However, to do that
|
||||
// we need to ~redesign the engine package for unit-testability.
|
||||
func newMinimalFakeSession() *mocks.Session {
|
||||
return &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
MockNewExperimentBuilder: func(name string) (model.ExperimentBuilder, error) {
|
||||
eb := &mocks.ExperimentBuilder{
|
||||
MockInputPolicy: func() model.InputPolicy {
|
||||
return model.InputNone
|
||||
},
|
||||
MockSetOptionsAny: func(options map[string]any) error {
|
||||
return nil
|
||||
},
|
||||
MockNewExperiment: func() model.Experiment {
|
||||
exp := &mocks.Experiment{
|
||||
MockMeasureAsync: func(ctx context.Context, input string) (<-chan *model.Measurement, error) {
|
||||
out := make(chan *model.Measurement)
|
||||
go func() {
|
||||
defer close(out)
|
||||
ff := &testingx.FakeFiller{}
|
||||
var meas model.Measurement
|
||||
ff.Fill(&meas)
|
||||
out <- &meas
|
||||
}()
|
||||
return out, nil
|
||||
},
|
||||
MockKibiBytesReceived: func() float64 {
|
||||
return 1.1
|
||||
},
|
||||
MockKibiBytesSent: func() float64 {
|
||||
return 0.1
|
||||
},
|
||||
}
|
||||
return exp
|
||||
},
|
||||
}
|
||||
return eb, nil
|
||||
},
|
||||
MockDefaultHTTPClient: func() model.HTTPClient {
|
||||
return http.DefaultClient
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestOONIRunV1Link(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
|
@ -23,7 +70,7 @@ func TestOONIRunV1Link(t *testing.T) {
|
|||
NoJSON: true,
|
||||
Random: false,
|
||||
ReportFile: "",
|
||||
Session: newSession(ctx, t),
|
||||
Session: newMinimalFakeSession(),
|
||||
}
|
||||
r := NewLinkRunner(config, "https://run.ooni.io/nettest?tn=example&mv=1.2.0")
|
||||
if err := r.Run(ctx); err != nil {
|
||||
|
|
@ -34,3 +81,188 @@ func TestOONIRunV1Link(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestV1MeasureInvalidURL(t *testing.T) {
|
||||
t.Run("URL does not parse", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "\t"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with https:// URL and invalid hostname", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "https://run.ooni.nu/nettest"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if !errors.Is(err, ErrInvalidV1URLHost) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with https:// URL and invalid path", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "https://run.ooni.io/antani"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if !errors.Is(err, ErrInvalidV1URLPath) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with ooni:// URL and invalid host", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "ooni://antani"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if !errors.Is(err, ErrInvalidV1URLHost) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with ooni:// URL and path", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "ooni://nettest/x"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if !errors.Is(err, ErrInvalidV1URLPath) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with invalid URL scheme", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "antani://nettest"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if !errors.Is(err, ErrInvalidV1URLScheme) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with empty test name", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "ooni://nettest/"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if !errors.Is(err, ErrInvalidV1URLQueryArgument) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with invalid JSON and explicit / as path", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "ooni://nettest/?tn=web_connectivity&ta=123x"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if !errors.Is(err, ErrInvalidV1URLQueryArgument) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with invalid JSON and empty path", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "ooni://nettest?tn=web_connectivity&ta=123x"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if !errors.Is(err, ErrInvalidV1URLQueryArgument) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with missing minimum version", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &LinkConfig{
|
||||
Session: &mocks.Session{
|
||||
MockLogger: func() model.Logger {
|
||||
return model.DiscardLogger
|
||||
},
|
||||
},
|
||||
}
|
||||
URL := "ooni://nettest?tn=example"
|
||||
err := v1Measure(ctx, config, URL)
|
||||
if !errors.Is(err, ErrInvalidV1URLQueryArgument) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestV1ParseArguments(t *testing.T) {
|
||||
t.Run("with invalid test arguments", func(t *testing.T) {
|
||||
// "[QueryUnescape] returns an error if any % is not followed by two hexadecimal digits."
|
||||
out, err := v1ParseArguments("%KK")
|
||||
if !errors.Is(err, ErrInvalidV1URLQueryArgument) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
if len(out) > 0 {
|
||||
t.Fatal("expected no output")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with valid arguments", func(t *testing.T) {
|
||||
out, err := v1ParseArguments("%7B%22urls%22%3A%5B%22https%3A%2F%2Fexample.com%2F%22%5D%7D")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(out) != 1 || out[0] != "https://example.com/" {
|
||||
t.Fatal("unexpected out", out)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue