d3c5196474
This diff changes the software name used by unattended runs for which we did not override the default software name (`ooniprobe-cli`). It will become `ooniprobe-cli-unattended`. This software name is in line with the one we use for Android, iOS, and desktop unattended runs. While working in this diff, I introduced string constants for the run types and a string constant for the default software name. See https://github.com/ooni/probe/issues/2081.
294 lines
8.1 KiB
Go
294 lines
8.1 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/url"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/apex/log"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/ooni/probe-cli/v3/internal/engine/geolocate"
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
|
)
|
|
|
|
func (s *Session) GetAvailableProbeServices() []model.OOAPIService {
|
|
return s.getAvailableProbeServicesUnlocked()
|
|
}
|
|
|
|
func (s *Session) AppendAvailableProbeService(svc model.OOAPIService) {
|
|
s.availableProbeServices = append(s.availableProbeServices, svc)
|
|
}
|
|
|
|
func (s *Session) QueryProbeServicesCount() int64 {
|
|
return s.queryProbeServicesCount.Load()
|
|
}
|
|
|
|
// mockableProbeServicesClientForCheckIn allows us to mock the
|
|
// probeservices.Client used by Session.CheckIn.
|
|
type mockableProbeServicesClientForCheckIn struct {
|
|
// Config is the config passed to the call.
|
|
Config *model.OOAPICheckInConfig
|
|
|
|
// Results contains the results of the call. This field MUST be
|
|
// non-nil if and only if Error is nil.
|
|
Results *model.OOAPICheckInInfo
|
|
|
|
// Error indicates whether the call failed. This field MUST be
|
|
// non-nil if and only if Error is nil.
|
|
Error error
|
|
|
|
// mu provides mutual exclusion.
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// CheckIn implements sessionProbeServicesClientForCheckIn.CheckIn.
|
|
func (c *mockableProbeServicesClientForCheckIn) CheckIn(
|
|
ctx context.Context, config model.OOAPICheckInConfig) (*model.OOAPICheckInInfo, error) {
|
|
defer c.mu.Unlock()
|
|
c.mu.Lock()
|
|
if c.Config != nil {
|
|
return nil, errors.New("called more than once")
|
|
}
|
|
c.Config = &config
|
|
if c.Results == nil && c.Error == nil {
|
|
return nil, errors.New("misconfigured mockableProbeServicesClientForCheckIn")
|
|
}
|
|
return c.Results, c.Error
|
|
}
|
|
|
|
func TestSessionCheckInSuccessful(t *testing.T) {
|
|
results := &model.OOAPICheckInInfo{
|
|
WebConnectivity: &model.OOAPICheckInInfoWebConnectivity{
|
|
ReportID: "xxx-x-xx",
|
|
URLs: []model.OOAPIURLInfo{{
|
|
CategoryCode: "NEWS",
|
|
CountryCode: "IT",
|
|
URL: "https://www.repubblica.it/",
|
|
}, {
|
|
CategoryCode: "NEWS",
|
|
CountryCode: "IT",
|
|
URL: "https://www.unita.it/",
|
|
}},
|
|
},
|
|
}
|
|
mockedClnt := &mockableProbeServicesClientForCheckIn{
|
|
Results: results,
|
|
}
|
|
s := &Session{
|
|
location: &geolocate.Results{
|
|
ASN: 137,
|
|
CountryCode: "IT",
|
|
},
|
|
softwareName: "miniooni",
|
|
softwareVersion: "0.1.0-dev",
|
|
testMaybeLookupLocationContext: func(ctx context.Context) error {
|
|
return nil
|
|
},
|
|
testNewProbeServicesClientForCheckIn: func(
|
|
ctx context.Context) (sessionProbeServicesClientForCheckIn, error) {
|
|
return mockedClnt, nil
|
|
},
|
|
}
|
|
out, err := s.CheckIn(context.Background(), &model.OOAPICheckInConfig{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(results, out); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
if mockedClnt.Config.Platform != s.Platform() {
|
|
t.Fatal("invalid Config.Platform")
|
|
}
|
|
if mockedClnt.Config.ProbeASN != "AS137" {
|
|
t.Fatal("invalid Config.ProbeASN")
|
|
}
|
|
if mockedClnt.Config.ProbeCC != "IT" {
|
|
t.Fatal("invalid Config.ProbeCC")
|
|
}
|
|
if mockedClnt.Config.RunType != model.RunTypeTimed {
|
|
t.Fatal("invalid Config.RunType")
|
|
}
|
|
if mockedClnt.Config.SoftwareName != "miniooni" {
|
|
t.Fatal("invalid Config.SoftwareName")
|
|
}
|
|
if mockedClnt.Config.SoftwareVersion != "0.1.0-dev" {
|
|
t.Fatal("invalid Config.SoftwareVersion")
|
|
}
|
|
if mockedClnt.Config.WebConnectivity.CategoryCodes == nil {
|
|
t.Fatal("invalid ...CategoryCodes")
|
|
}
|
|
}
|
|
|
|
func TestSessionCheckInCannotLookupLocation(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
s := &Session{
|
|
testMaybeLookupLocationContext: func(ctx context.Context) error {
|
|
return errMocked
|
|
},
|
|
}
|
|
out, err := s.CheckIn(context.Background(), &model.OOAPICheckInConfig{})
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("no the error we expected", err)
|
|
}
|
|
if out != nil {
|
|
t.Fatal("expected nil result here")
|
|
}
|
|
}
|
|
|
|
func TestSessionCheckInCannotCreateProbeServicesClient(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
s := &Session{
|
|
location: &geolocate.Results{
|
|
ASN: 137,
|
|
CountryCode: "IT",
|
|
},
|
|
softwareName: "miniooni",
|
|
softwareVersion: "0.1.0-dev",
|
|
testMaybeLookupLocationContext: func(ctx context.Context) error {
|
|
return nil
|
|
},
|
|
testNewProbeServicesClientForCheckIn: func(
|
|
ctx context.Context) (sessionProbeServicesClientForCheckIn, error) {
|
|
return nil, errMocked
|
|
},
|
|
}
|
|
out, err := s.CheckIn(context.Background(), &model.OOAPICheckInConfig{})
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("no the error we expected", err)
|
|
}
|
|
if out != nil {
|
|
t.Fatal("expected nil result here")
|
|
}
|
|
}
|
|
|
|
func TestLowercaseMaybeLookupLocationContextWithCancelledContext(t *testing.T) {
|
|
s := &Session{}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // immediately kill the context
|
|
err := s.maybeLookupLocationContext(ctx)
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
}
|
|
|
|
func TestNewProbeServicesClientForCheckIn(t *testing.T) {
|
|
s := &Session{}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // immediately kill the context
|
|
clnt, err := s.newProbeServicesClientForCheckIn(ctx)
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if clnt != nil {
|
|
t.Fatal("expected nil client here")
|
|
}
|
|
}
|
|
|
|
func TestSessionNewSubmitterWithCancelledContext(t *testing.T) {
|
|
sess := newSessionForTesting(t)
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // fail immediately
|
|
subm, err := sess.NewSubmitter(ctx)
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if subm != nil {
|
|
t.Fatal("expected nil submitter here")
|
|
}
|
|
}
|
|
|
|
func TestSessionMaybeLookupLocationContextLookupLocationContextFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
sess := newSessionForTestingNoLookups(t)
|
|
sess.testLookupLocationContext = func(ctx context.Context) (*geolocate.Results, error) {
|
|
return nil, errMocked
|
|
}
|
|
err := sess.MaybeLookupLocationContext(context.Background())
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
}
|
|
|
|
func TestSessionFetchURLListWithCancelledContext(t *testing.T) {
|
|
sess := &Session{}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // cause failure
|
|
resp, err := sess.FetchURLList(ctx, model.OOAPIURLListConfig{})
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil response here")
|
|
}
|
|
}
|
|
|
|
func TestSessionFetchTorTargetsWithCancelledContext(t *testing.T) {
|
|
sess := &Session{}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // cause failure
|
|
resp, err := sess.FetchTorTargets(ctx, "IT")
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil response here")
|
|
}
|
|
}
|
|
|
|
func TestSessionFetchPsiphonConfigWithCancelledContext(t *testing.T) {
|
|
sess := &Session{}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // cause failure
|
|
resp, err := sess.FetchPsiphonConfig(ctx)
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil response here")
|
|
}
|
|
}
|
|
|
|
func TestNewSessionWithFakeTunnel(t *testing.T) {
|
|
ctx := context.Background()
|
|
sess, err := NewSession(ctx, SessionConfig{
|
|
Logger: log.Log,
|
|
ProxyURL: &url.URL{Scheme: "fake"},
|
|
SoftwareName: "miniooni",
|
|
SoftwareVersion: "0.1.0-dev",
|
|
TunnelDir: "testdata",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if sess == nil {
|
|
t.Fatal("expected non-nil session here")
|
|
}
|
|
if sess.ProxyURL() == nil {
|
|
t.Fatal("expected non-nil proxyURL here")
|
|
}
|
|
if sess.tunnel == nil {
|
|
t.Fatal("expected non-nil tunnel here")
|
|
}
|
|
sess.Close() // ensure we don't crash
|
|
}
|
|
|
|
func TestNewSessionWithFakeTunnelAndCancelledContext(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // fail immediately
|
|
sess, err := NewSession(ctx, SessionConfig{
|
|
Logger: log.Log,
|
|
ProxyURL: &url.URL{Scheme: "fake"},
|
|
SoftwareName: "miniooni",
|
|
SoftwareVersion: "0.1.0-dev",
|
|
TunnelDir: "testdata",
|
|
})
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if sess != nil {
|
|
t.Fatal("expected nil session here")
|
|
}
|
|
}
|