Skip to content

Commit

Permalink
feat(oonirun): improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bassosimone committed Aug 31, 2022
1 parent a8a29cc commit e8e61c9
Show file tree
Hide file tree
Showing 32 changed files with 1,822 additions and 112 deletions.
2 changes: 1 addition & 1 deletion internal/cmd/miniooni/consent.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func acquireUserConsent(miniooniDir string, currentOptions *Options) {
consentFile := path.Join(miniooniDir, "informed")
err := maybeWriteConsentFile(currentOptions.Yes, consentFile)
runtimex.PanicOnError(err, "cannot write informed consent file")
runtimex.PanicIfFalse(
runtimex.Assert(
regularFileExists(consentFile),
riskOfRunningOONI,
)
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/miniooni/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func mainSingleIteration(logger model.Logger, experimentName string, currentOpti
log.Infof("Current time: %s", time.Now().Format("2006-01-02 15:04:05 MST"))

homeDir := gethomedir(currentOptions.HomeDir)
runtimex.PanicIfFalse(homeDir != "", "home directory is empty")
runtimex.Assert(homeDir != "", "home directory is empty")
miniooniDir := path.Join(homeDir, ".miniooni")
err := os.MkdirAll(miniooniDir, 0700)
runtimex.PanicOnError(err, "cannot create $HOME/.miniooni directory")
Expand Down
2 changes: 2 additions & 0 deletions internal/engine/mockable/mockable.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
)

// Session allows to mock sessions.
//
// Deprecated: use ./internal/model/mocks.Session instead.
type Session struct {
MockableTestHelpers map[string][]model.OOAPIService
MockableHTTPClient model.HTTPClient
Expand Down
6 changes: 2 additions & 4 deletions internal/engine/saver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import (
"github.com/ooni/probe-cli/v3/internal/model"
)

// Saver saves a measurement on some persistent storage.
type Saver interface {
SaveMeasurement(m *model.Measurement) error
}
// Saver is an alias for model.Saver.
type Saver = model.Saver

// SaverConfig is the configuration for creating a new Saver.
type SaverConfig struct {
Expand Down
8 changes: 2 additions & 6 deletions internal/engine/submitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@ import (
// TODO(bassosimone): maybe keep track of which measurements
// could not be submitted by a specific submitter?

// Submitter submits a measurement to the OONI collector.
type Submitter interface {
// Submit submits the measurement and updates its
// report ID field in case of success.
Submit(ctx context.Context, m *model.Measurement) error
}
// Submitter is an alias for model.Submitter
type Submitter = model.Submitter

// SubmitterSession is the Submitter's view of the Session.
type SubmitterSession interface {
Expand Down
2 changes: 1 addition & 1 deletion internal/measurexlite/failure.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func NewFailure(err error) *string {
if !errors.As(err, &errWrapper) {
err := netxlite.NewTopLevelGenericErrWrapper(err)
couldConvert := errors.As(err, &errWrapper)
runtimex.PanicIfFalse(couldConvert, "we should have an ErrWrapper here")
runtimex.Assert(couldConvert, "we should have an ErrWrapper here")
}
s := errWrapper.Failure
if s == "" {
Expand Down
22 changes: 22 additions & 0 deletions internal/model/experiment.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,25 @@ type ExperimentOptionInfo struct {
// Type contains the type.
Type string
}

// ExperimentInputLoader loads inputs from local or remote sources.
type ExperimentInputLoader interface {
Load(ctx context.Context) ([]OOAPIURLInfo, error)
}

// Submitter submits a measurement to the OONI collector.
type Submitter interface {
// Submit submits the measurement and updates its
// report ID field in case of success.
Submit(ctx context.Context, m *Measurement) error
}

// Saver saves a measurement on some persistent storage.
type Saver interface {
SaveMeasurement(m *Measurement) error
}

// ExperimentInputProcessor processes inputs running the given experiment.
type ExperimentInputProcessor interface {
Run(ctx context.Context) error
}
75 changes: 75 additions & 0 deletions internal/model/mocks/experiment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package mocks

import (
"context"

"github.com/ooni/probe-cli/v3/internal/model"
)

// Experiment mocks model.Experiment
type Experiment struct {
MockKibiBytesReceived func() float64

MockKibiBytesSent func() float64

MockName func() string

MockGetSummaryKeys func(m *model.Measurement) (any, error)

MockReportID func() string

MockMeasureAsync func(ctx context.Context, input string) (<-chan *model.Measurement, error)

MockMeasureWithContext func(
ctx context.Context, input string) (measurement *model.Measurement, err error)

MockSaveMeasurement func(measurement *model.Measurement, filePath string) error

MockSubmitAndUpdateMeasurementContext func(
ctx context.Context, measurement *model.Measurement) error

MockOpenReportContext func(ctx context.Context) error
}

func (e *Experiment) KibiBytesReceived() float64 {
return e.MockKibiBytesReceived()
}

func (e *Experiment) KibiBytesSent() float64 {
return e.MockKibiBytesSent()
}

func (e *Experiment) Name() string {
return e.MockName()
}

func (e *Experiment) GetSummaryKeys(m *model.Measurement) (any, error) {
return e.MockGetSummaryKeys(m)
}

func (e *Experiment) ReportID() string {
return e.MockReportID()
}

func (e *Experiment) MeasureAsync(
ctx context.Context, input string) (<-chan *model.Measurement, error) {
return e.MockMeasureAsync(ctx, input)
}

func (e *Experiment) MeasureWithContext(
ctx context.Context, input string) (measurement *model.Measurement, err error) {
return e.MockMeasureWithContext(ctx, input)
}

func (e *Experiment) SaveMeasurement(measurement *model.Measurement, filePath string) error {
return e.MockSaveMeasurement(measurement, filePath)
}

func (e *Experiment) SubmitAndUpdateMeasurementContext(
ctx context.Context, measurement *model.Measurement) error {
return e.MockSubmitAndUpdateMeasurementContext(ctx, measurement)
}

func (e *Experiment) OpenReportContext(ctx context.Context) error {
return e.MockOpenReportContext(ctx)
}
146 changes: 146 additions & 0 deletions internal/model/mocks/experiment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package mocks

import (
"context"
"errors"
"testing"

"github.com/ooni/probe-cli/v3/internal/model"
)

func TestExperiment(t *testing.T) {
t.Run("KibiBytesReceived", func(t *testing.T) {
expected := 1.0
e := &Experiment{
MockKibiBytesReceived: func() float64 {
return expected
},
}
if e.KibiBytesReceived() != expected {
t.Fatal("unexpected result")
}
})

t.Run("KibiBytesSent", func(t *testing.T) {
expected := 1.0
e := &Experiment{
MockKibiBytesSent: func() float64 {
return expected
},
}
if e.KibiBytesSent() != expected {
t.Fatal("unexpected result")
}
})

t.Run("Name", func(t *testing.T) {
expected := "antani"
e := &Experiment{
MockName: func() string {
return expected
},
}
if e.Name() != expected {
t.Fatal("unexpected result")
}
})

t.Run("GetSummaryKeys", func(t *testing.T) {
expected := errors.New("mocked err")
e := &Experiment{
MockGetSummaryKeys: func(m *model.Measurement) (any, error) {
return nil, expected
},
}
out, err := e.GetSummaryKeys(&model.Measurement{})
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
if out != nil {
t.Fatal("invalid out")
}
})

t.Run("ReportID", func(t *testing.T) {
expect := "xyz"
e := &Experiment{
MockReportID: func() string {
return expect
},
}
if e.ReportID() != expect {
t.Fatal("invalid value")
}
})

t.Run("MeasureAsync", func(t *testing.T) {
expected := errors.New("mocked err")
e := &Experiment{
MockMeasureAsync: func(ctx context.Context, input string) (<-chan *model.Measurement, error) {
return nil, expected
},
}
out, err := e.MeasureAsync(context.Background(), "xo")
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
if out != nil {
t.Fatal("expected nil")
}
})

t.Run("MeasureWithContext", func(t *testing.T) {
expected := errors.New("mocked err")
e := &Experiment{
MockMeasureWithContext: func(ctx context.Context, input string) (measurement *model.Measurement, err error) {
return nil, expected
},
}
out, err := e.MeasureWithContext(context.Background(), "xo")
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
if out != nil {
t.Fatal("expected nil")
}
})

t.Run("SaveMeasurement", func(t *testing.T) {
expected := errors.New("mocked err")
e := &Experiment{
MockSaveMeasurement: func(measurement *model.Measurement, filePath string) error {
return expected
},
}
err := e.SaveMeasurement(&model.Measurement{}, "x")
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
})

t.Run("SubmitAndUpdateMeasurementContext", func(t *testing.T) {
expected := errors.New("mocked err")
e := &Experiment{
MockSubmitAndUpdateMeasurementContext: func(ctx context.Context, measurement *model.Measurement) error {
return expected
},
}
err := e.SubmitAndUpdateMeasurementContext(context.Background(), &model.Measurement{})
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
})

t.Run("OpenReportContext", func(t *testing.T) {
expected := errors.New("mocked err")
e := &Experiment{
MockOpenReportContext: func(ctx context.Context) error {
return expected
},
}
err := e.OpenReportContext(context.Background())
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
})
}
48 changes: 48 additions & 0 deletions internal/model/mocks/experimentbuilder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package mocks

import "github.com/ooni/probe-cli/v3/internal/model"

// ExperimentBuilder mocks model.ExperimentBuilder.
type ExperimentBuilder struct {
MockInterruptible func() bool

MockInputPolicy func() model.InputPolicy

MockOptions func() (map[string]model.ExperimentOptionInfo, error)

MockSetOptionAny func(key string, value any) error

MockSetOptionsAny func(options map[string]any) error

MockSetCallbacks func(callbacks model.ExperimentCallbacks)

MockNewExperiment func() model.Experiment
}

func (eb *ExperimentBuilder) Interruptible() bool {
return eb.MockInterruptible()
}

func (eb *ExperimentBuilder) InputPolicy() model.InputPolicy {
return eb.MockInputPolicy()
}

func (eb *ExperimentBuilder) Options() (map[string]model.ExperimentOptionInfo, error) {
return eb.MockOptions()
}

func (eb *ExperimentBuilder) SetOptionAny(key string, value any) error {
return eb.MockSetOptionAny(key, value)
}

func (eb *ExperimentBuilder) SetOptionsAny(options map[string]any) error {
return eb.MockSetOptionsAny(options)
}

func (eb *ExperimentBuilder) SetCallbacks(callbacks model.ExperimentCallbacks) {
eb.MockSetCallbacks(callbacks)
}

func (eb *ExperimentBuilder) NewExperiment() model.Experiment {
return eb.MockNewExperiment()
}
Loading

0 comments on commit e8e61c9

Please sign in to comment.