@@ -63,6 +63,10 @@ impl Server {
6363 let text = String :: from_utf8_lossy ( & request[ ..size] ) ;
6464 let path = text. split_whitespace ( ) . nth ( 1 ) . unwrap_or ( "" ) ;
6565 let mode = path. trim_start_matches ( '/' ) . trim_start_matches ( "feed/" ) ;
66+ if !path. starts_with ( "/feed/" ) && mode == "not-found" {
67+ let _ = stream. write_all ( b"HTTP/1.1 404 Not Found\r \n Content-Length: 0\r \n Connection: close\r \n \r \n " ) ;
68+ return ;
69+ }
6670 if ( !path. starts_with ( "/feed/" ) && mode == "headers" ) || mode == "feed-stall" {
6771 pause ( & done, Duration :: from_secs ( 40 ) ) ;
6872 return ;
@@ -147,7 +151,12 @@ fn native_update_transport_checks_signatures_truncation_and_cache_readback() {
147151 let app = app ( KEY ) ;
148152 let server = Server :: new ( ) ;
149153 tauri:: async_runtime:: block_on ( async {
150- for mode in [ "fast" , "tampered" , "truncated" ] {
154+ for ( mode, expected_error) in [
155+ ( "fast" , None ) ,
156+ ( "tampered" , Some ( "update_signature_invalid" ) ) ,
157+ ( "truncated" , Some ( "update_network_failed" ) ) ,
158+ ( "not-found" , Some ( "update_download_failed" ) ) ,
159+ ] {
151160 let update = find_update (
152161 app. handle ( ) ,
153162 "stable" ,
@@ -158,8 +167,8 @@ fn native_update_transport_checks_signatures_truncation_and_cache_readback() {
158167 . unwrap ( )
159168 . unwrap ( ) ;
160169 let result = download_update ( & update, |_, _| { } ) . await ;
161- if mode != "fast" {
162- assert ! ( result. is_err ( ) , "invalid archive was accepted : {mode}") ;
170+ if let Some ( expected ) = expected_error {
171+ assert_eq ! ( result. unwrap_err ( ) , expected , " archive mode : {mode}") ;
163172 continue ;
164173 }
165174 let bytes = result. unwrap ( ) ;
@@ -181,6 +190,44 @@ fn native_update_transport_checks_signatures_truncation_and_cache_readback() {
181190 } ) ;
182191}
183192
193+ #[ test]
194+ fn background_download_failures_keep_their_cause_without_blocking_local_services ( ) {
195+ let app = app ( KEY ) ;
196+ let server = Server :: new ( ) ;
197+ tauri:: async_runtime:: block_on ( async {
198+ for ( mode, expected) in [
199+ ( "tampered" , "update_signature_invalid" ) ,
200+ ( "truncated" , "update_network_failed" ) ,
201+ ( "not-found" , "update_download_failed" ) ,
202+ ] {
203+ let state = Maintenance :: default ( ) ;
204+ state. publish ( "ready" , json ! ( { } ) ) ;
205+ let generation = state. admit_background_update ( ) . unwrap ( ) ;
206+ let result = state
207+ . run_background_update (
208+ generation,
209+ "stable" ,
210+ find_update (
211+ app. handle ( ) ,
212+ "stable" ,
213+ server. feed ( mode) ,
214+ Duration :: from_secs ( 2 ) ,
215+ ) ,
216+ |update| async move {
217+ download_update ( & update, |_, _| { } ) . await ?;
218+ panic ! ( "a rejected archive cannot be published to the cache" ) ;
219+ } ,
220+ )
221+ . await ;
222+ assert_eq ! ( result. unwrap_err( ) , expected) ;
223+ assert_eq ! ( state. status_snapshot( ) [ "details" ] [ "code" ] , expected) ;
224+ assert_eq ! ( state. status_snapshot( ) [ "details" ] [ "background" ] , true ) ;
225+ assert_eq ! ( state. recover_connected( || 1 ) , Some ( 1 ) ) ;
226+ assert ! ( state. pending. lock( ) . unwrap( ) . is_none( ) ) ;
227+ }
228+ } ) ;
229+ }
230+
184231#[ test]
185232fn native_feed_keeps_its_short_deadline ( ) {
186233 let app = app ( KEY ) ;
@@ -292,7 +339,10 @@ fn stalled_update_transfer_remains_bounded() {
292339 . unwrap ( )
293340 . unwrap ( ) ;
294341 let start = Instant :: now ( ) ;
295- assert ! ( tauri:: async_runtime:: block_on( download_update( & update, |_, _| { } ) ) . is_err( ) ) ;
342+ assert_eq ! (
343+ tauri:: async_runtime:: block_on( download_update( & update, |_, _| { } ) ) . unwrap_err( ) ,
344+ "update_download_timeout"
345+ ) ;
296346 assert ! (
297347 start. elapsed( ) < Duration :: from_secs( 35 ) ,
298348 "stalled {mode} transfer was not bounded"
0 commit comments