@@ -22,58 +22,71 @@ package clientlib
2222import (
2323 "context"
2424 "encoding/json"
25+ "errors"
2526 "os"
27+ "strings"
2628 "testing"
2729 "time"
2830
2931 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
3032 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
3133)
3234
33- func TestStartTunnel (t * testing.T ) {
34- // TODO: More comprehensive tests. This is only a smoke test.
35-
36- clientPlatform := "clientlib_test.go"
37- networkID := "UNKNOWN"
38- timeout := 60
39- quickTimeout := 1
40- trueVal := true
41-
35+ func setupConfig (t * testing.T , disableFetcher bool ) []byte {
4236 configJSON , err := os .ReadFile ("../../psiphon/controller_test.config" )
4337 if err != nil {
44- // Skip, don't fail, if config file is not present
38+ // What to do if config file is not present?
4539 t .Skipf ("error loading configuration file: %s" , err )
4640 }
4741
48- // Initialize a fresh datastore and create a modified config which cannot
49- // connect without known servers, to be used in timeout cases.
50-
51- testDataDirName , err := os .MkdirTemp ("" , "psiphon-clientlib-test" )
52- if err != nil {
53- t .Fatalf ("ioutil.TempDir failed: %v" , err )
54- }
55- defer os .RemoveAll (testDataDirName )
56-
5742 var config map [string ]interface {}
5843 err = json .Unmarshal (configJSON , & config )
5944 if err != nil {
6045 t .Fatalf ("json.Unmarshal failed: %v" , err )
6146 }
6247
63- // Use the legacy encoding to both exercise that case, and facilitate a
64- // gradual network upgrade to new encoding support.
48+ // Use the legacy encoding to both exercise that case, and facilitate a gradual
49+ // network upgrade to new encoding support.
6550 config ["TargetAPIEncoding" ] = protocol .PSIPHON_API_ENCODING_JSON
6651
52+ if disableFetcher {
53+ config ["DisableRemoteServerListFetcher" ] = true
54+ }
55+
6756 configJSON , err = json .Marshal (config )
6857 if err != nil {
6958 t .Fatalf ("json.Marshal failed: %v" , err )
7059 }
7160
72- config ["DisableRemoteServerListFetcher" ] = true
61+ return configJSON
62+ }
63+
64+ func TestStartTunnel (t * testing.T ) {
65+ // TODO: More comprehensive tests. This is only a smoke test.
66+
67+ configJSON := setupConfig (t , false )
68+ configJSONNoFetcher := setupConfig (t , true )
7369
74- configJSONNoFetcher , err := json .Marshal (config )
70+ clientPlatform := "clientlib_test.go"
71+ networkID := "UNKNOWN"
72+ timeout := 60
73+ quickTimeout := 1
74+ trueVal := true
75+
76+ // Initialize a fresh datastore and create a modified config which cannot
77+ // connect without known servers, to be used in timeout cases.
78+
79+ testDataDirName , err := os .MkdirTemp ("" , "psiphon-clientlib-test" )
7580 if err != nil {
76- t .Fatalf ("json.Marshal failed: %v" , err )
81+ t .Fatalf ("ioutil.TempDir failed: %v" , err )
82+ }
83+ defer os .RemoveAll (testDataDirName )
84+
85+ paramsDeltaErr := func (err error ) bool {
86+ return strings .Contains (err .Error (), "SetParameters failed for delta" )
87+ }
88+ timeoutErr := func (err error ) bool {
89+ return errors .Is (err , ErrTimeout )
7790 }
7891
7992 type args struct {
@@ -88,7 +101,7 @@ func TestStartTunnel(t *testing.T) {
88101 name string
89102 args args
90103 wantTunnel bool
91- expectedErr error
104+ expectedErr func ( error ) bool
92105 }{
93106 {
94107 name : "Failure: context timeout" ,
@@ -106,7 +119,7 @@ func TestStartTunnel(t *testing.T) {
106119 noticeReceiver : nil ,
107120 },
108121 wantTunnel : false ,
109- expectedErr : ErrTimeout ,
122+ expectedErr : timeoutErr ,
110123 },
111124 {
112125 name : "Failure: config timeout" ,
@@ -124,7 +137,7 @@ func TestStartTunnel(t *testing.T) {
124137 noticeReceiver : nil ,
125138 },
126139 wantTunnel : false ,
127- expectedErr : ErrTimeout ,
140+ expectedErr : timeoutErr ,
128141 },
129142 {
130143 name : "Success: simple" ,
@@ -202,6 +215,42 @@ func TestStartTunnel(t *testing.T) {
202215 wantTunnel : true ,
203216 expectedErr : nil ,
204217 },
218+ {
219+ name : "Success: good ParametersDelta" ,
220+ args : args {
221+ ctxTimeout : 0 ,
222+ configJSON : configJSON ,
223+ embeddedServerEntryList : "" ,
224+ params : Parameters {
225+ DataRootDirectory : & testDataDirName ,
226+ ClientPlatform : & clientPlatform ,
227+ NetworkID : & networkID ,
228+ EstablishTunnelTimeoutSeconds : & timeout ,
229+ },
230+ paramsDelta : ParametersDelta {"NetworkLatencyMultiplierMin" : 1 },
231+ noticeReceiver : nil ,
232+ },
233+ wantTunnel : true ,
234+ expectedErr : nil ,
235+ },
236+ {
237+ name : "Failure: bad ParametersDelta" ,
238+ args : args {
239+ ctxTimeout : 0 ,
240+ configJSON : configJSON ,
241+ embeddedServerEntryList : "" ,
242+ params : Parameters {
243+ DataRootDirectory : & testDataDirName ,
244+ ClientPlatform : & clientPlatform ,
245+ NetworkID : & networkID ,
246+ EstablishTunnelTimeoutSeconds : & timeout ,
247+ },
248+ paramsDelta : ParametersDelta {"invalidParam" : 1 },
249+ noticeReceiver : nil ,
250+ },
251+ wantTunnel : false ,
252+ expectedErr : paramsDeltaErr ,
253+ },
205254 }
206255 for _ , tt := range tests {
207256 t .Run (tt .name , func (t * testing.T ) {
@@ -234,8 +283,16 @@ func TestStartTunnel(t *testing.T) {
234283 t .Errorf ("StartTunnel() gotTunnel = %v, wantTunnel %v" , err , tt .wantTunnel )
235284 }
236285
237- if err != tt .expectedErr {
238- t .Fatalf ("StartTunnel() error = %v, expectedErr %v" , err , tt .expectedErr )
286+ if tt .expectedErr == nil {
287+ if err != nil {
288+ t .Fatalf ("StartTunnel() returned unexpected error: %v" , err )
289+ }
290+ } else if ! tt .expectedErr (err ) {
291+ t .Fatalf ("StartTunnel() error: %v" , err )
292+ return
293+ }
294+
295+ if err != nil {
239296 return
240297 }
241298
@@ -267,27 +324,7 @@ func TestStartTunnel(t *testing.T) {
267324}
268325
269326func TestMultipleStartTunnel (t * testing.T ) {
270- configJSON , err := os .ReadFile ("../../psiphon/controller_test.config" )
271- if err != nil {
272- // What to do if config file is not present?
273- t .Skipf ("error loading configuration file: %s" , err )
274- }
275-
276- var config map [string ]interface {}
277- err = json .Unmarshal (configJSON , & config )
278- if err != nil {
279- t .Fatalf ("json.Unmarshal failed: %v" , err )
280- }
281-
282- // Use the legacy encoding to both exercise that case, and facilitate a
283- // gradual network upgrade to new encoding support.
284- config ["TargetAPIEncoding" ] = protocol .PSIPHON_API_ENCODING_JSON
285-
286- configJSON , err = json .Marshal (config )
287- if err != nil {
288- t .Fatalf ("json.Marshal failed: %v" , err )
289- }
290-
327+ configJSON := setupConfig (t , false )
291328 testDataDirName , err := os .MkdirTemp ("" , "psiphon-clientlib-test" )
292329 if err != nil {
293330 t .Fatalf ("ioutil.TempDir failed: %v" , err )
@@ -340,27 +377,8 @@ func TestMultipleStartTunnel(t *testing.T) {
340377}
341378
342379func TestPsiphonTunnel_Dial (t * testing.T ) {
380+ configJSON := setupConfig (t , false )
343381 trueVal := true
344- configJSON , err := os .ReadFile ("../../psiphon/controller_test.config" )
345- if err != nil {
346- // Skip, don't fail, if config file is not present
347- t .Skipf ("error loading configuration file: %s" , err )
348- }
349-
350- var config map [string ]interface {}
351- err = json .Unmarshal (configJSON , & config )
352- if err != nil {
353- t .Fatalf ("json.Unmarshal failed: %v" , err )
354- }
355-
356- // Use the legacy encoding to both exercise that case, and facilitate a
357- // gradual network upgrade to new encoding support.
358- config ["TargetAPIEncoding" ] = protocol .PSIPHON_API_ENCODING_JSON
359-
360- configJSON , err = json .Marshal (config )
361- if err != nil {
362- t .Fatalf ("json.Marshal failed: %v" , err )
363- }
364382
365383 testDataDirName , err := os .MkdirTemp ("" , "psiphon-clientlib-test" )
366384 if err != nil {
@@ -372,9 +390,10 @@ func TestPsiphonTunnel_Dial(t *testing.T) {
372390 remoteAddr string
373391 }
374392 tests := []struct {
375- name string
376- args args
377- wantErr bool
393+ name string
394+ args args
395+ wantErr bool
396+ tunnelStopped bool
378397 }{
379398 {
380399 name : "Success: example.com" ,
@@ -386,6 +405,12 @@ func TestPsiphonTunnel_Dial(t *testing.T) {
386405 args : args {remoteAddr : "example.com:99999" },
387406 wantErr : true ,
388407 },
408+ {
409+ name : "Failure: tunnel not started" ,
410+ args : args {remoteAddr : "example.com:443" },
411+ wantErr : true ,
412+ tunnelStopped : true ,
413+ },
389414 }
390415 for _ , tt := range tests {
391416 t .Run (tt .name , func (t * testing.T ) {
@@ -407,6 +432,10 @@ func TestPsiphonTunnel_Dial(t *testing.T) {
407432 }
408433 defer tunnel .Stop ()
409434
435+ if tt .tunnelStopped {
436+ tunnel .Stop ()
437+ }
438+
410439 conn , err := tunnel .Dial (tt .args .remoteAddr )
411440 if (err != nil ) != tt .wantErr {
412441 t .Fatalf ("PsiphonTunnel.Dial() error = %v, wantErr %v" , err , tt .wantErr )
@@ -423,33 +452,14 @@ func TestPsiphonTunnel_Dial(t *testing.T) {
423452// We had a problem where config-related notices were being printed to stderr before we
424453// set the NoticeWriter. We want to make sure that no longer happens.
425454func TestStartTunnelNoOutput (t * testing.T ) {
426- configJSON , err := os .ReadFile ("../../psiphon/controller_test.config" )
427- if err != nil {
428- // What to do if config file is not present?
429- t .Skipf ("error loading configuration file: %s" , err )
430- }
431-
432- var config map [string ]interface {}
433- err = json .Unmarshal (configJSON , & config )
434- if err != nil {
435- t .Fatalf ("json.Unmarshal failed: %v" , err )
436- }
437-
438455 // Before starting the tunnel, set up a notice receiver. If it receives anything at
439456 // all, that means that it would have been printed to stderr.
440457 psiphon .SetNoticeWriter (psiphon .NewNoticeReceiver (
441458 func (notice []byte ) {
442459 t .Fatalf ("Received notice: %v" , string (notice ))
443460 }))
444461
445- // Use the legacy encoding to both exercise that case, and facilitate a
446- // gradual network upgrade to new encoding support.
447- config ["TargetAPIEncoding" ] = protocol .PSIPHON_API_ENCODING_JSON
448-
449- configJSON , err = json .Marshal (config )
450- if err != nil {
451- t .Fatalf ("json.Marshal failed: %v" , err )
452- }
462+ configJSON := setupConfig (t , false )
453463
454464 testDataDirName , err := os .MkdirTemp ("" , "psiphon-clientlib-test" )
455465 if err != nil {
@@ -499,26 +509,7 @@ func TestStartTunnelReentry(t *testing.T) {
499509 }
500510
501511 // Call again with a good config. Should work.
502- configJSON , err = os .ReadFile ("../../psiphon/controller_test.config" )
503- if err != nil {
504- // What to do if config file is not present?
505- t .Skipf ("error loading configuration file: %s" , err )
506- }
507-
508- var config map [string ]interface {}
509- err = json .Unmarshal (configJSON , & config )
510- if err != nil {
511- t .Fatalf ("json.Unmarshal failed: %v" , err )
512- }
513-
514- // Use the legacy encoding to both exercise that case, and facilitate a
515- // gradual network upgrade to new encoding support.
516- config ["TargetAPIEncoding" ] = protocol .PSIPHON_API_ENCODING_JSON
517-
518- configJSON , err = json .Marshal (config )
519- if err != nil {
520- t .Fatalf ("json.Marshal failed: %v" , err )
521- }
512+ configJSON = setupConfig (t , false )
522513
523514 tunnel , err := StartTunnel (
524515 ctx ,
0 commit comments