@@ -2,6 +2,8 @@ package cmd
22
33import (
44 "bytes"
5+ "context"
6+ "crypto/x509"
57 "encoding/json"
68 "io"
79 "net/http"
@@ -13,10 +15,12 @@ import (
1315
1416 "github.com/akamensky/argparse"
1517
18+ speedconfig "github.com/nxtrace/NTrace-core/internal/speedtest/config"
1619 "github.com/nxtrace/NTrace-core/internal/speedtest/netx"
1720 "github.com/nxtrace/NTrace-core/internal/speedtest/provider/apple"
1821 "github.com/nxtrace/NTrace-core/internal/speedtest/provider/cloudflare"
1922 "github.com/nxtrace/NTrace-core/internal/speedtest/result"
23+ "github.com/nxtrace/NTrace-core/util"
2024)
2125
2226func TestRegisterSpeedFlagWithAvailabilityEnabledAddsSingleHelpEntry (t * testing.T ) {
@@ -79,6 +83,70 @@ func TestRunSpeedModeRejectsUnexpectedTarget(t *testing.T) {
7983 }
8084}
8185
86+ type fakeSpeedMetadataBackend struct {
87+ closed bool
88+ }
89+
90+ func (f * fakeSpeedMetadataBackend ) Close () {
91+ f .closed = true
92+ }
93+
94+ func TestStartSpeedMetadataBackendHonorsNoMetadata (t * testing.T ) {
95+ calls := 0
96+ prev := newSpeedMetadataBackend
97+ newSpeedMetadataBackend = func (context.Context ) speedMetadataBackend {
98+ calls ++
99+ return & fakeSpeedMetadataBackend {}
100+ }
101+ defer func () { newSpeedMetadataBackend = prev }()
102+
103+ if backend := startSpeedMetadataBackend (context .Background (), & speedconfig.Config {NoMetadata : true }); backend != nil {
104+ t .Fatalf ("backend = %#v, want nil when metadata is disabled" , backend )
105+ }
106+ if calls != 0 {
107+ t .Fatalf ("metadata backend calls = %d, want 0" , calls )
108+ }
109+ }
110+
111+ func TestStartSpeedMetadataBackendClosesWhenEnabled (t * testing.T ) {
112+ fake := & fakeSpeedMetadataBackend {}
113+ prev := newSpeedMetadataBackend
114+ newSpeedMetadataBackend = func (context.Context ) speedMetadataBackend {
115+ return fake
116+ }
117+ defer func () { newSpeedMetadataBackend = prev }()
118+
119+ backend := startSpeedMetadataBackend (context .Background (), & speedconfig.Config {})
120+ if backend != fake {
121+ t .Fatalf ("backend = %#v, want fake backend" , backend )
122+ }
123+ closeSpeedMetadataBackend (backend )
124+ if ! fake .closed {
125+ t .Fatal ("metadata backend was not closed" )
126+ }
127+ }
128+
129+ func TestSuppressSpeedMetadataOutputOnlyForJSONMetadata (t * testing.T ) {
130+ orig := util .SuppressFastIPOutput
131+ defer func () { util .SuppressFastIPOutput = orig }()
132+
133+ util .SuppressFastIPOutput = false
134+ restore := suppressSpeedMetadataOutput (& speedconfig.Config {OutputJSON : true })
135+ if ! util .SuppressFastIPOutput {
136+ t .Fatal ("SuppressFastIPOutput = false, want true for JSON metadata" )
137+ }
138+ restore ()
139+ if util .SuppressFastIPOutput {
140+ t .Fatal ("SuppressFastIPOutput = true after restore, want false" )
141+ }
142+
143+ restore = suppressSpeedMetadataOutput (& speedconfig.Config {OutputJSON : true , NoMetadata : true })
144+ if util .SuppressFastIPOutput {
145+ t .Fatal ("SuppressFastIPOutput = true, want unchanged when metadata is disabled" )
146+ }
147+ restore ()
148+ }
149+
82150func TestRunSpeedModeAppleJSON (t * testing.T ) {
83151 srv := httptest .NewTLSServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
84152 switch r .URL .Path {
@@ -100,16 +168,22 @@ func TestRunSpeedModeAppleJSON(t *testing.T) {
100168
101169 restoreApple := apple .SetBaseForTest (srv .URL )
102170 defer restoreApple ()
103- restoreRoots := netx .SetExtraRootCAsForTest (srv . Client (). Transport .( * http. Transport ). TLSClientConfig . RootCAs )
171+ restoreRoots := netx .SetExtraRootCAsForTest (testServerRootCAs ( srv ) )
104172 defer restoreRoots ()
105173
106174 u , _ := url .Parse (srv .URL )
107175 var stdout , stderr bytes.Buffer
108176 rc := runSpeedMode ([]string {"--speed" , "--json" , "--no-metadata" , "--max" , "64KiB" , "--timeout" , "1500" , "--threads" , "2" , "--latency-count" , "2" , "--endpoint" , u .Hostname ()}, & stdout , & stderr )
109177 if rc != 0 && rc != 2 {
110- t .Fatalf ("runSpeedMode(apple) rc = %d, stderr=%s" , rc , stderr .String ())
178+ t .Fatalf ("runSpeedMode(apple) rc = %d, want 0 or degraded 2, stderr=%s" , rc , stderr .String ())
179+ }
180+ res := assertPureJSONSpeedResult (t , stdout .Bytes (), "apple" )
181+ if res .ExitCode != rc {
182+ t .Fatalf ("JSON exit_code = %d, want rc %d" , res .ExitCode , rc )
183+ }
184+ if rc == 2 && ! res .Degraded {
185+ t .Fatal ("Degraded = false, want true when rc=2" )
111186 }
112- assertPureJSONSpeedResult (t , stdout .Bytes (), "apple" )
113187}
114188
115189func TestRunSpeedModeCloudflareJSON (t * testing.T ) {
@@ -136,18 +210,24 @@ func TestRunSpeedModeCloudflareJSON(t *testing.T) {
136210
137211 restoreCF := cloudflare .SetBaseForTest (srv .URL )
138212 defer restoreCF ()
139- restoreRoots := netx .SetExtraRootCAsForTest (srv . Client (). Transport .( * http. Transport ). TLSClientConfig . RootCAs )
213+ restoreRoots := netx .SetExtraRootCAsForTest (testServerRootCAs ( srv ) )
140214 defer restoreRoots ()
141215
142216 var stdout , stderr bytes.Buffer
143217 rc := runSpeedMode ([]string {"--speed" , "--speed-provider" , "cloudflare" , "--json" , "--no-metadata" , "--max" , "64KiB" , "--timeout" , "1500" , "--threads" , "2" , "--latency-count" , "2" , "--non-interactive" }, & stdout , & stderr )
144218 if rc != 0 && rc != 2 {
145- t .Fatalf ("runSpeedMode(cloudflare) rc = %d, stderr=%s" , rc , stderr .String ())
219+ t .Fatalf ("runSpeedMode(cloudflare) rc = %d, want 0 or degraded 2, stderr=%s" , rc , stderr .String ())
220+ }
221+ res := assertPureJSONSpeedResult (t , stdout .Bytes (), "cloudflare" )
222+ if res .ExitCode != rc {
223+ t .Fatalf ("JSON exit_code = %d, want rc %d" , res .ExitCode , rc )
224+ }
225+ if rc == 2 && ! res .Degraded {
226+ t .Fatal ("Degraded = false, want true when rc=2" )
146227 }
147- assertPureJSONSpeedResult (t , stdout .Bytes (), "cloudflare" )
148228}
149229
150- func assertPureJSONSpeedResult (t * testing.T , data []byte , providerName string ) {
230+ func assertPureJSONSpeedResult (t * testing.T , data []byte , providerName string ) result. RunResult {
151231 t .Helper ()
152232 if bytes .Contains (data , []byte ("preferred API IP" )) {
153233 t .Fatalf ("speed JSON output should not contain text noise:\n %s" , data )
@@ -162,11 +242,28 @@ func assertPureJSONSpeedResult(t *testing.T, data []byte, providerName string) {
162242 if len (res .Rounds ) != 4 {
163243 t .Fatalf ("len(Rounds) = %d, want 4" , len (res .Rounds ))
164244 }
245+ return res
246+ }
247+
248+ func testServerRootCAs (srv * httptest.Server ) * x509.CertPool {
249+ if transport , ok := srv .Client ().Transport .(* http.Transport ); ok && transport .TLSClientConfig != nil && transport .TLSClientConfig .RootCAs != nil {
250+ return transport .TLSClientConfig .RootCAs
251+ }
252+ pool := x509 .NewCertPool ()
253+ pool .AddCert (srv .Certificate ())
254+ return pool
165255}
166256
167257func TestContainsSpeedFlagSupportsAssignedFormAndRespectsTerminator (t * testing.T ) {
168- if ! containsSpeedFlag ([]string {"--speed=true" }) {
169- t .Fatal ("containsSpeedFlag(--speed=true) = false, want true" )
258+ for _ , arg := range []string {"--speed=true" , "--speed=True" , "--speed=1" , "--speed=" } {
259+ if ! containsSpeedFlag ([]string {arg }) {
260+ t .Fatalf ("containsSpeedFlag(%s) = false, want true" , arg )
261+ }
262+ }
263+ for _ , arg := range []string {"--speed=false" , "--speed=0" , "--speed=no" } {
264+ if containsSpeedFlag ([]string {arg }) {
265+ t .Fatalf ("containsSpeedFlag(%s) = true, want false" , arg )
266+ }
170267 }
171268 if containsSpeedFlag ([]string {"--" , "--speed" }) {
172269 t .Fatal ("containsSpeedFlag should ignore --speed after terminator" )
0 commit comments