ooni-probe-cli/internal/engine/experiment/psiphon/psiphon_test.go
Simone Basso 99ec7ffca9
fix: ensure experiments return nil when we want to submit (#654)
Since https://github.com/ooni/probe-cli/pull/527, if an experiment
returns an error, the corresponding measurement is not submitted since
the semantics of returning an error is that something fundamental
went wrong (e.g., we could not parse the input URL).

This diff ensures that all experiments only return and error when
something fundamental was wrong and return nil otherwise.

Reference issue: https://github.com/ooni/probe/issues/1808.
2022-01-07 13:17:20 +01:00

164 lines
4.6 KiB
Go

package psiphon_test
import (
"context"
"errors"
"io"
"testing"
"time"
"github.com/apex/log"
"github.com/ooni/probe-cli/v3/internal/atomicx"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/psiphon"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/urlgetter"
"github.com/ooni/probe-cli/v3/internal/engine/mockable"
"github.com/ooni/probe-cli/v3/internal/model"
)
// Implementation note: integration test performed by
// the $topdir/experiment_test.go file
func TestNewExperimentMeasurer(t *testing.T) {
measurer := psiphon.NewExperimentMeasurer(psiphon.Config{})
if measurer.ExperimentName() != "psiphon" {
t.Fatal("unexpected name")
}
if measurer.ExperimentVersion() != "0.6.0" {
t.Fatal("unexpected version")
}
}
func TestRunWithCancelledContext(t *testing.T) {
measurer := psiphon.NewExperimentMeasurer(psiphon.Config{})
ctx, cancel := context.WithCancel(context.Background())
cancel() // fail immediately
measurement := new(model.Measurement)
err := measurer.Run(ctx, newfakesession(), measurement,
model.NewPrinterCallbacks(log.Log))
if !errors.Is(err, nil) { // nil because we want to submit the measurement
t.Fatal("expected another error here")
}
tk := measurement.TestKeys.(*psiphon.TestKeys)
if tk.MaxRuntime <= 0 {
t.Fatal("you did not set the max runtime")
}
sk, err := measurer.GetSummaryKeys(measurement)
if err != nil {
t.Fatal(err)
}
if _, ok := sk.(psiphon.SummaryKeys); !ok {
t.Fatal("invalid type for summary keys")
}
}
func TestRunWithCustomInputAndCancelledContext(t *testing.T) {
expected := "http://x.org"
measurement := &model.Measurement{
Input: model.MeasurementTarget(expected),
}
measurer := psiphon.NewExperimentMeasurer(psiphon.Config{})
measurer.(*psiphon.Measurer).BeforeGetHook = func(g urlgetter.Getter) {
if g.Target != expected {
t.Fatal("target was not correctly set")
}
}
ctx, cancel := context.WithCancel(context.Background())
cancel() // fail immediately
err := measurer.Run(ctx, newfakesession(), measurement,
model.NewPrinterCallbacks(log.Log))
if !errors.Is(err, nil) { // nil because we want to submit the measurement
t.Fatal("expected another error here")
}
tk := measurement.TestKeys.(*psiphon.TestKeys)
if tk.MaxRuntime <= 0 {
t.Fatal("you did not set the max runtime")
}
}
func TestRunWillPrintSomethingWithCancelledContext(t *testing.T) {
measurement := new(model.Measurement)
measurer := psiphon.NewExperimentMeasurer(psiphon.Config{})
ctx, cancel := context.WithCancel(context.Background())
measurer.(*psiphon.Measurer).BeforeGetHook = func(g urlgetter.Getter) {
time.Sleep(2 * time.Second)
cancel() // fail after we've given the printer a chance to run
}
observer := observerCallbacks{progress: &atomicx.Int64{}}
err := measurer.Run(ctx, newfakesession(), measurement, observer)
if !errors.Is(err, nil) { // nil because we want to submit the measurement
t.Fatal("expected another error here")
}
tk := measurement.TestKeys.(*psiphon.TestKeys)
if tk.MaxRuntime <= 0 {
t.Fatal("you did not set the max runtime")
}
if observer.progress.Load() < 2 {
t.Fatal("not enough progress emitted?!")
}
}
type observerCallbacks struct {
progress *atomicx.Int64
}
func (d observerCallbacks) OnProgress(percentage float64, message string) {
d.progress.Add(1)
}
func newfakesession() model.ExperimentSession {
return &mockable.Session{MockableLogger: log.Log}
}
func TestSummaryKeysInvalidType(t *testing.T) {
measurement := new(model.Measurement)
m := &psiphon.Measurer{}
_, err := m.GetSummaryKeys(measurement)
if err.Error() != "invalid test keys type" {
t.Fatal("not the error we expected")
}
}
func TestSummaryKeysGood(t *testing.T) {
measurement := &model.Measurement{TestKeys: &psiphon.TestKeys{TestKeys: urlgetter.TestKeys{
BootstrapTime: 123,
}}}
m := &psiphon.Measurer{}
osk, err := m.GetSummaryKeys(measurement)
if err != nil {
t.Fatal(err)
}
sk := osk.(psiphon.SummaryKeys)
if sk.BootstrapTime != 123 {
t.Fatal("invalid latency")
}
if sk.Failure != "" {
t.Fatal("invalid failure")
}
if sk.IsAnomaly {
t.Fatal("invalid isAnomaly")
}
}
func TestSummaryKeysFailure(t *testing.T) {
expected := io.EOF.Error()
measurement := &model.Measurement{TestKeys: &psiphon.TestKeys{TestKeys: urlgetter.TestKeys{
BootstrapTime: 123,
Failure: &expected,
}}}
m := &psiphon.Measurer{}
osk, err := m.GetSummaryKeys(measurement)
if err != nil {
t.Fatal(err)
}
sk := osk.(psiphon.SummaryKeys)
if sk.BootstrapTime != 123 {
t.Fatal("invalid latency")
}
if sk.Failure != expected {
t.Fatal("invalid failure")
}
if sk.IsAnomaly == false {
t.Fatal("invalid isAnomaly")
}
}