ooni-probe-cli/internal/engine/session_internal_test.go

294 lines
8.1 KiB
Go
Raw Permalink Normal View History

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")
}
}