a5f4fc997c
* doc: merge the engine and the cli readmes Part of https://github.com/ooni/probe/issues/1335 * refactor: we don't wanna export pkg/oonimkall/tasks See https://github.com/ooni/probe/issues/1335
405 lines
10 KiB
Go
405 lines
10 KiB
Go
package tasks_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ooni/probe-cli/v3/pkg/oonimkall/internal/tasks"
|
|
)
|
|
|
|
func TestRunnerMaybeLookupBackendsFailure(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(500)
|
|
}))
|
|
defer server.Close()
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
Name: "Example",
|
|
Options: tasks.SettingsOptions{
|
|
ProbeServicesBaseURL: server.URL,
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "../../testdata/oonimkall/state",
|
|
Version: 1,
|
|
}
|
|
go func() {
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
}()
|
|
var failures []string
|
|
for ev := range out {
|
|
switch ev.Key {
|
|
case "failure.startup":
|
|
failure := ev.Value.(tasks.EventFailure).Failure
|
|
failures = append(failures, failure)
|
|
case "status.queued", "status.started", "log", "status.end":
|
|
default:
|
|
panic(fmt.Sprintf("unexpected key: %s", ev.Key))
|
|
}
|
|
}
|
|
if len(failures) != 1 {
|
|
t.Fatal("unexpected number of failures")
|
|
}
|
|
if failures[0] != "all available probe services failed" {
|
|
t.Fatal("invalid failure")
|
|
}
|
|
}
|
|
|
|
func TestRunnerOpenReportFailure(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
var (
|
|
nreq int64
|
|
mu sync.Mutex
|
|
)
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
nreq++
|
|
if nreq == 1 {
|
|
w.Write([]byte(`{}`))
|
|
return
|
|
}
|
|
w.WriteHeader(500)
|
|
}))
|
|
defer server.Close()
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
Name: "Example",
|
|
Options: tasks.SettingsOptions{
|
|
ProbeServicesBaseURL: server.URL,
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "../../testdata/oonimkall/state",
|
|
Version: 1,
|
|
}
|
|
seench := make(chan int64)
|
|
go func() {
|
|
var seen int64
|
|
for ev := range out {
|
|
switch ev.Key {
|
|
case "failure.report_create":
|
|
seen++
|
|
case "status.progress":
|
|
evv := ev.Value.(tasks.EventStatusProgress)
|
|
if evv.Percentage >= 0.4 {
|
|
panic(fmt.Sprintf("too much progress: %+v", ev))
|
|
}
|
|
case "status.queued", "status.started", "log", "status.end",
|
|
"status.geoip_lookup", "status.resolver_lookup":
|
|
default:
|
|
panic(fmt.Sprintf("unexpected key: %s", ev.Key))
|
|
}
|
|
}
|
|
seench <- seen
|
|
}()
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
if n := <-seench; n != 1 {
|
|
t.Fatal("unexpected number of events")
|
|
}
|
|
}
|
|
|
|
func TestRunnerGood(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
LogLevel: "DEBUG",
|
|
Name: "Example",
|
|
Options: tasks.SettingsOptions{
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "../../testdata/oonimkall/state",
|
|
Version: 1,
|
|
}
|
|
go func() {
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
}()
|
|
var found bool
|
|
for ev := range out {
|
|
if ev.Key == "status.end" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("status.end event not found")
|
|
}
|
|
}
|
|
|
|
func TestRunnerWithUnsupportedSettings(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
LogLevel: "DEBUG",
|
|
Name: "Example",
|
|
Options: tasks.SettingsOptions{
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "../../testdata/oonimkall/state",
|
|
}
|
|
go func() {
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
}()
|
|
var failures []string
|
|
for ev := range out {
|
|
if ev.Key == "failure.startup" {
|
|
failure := ev.Value.(tasks.EventFailure).Failure
|
|
failures = append(failures, failure)
|
|
}
|
|
}
|
|
if len(failures) != 1 {
|
|
t.Fatal("invalid number of failures")
|
|
}
|
|
if failures[0] != tasks.FailureInvalidVersion {
|
|
t.Fatal("not the failure we expected")
|
|
}
|
|
}
|
|
|
|
func TestRunnerWithInvalidKVStorePath(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
LogLevel: "DEBUG",
|
|
Name: "Example",
|
|
Options: tasks.SettingsOptions{
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "", // must be empty to cause the failure below
|
|
Version: 1,
|
|
}
|
|
go func() {
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
}()
|
|
var failures []string
|
|
for ev := range out {
|
|
if ev.Key == "failure.startup" {
|
|
failure := ev.Value.(tasks.EventFailure).Failure
|
|
failures = append(failures, failure)
|
|
}
|
|
}
|
|
if len(failures) != 1 {
|
|
t.Fatal("invalid number of failures")
|
|
}
|
|
if failures[0] != "mkdir : no such file or directory" {
|
|
t.Fatal("not the failure we expected")
|
|
}
|
|
}
|
|
|
|
func TestRunnerWithInvalidExperimentName(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
LogLevel: "DEBUG",
|
|
Name: "Nonexistent",
|
|
Options: tasks.SettingsOptions{
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "../../testdata/oonimkall/state",
|
|
Version: 1,
|
|
}
|
|
go func() {
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
}()
|
|
var failures []string
|
|
for ev := range out {
|
|
if ev.Key == "failure.startup" {
|
|
failure := ev.Value.(tasks.EventFailure).Failure
|
|
failures = append(failures, failure)
|
|
}
|
|
}
|
|
if len(failures) != 1 {
|
|
t.Fatal("invalid number of failures")
|
|
}
|
|
if failures[0] != "no such experiment: Nonexistent" {
|
|
t.Fatalf("not the failure we expected: %s", failures[0])
|
|
}
|
|
}
|
|
|
|
func TestRunnerWithMissingInput(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
LogLevel: "DEBUG",
|
|
Name: "ExampleWithInput",
|
|
Options: tasks.SettingsOptions{
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "../../testdata/oonimkall/state",
|
|
Version: 1,
|
|
}
|
|
go func() {
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
}()
|
|
var failures []string
|
|
for ev := range out {
|
|
if ev.Key == "failure.startup" {
|
|
failure := ev.Value.(tasks.EventFailure).Failure
|
|
failures = append(failures, failure)
|
|
}
|
|
}
|
|
if len(failures) != 1 {
|
|
t.Fatal("invalid number of failures")
|
|
}
|
|
if failures[0] != "no input provided" {
|
|
t.Fatalf("not the failure we expected: %s", failures[0])
|
|
}
|
|
}
|
|
|
|
func TestRunnerWithMaxRuntime(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
Inputs: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i"},
|
|
LogLevel: "DEBUG",
|
|
Name: "ExampleWithInput",
|
|
Options: tasks.SettingsOptions{
|
|
MaxRuntime: 1,
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "../../testdata/oonimkall/state",
|
|
Version: 1,
|
|
}
|
|
begin := time.Now()
|
|
go func() {
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
}()
|
|
var found bool
|
|
for ev := range out {
|
|
if ev.Key == "status.end" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("status.end event not found")
|
|
}
|
|
// The runtime is long because of ancillary operations and is even more
|
|
// longer because of self shaping we may be performing (especially in
|
|
// CI builds) using `-tags shaping`). We have experimentally determined
|
|
// that ~10 seconds is the typical CI test run time. See:
|
|
//
|
|
// 1. https://github.com/ooni/probe-cli/v3/internal/engine/pull/588/checks?check_run_id=667263788
|
|
//
|
|
// 2. https://github.com/ooni/probe-cli/v3/internal/engine/pull/588/checks?check_run_id=667263855
|
|
if time.Now().Sub(begin) > 10*time.Second {
|
|
t.Fatal("expected shorter runtime")
|
|
}
|
|
}
|
|
|
|
func TestRunnerWithMaxRuntimeNonInterruptible(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
Inputs: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i"},
|
|
LogLevel: "DEBUG",
|
|
Name: "ExampleWithInputNonInterruptible",
|
|
Options: tasks.SettingsOptions{
|
|
MaxRuntime: 1,
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "../../testdata/oonimkall/state",
|
|
Version: 1,
|
|
}
|
|
begin := time.Now()
|
|
go func() {
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
}()
|
|
var found bool
|
|
for ev := range out {
|
|
if ev.Key == "status.end" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("status.end event not found")
|
|
}
|
|
// The runtime is long because of ancillary operations and is even more
|
|
// longer because of self shaping we may be performing (especially in
|
|
// CI builds) using `-tags shaping`). We have experimentally determined
|
|
// that ~10 seconds is the typical CI test run time. See:
|
|
//
|
|
// 1. https://github.com/ooni/probe-cli/v3/internal/engine/pull/588/checks?check_run_id=667263788
|
|
//
|
|
// 2. https://github.com/ooni/probe-cli/v3/internal/engine/pull/588/checks?check_run_id=667263855
|
|
if time.Now().Sub(begin) > 10*time.Second {
|
|
t.Fatal("expected shorter runtime")
|
|
}
|
|
}
|
|
|
|
func TestRunnerWithFailedMeasurement(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
out := make(chan *tasks.Event)
|
|
settings := &tasks.Settings{
|
|
AssetsDir: "../../testdata/oonimkall/assets",
|
|
Inputs: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i"},
|
|
LogLevel: "DEBUG",
|
|
Name: "ExampleWithFailure",
|
|
Options: tasks.SettingsOptions{
|
|
MaxRuntime: 1,
|
|
SoftwareName: "oonimkall-test",
|
|
SoftwareVersion: "0.1.0",
|
|
},
|
|
StateDir: "../../testdata/oonimkall/state",
|
|
Version: 1,
|
|
}
|
|
go func() {
|
|
tasks.Run(context.Background(), settings, out)
|
|
close(out)
|
|
}()
|
|
var found bool
|
|
for ev := range out {
|
|
if ev.Key == "failure.measurement" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("failure.measurement event not found")
|
|
}
|
|
}
|