ooni-probe-cli/internal/oonirun/v2_test.go
Simone Basso 9a0153a349
feat(oonirun): add support for OONIRun v2 links (#844)
This diff adds support for OONIRun v2 links.

Part of https://github.com/ooni/probe/issues/2184.
2022-07-08 16:53:59 +02:00

286 lines
7.3 KiB
Go

package oonirun
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/ooni/probe-cli/v3/internal/kvstore"
"github.com/ooni/probe-cli/v3/internal/model/mocks"
"github.com/ooni/probe-cli/v3/internal/runtimex"
)
// 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 TestOONIRunV2LinkCommonCase(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
descriptor := &v2Descriptor{
Name: "",
Description: "",
Author: "",
Nettests: []v2Nettest{{
Inputs: []string{},
Options: map[string]any{
"SleepTime": int64(10 * time.Millisecond),
},
TestName: "example",
}},
}
data, err := json.Marshal(descriptor)
runtimex.PanicOnError(err, "json.Marshal failed")
w.Write(data)
}))
defer server.Close()
ctx := context.Background()
config := &LinkConfig{
AcceptChanges: true, // avoid "oonirun: need to accept changes" error
Annotations: map[string]string{
"platform": "linux",
},
KVStore: &kvstore.Memory{},
MaxRuntime: 0,
NoCollector: true,
NoJSON: true,
Random: false,
ReportFile: "",
Session: newSession(ctx, t),
}
r := NewLinkRunner(config, server.URL)
if err := r.Run(ctx); err != nil {
t.Fatal(err)
}
}
func TestOONIRunV2LinkCannotUpdateCache(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
descriptor := &v2Descriptor{
Name: "",
Description: "",
Author: "",
Nettests: []v2Nettest{{
Inputs: []string{},
Options: map[string]any{
"SleepTime": int64(10 * time.Millisecond),
},
TestName: "example",
}},
}
data, err := json.Marshal(descriptor)
runtimex.PanicOnError(err, "json.Marshal failed")
w.Write(data)
}))
defer server.Close()
ctx := context.Background()
expected := errors.New("mocked")
config := &LinkConfig{
AcceptChanges: true, // avoid "oonirun: need to accept changes" error
Annotations: map[string]string{
"platform": "linux",
},
KVStore: &mocks.KeyValueStore{
MockGet: func(key string) ([]byte, error) {
return []byte("{}"), nil
},
MockSet: func(key string, value []byte) error {
return expected
},
},
MaxRuntime: 0,
NoCollector: true,
NoJSON: true,
Random: false,
ReportFile: "",
Session: newSession(ctx, t),
}
r := NewLinkRunner(config, server.URL)
err := r.Run(ctx)
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
}
func TestOONIRunV2LinkWithoutAcceptChanges(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
descriptor := &v2Descriptor{
Name: "",
Description: "",
Author: "",
Nettests: []v2Nettest{{
Inputs: []string{},
Options: map[string]any{
"SleepTime": int64(10 * time.Millisecond),
},
TestName: "example",
}},
}
data, err := json.Marshal(descriptor)
runtimex.PanicOnError(err, "json.Marshal failed")
w.Write(data)
}))
defer server.Close()
ctx := context.Background()
config := &LinkConfig{
AcceptChanges: false, // should see "oonirun: need to accept changes" error
Annotations: map[string]string{
"platform": "linux",
},
KVStore: &kvstore.Memory{},
MaxRuntime: 0,
NoCollector: true,
NoJSON: true,
Random: false,
ReportFile: "",
Session: newSession(ctx, t),
}
r := NewLinkRunner(config, server.URL)
err := r.Run(ctx)
if !errors.Is(err, ErrNeedToAcceptChanges) {
t.Fatal("unexpected err", err)
}
}
func TestOONIRunV2LinkNilDescriptor(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("null"))
}))
defer server.Close()
ctx := context.Background()
config := &LinkConfig{
AcceptChanges: true, // avoid "oonirun: need to accept changes" error
Annotations: map[string]string{
"platform": "linux",
},
KVStore: &kvstore.Memory{},
MaxRuntime: 0,
NoCollector: true,
NoJSON: true,
Random: false,
ReportFile: "",
Session: newSession(ctx, t),
}
r := NewLinkRunner(config, server.URL)
if err := r.Run(ctx); err != nil {
t.Fatal(err)
}
}
func TestOONIRunV2LinkEmptyTestName(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
descriptor := &v2Descriptor{
Name: "",
Description: "",
Author: "",
Nettests: []v2Nettest{{
Inputs: []string{},
Options: map[string]any{
"SleepTime": int64(10 * time.Millisecond),
},
TestName: "", // empty!
}},
}
data, err := json.Marshal(descriptor)
runtimex.PanicOnError(err, "json.Marshal failed")
w.Write(data)
}))
defer server.Close()
ctx := context.Background()
config := &LinkConfig{
AcceptChanges: true, // avoid "oonirun: need to accept changes" error
Annotations: map[string]string{
"platform": "linux",
},
KVStore: &kvstore.Memory{},
MaxRuntime: 0,
NoCollector: true,
NoJSON: true,
Random: false,
ReportFile: "",
Session: newSession(ctx, t),
}
r := NewLinkRunner(config, server.URL)
if err := r.Run(ctx); err != nil {
t.Fatal(err)
}
if v2CountEmptyNettestNames.Load() != 1 {
t.Fatal("expected to see 1 instance of empty nettest names")
}
}
func TestV2MeasureDescriptor(t *testing.T) {
t.Run("with nil descriptor", func(t *testing.T) {
ctx := context.Background()
config := &LinkConfig{}
err := v2MeasureDescriptor(ctx, config, nil)
if !errors.Is(err, ErrNilDescriptor) {
t.Fatal("unexpected err", err)
}
})
}
func TestV2MeasureHTTPS(t *testing.T) {
t.Run("when we cannot load from cache", func(t *testing.T) {
expected := errors.New("mocked error")
ctx := context.Background()
config := &LinkConfig{
AcceptChanges: false,
Annotations: map[string]string{},
KVStore: &mocks.KeyValueStore{
MockGet: func(key string) (value []byte, err error) {
return nil, expected
},
},
MaxRuntime: 0,
NoCollector: false,
NoJSON: false,
Random: false,
ReportFile: "",
Session: newSession(ctx, t),
}
err := v2MeasureHTTPS(ctx, config, "")
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
})
t.Run("when we cannot pull changes", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel() // fail immediately
config := &LinkConfig{
AcceptChanges: false,
Annotations: map[string]string{},
KVStore: &kvstore.Memory{},
MaxRuntime: 0,
NoCollector: false,
NoJSON: false,
Random: false,
ReportFile: "",
Session: newSession(ctx, t),
}
err := v2MeasureHTTPS(ctx, config, "https://example.com") // should not use URL
if !errors.Is(err, context.Canceled) {
t.Fatal("unexpected err", err)
}
})
}
func TestV2DescriptorCacheLoad(t *testing.T) {
t.Run("cannot unmarshal cache content", func(t *testing.T) {
fsstore := &kvstore.Memory{}
if err := fsstore.Set(v2DescriptorCacheKey, []byte("{")); err != nil {
t.Fatal(err)
}
cache, err := v2DescriptorCacheLoad(fsstore)
if err == nil || err.Error() != "unexpected end of JSON input" {
t.Fatal("unexpected err", err)
}
if cache != nil {
t.Fatal("expected nil cache")
}
})
}