@@ -7,16 +7,16 @@ use crate::repositories::RepositoryStatsUpdater;
7
7
use crate :: storage:: { Storage , StorageKind } ;
8
8
use crate :: web:: Server ;
9
9
use crate :: { BuildQueue , Config , Context , Index , Metrics } ;
10
+ use anyhow:: Context as _;
11
+ use fn_error_context:: context;
10
12
use log:: error;
11
13
use once_cell:: unsync:: OnceCell ;
12
14
use postgres:: Client as Connection ;
13
15
use reqwest:: {
14
- blocking:: { Client , RequestBuilder } ,
16
+ blocking:: { Client , ClientBuilder , RequestBuilder } ,
15
17
Method ,
16
18
} ;
17
- use std:: fs;
18
- use std:: net:: SocketAddr ;
19
- use std:: { panic, sync:: Arc } ;
19
+ use std:: { fs, net:: SocketAddr , panic, sync:: Arc , time:: Duration } ;
20
20
21
21
pub ( crate ) fn wrapper ( f : impl FnOnce ( & TestEnvironment ) -> Result < ( ) > ) {
22
22
let _ = dotenv:: dotenv ( ) ;
@@ -56,45 +56,57 @@ pub(crate) fn assert_not_found(path: &str, web: &TestFrontend) -> Result<()> {
56
56
Ok ( ( ) )
57
57
}
58
58
59
- /// Make sure that a URL redirects to a specific page
60
- pub ( crate ) fn assert_redirect ( path : & str , expected_target : & str , web : & TestFrontend ) -> Result < ( ) > {
61
- // Reqwest follows redirects automatically
62
- let response = web. get ( path) . send ( ) ?;
59
+ fn assert_redirect_common ( path : & str , expected_target : & str , web : & TestFrontend ) -> Result < ( ) > {
60
+ let response = web. get_no_redirect ( path) . send ( ) ?;
63
61
let status = response. status ( ) ;
62
+ if !status. is_redirection ( ) {
63
+ anyhow:: bail!( "non-redirect from GET {path}: {status}" ) ;
64
+ }
65
+
66
+ let mut redirect_target = response
67
+ . headers ( )
68
+ . get ( "Location" )
69
+ . context ( "missing 'Location' header" ) ?
70
+ . to_str ( )
71
+ . context ( "non-ASCII redirect" ) ?;
72
+
73
+ if !expected_target. starts_with ( "http" ) {
74
+ // TODO: Should be able to use Url::make_relative,
75
+ // but https://github.com/servo/rust-url/issues/766
76
+ let base = format ! ( "http://{}" , web. server_addr( ) ) ;
77
+ redirect_target = redirect_target
78
+ . strip_prefix ( & base)
79
+ . unwrap_or ( redirect_target) ;
80
+ }
64
81
65
- let mut tmp;
66
- let redirect_target = if expected_target. starts_with ( "https://" ) {
67
- response. url ( ) . as_str ( )
68
- } else {
69
- tmp = String :: from ( response. url ( ) . path ( ) ) ;
70
- if let Some ( query) = response. url ( ) . query ( ) {
71
- tmp. push ( '?' ) ;
72
- tmp. push_str ( query) ;
73
- }
74
- & tmp
75
- } ;
76
- // Either we followed a redirect to the wrong place, or there was no redirect
77
82
if redirect_target != expected_target {
78
- // wrong place
79
- if redirect_target != path {
80
- panic ! (
81
- "{}: expected redirect to {}, got redirect to {}" ,
82
- path, expected_target, redirect_target
83
- ) ;
84
- } else {
85
- // no redirect
86
- panic ! (
87
- "{}: expected redirect to {}, got {}" ,
88
- path, expected_target, status
89
- ) ;
90
- }
83
+ anyhow:: bail!( "got redirect to {redirect_target}" ) ;
91
84
}
92
- assert ! (
93
- status. is_success( ) ,
94
- "failed to GET {}: {}" ,
95
- expected_target,
96
- status
97
- ) ;
85
+
86
+ Ok ( ( ) )
87
+ }
88
+
89
+ /// Makes sure that a URL redirects to a specific page, but doesn't check that the target exists
90
+ #[ context( "expected redirect from {path} to {expected_target}" ) ]
91
+ pub ( crate ) fn assert_redirect_unchecked (
92
+ path : & str ,
93
+ expected_target : & str ,
94
+ web : & TestFrontend ,
95
+ ) -> Result < ( ) > {
96
+ assert_redirect_common ( path, expected_target, web)
97
+ }
98
+
99
+ /// Make sure that a URL redirects to a specific page, and that the target exists and is not another redirect
100
+ #[ context( "expected redirect from {path} to {expected_target}" ) ]
101
+ pub ( crate ) fn assert_redirect ( path : & str , expected_target : & str , web : & TestFrontend ) -> Result < ( ) > {
102
+ assert_redirect_common ( path, expected_target, web) ?;
103
+
104
+ let response = web. get_no_redirect ( expected_target) . send ( ) ?;
105
+ let status = response. status ( ) ;
106
+ if !status. is_success ( ) {
107
+ anyhow:: bail!( "failed to GET {expected_target}: {status}" ) ;
108
+ }
109
+
98
110
Ok ( ( ) )
99
111
}
100
112
@@ -113,6 +125,7 @@ pub(crate) fn init_logger() {
113
125
// initializing rustwide logging also sets the global logger
114
126
rustwide:: logging:: init_with (
115
127
env_logger:: Builder :: from_env ( env_logger:: Env :: default ( ) . filter ( "DOCSRS_LOG" ) )
128
+ . format_timestamp_millis ( )
116
129
. is_test ( true )
117
130
. build ( ) ,
118
131
) ;
@@ -372,29 +385,50 @@ impl Drop for TestDatabase {
372
385
pub ( crate ) struct TestFrontend {
373
386
server : Server ,
374
387
pub ( crate ) client : Client ,
388
+ pub ( crate ) client_no_redirect : Client ,
375
389
}
376
390
377
391
impl TestFrontend {
378
392
fn new ( context : & dyn Context ) -> Self {
393
+ fn build ( f : impl FnOnce ( ClientBuilder ) -> ClientBuilder ) -> Client {
394
+ let base = Client :: builder ( )
395
+ . connect_timeout ( Duration :: from_millis ( 2000 ) )
396
+ . timeout ( Duration :: from_millis ( 2000 ) )
397
+ // The test server only supports a single connection, so having two clients with
398
+ // idle connections deadlocks the tests
399
+ . pool_max_idle_per_host ( 0 ) ;
400
+ f ( base) . build ( ) . unwrap ( )
401
+ }
402
+
379
403
Self {
380
404
server : Server :: start ( Some ( "127.0.0.1:0" ) , context)
381
405
. expect ( "failed to start the web server" ) ,
382
- client : Client :: new ( ) ,
406
+ client : build ( |b| b) ,
407
+ client_no_redirect : build ( |b| b. redirect ( reqwest:: redirect:: Policy :: none ( ) ) ) ,
383
408
}
384
409
}
385
410
386
- fn build_request ( & self , method : Method , mut url : String ) -> RequestBuilder {
411
+ fn build_url ( & self , url : & str ) -> String {
387
412
if url. is_empty ( ) || url. starts_with ( '/' ) {
388
- url = format ! ( "http://{}{}" , self . server. addr( ) , url) ;
413
+ format ! ( "http://{}{}" , self . server. addr( ) , url)
414
+ } else {
415
+ url. to_owned ( )
389
416
}
390
- self . client . request ( method, url)
391
417
}
392
418
393
419
pub ( crate ) fn server_addr ( & self ) -> SocketAddr {
394
420
self . server . addr ( )
395
421
}
396
422
397
423
pub ( crate ) fn get ( & self , url : & str ) -> RequestBuilder {
398
- self . build_request ( Method :: GET , url. to_string ( ) )
424
+ let url = self . build_url ( url) ;
425
+ log:: debug!( "getting {url}" ) ;
426
+ self . client . request ( Method :: GET , url)
427
+ }
428
+
429
+ pub ( crate ) fn get_no_redirect ( & self , url : & str ) -> RequestBuilder {
430
+ let url = self . build_url ( url) ;
431
+ log:: debug!( "getting {url} (no redirects)" ) ;
432
+ self . client_no_redirect . request ( Method :: GET , url)
399
433
}
400
434
}
0 commit comments