@@ -11,7 +11,7 @@ import * as Layer from "effect/Layer";
1111import * as Path from "effect/Path" ;
1212import * as Stream from "effect/Stream" ;
1313import * as TestClock from "effect/testing/TestClock" ;
14- import { HttpClient , HttpClientResponse } from "effect/http" ;
14+ import { HttpClient , HttpClientRequest , HttpClientResponse } from "effect/http" ;
1515import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner" ;
1616
1717import packageJson from "../../package.json" with { type : "json" } ;
@@ -30,6 +30,7 @@ interface HarnessOptions {
3030 readonly preflightDuration ?: Duration . Input ;
3131 readonly preflightStarted ?: Deferred . Deferred < void > ;
3232 readonly beforeDownload ?: Effect . Effect < void > ;
33+ readonly httpClient ?: HttpClient . HttpClient ;
3334 readonly requestUpdate ?: ServiceLauncherClient . ServiceLauncherClient [ "Service" ] [ "requestUpdate" ] ;
3435 readonly desktopAppUpdate ?: DesktopAppUpdate . DesktopAppUpdate [ "Service" ] ;
3536 readonly policy ?: ManagedPolicyDocument ;
@@ -145,7 +146,7 @@ const makeHarness = Effect.fn("test.make_self_update_harness")(function* (
145146 ) ,
146147 Effect . provideService (
147148 HttpClient . HttpClient ,
148- releaseHttpClient ( order , urls , options . beforeDownload ) ,
149+ options . httpClient ?? releaseHttpClient ( order , urls , options . beforeDownload ) ,
149150 ) ,
150151 Effect . provideService ( HostProcess . Platform , "linux" ) ,
151152 Effect . provideService ( HostProcess . Architecture , "x64" ) ,
@@ -156,10 +157,45 @@ const makeHarness = Effect.fn("test.make_self_update_harness")(function* (
156157 ) ,
157158 ) ,
158159 ) ;
159- return { selfUpdate, order, urls } ;
160+ return { selfUpdate, order, urls, baseDir } ;
160161} ) ;
161162
162163it . layer ( NodeServices . layer ) ( "server self update" , ( it ) => {
164+ it . effect . each ( [
165+ { asset : "checksums" , status : 504 } ,
166+ { asset : "archive" , status : 503 } ,
167+ { asset : "checksums" , status : 403 } ,
168+ ] ) ( "reports an HTTP $status downloading $asset and allows retry" , ( { asset, status } ) =>
169+ Effect . gen ( function * ( ) {
170+ let failDownload = true ;
171+ const { selfUpdate, order, baseDir } = yield * makeHarness ( {
172+ httpClient : releaseHttpClient ( [ ] , [ ] ) . pipe (
173+ HttpClient . transform ( ( response , request ) =>
174+ failDownload && request . url . endsWith ( "/SHA256SUMS" ) === ( asset === "checksums" )
175+ ? Effect . succeed (
176+ HttpClientResponse . fromWeb (
177+ HttpClientRequest . get ( "https://user:[email protected] /private?token=secret" ) , 178+ new Response ( "private response body" , { status } ) ,
179+ ) ,
180+ )
181+ : response ,
182+ ) ,
183+ ) ,
184+ } ) ;
185+ const error = yield * selfUpdate . update ( { targetVersion : "1.1.0" } ) . pipe ( Effect . flip ) ;
186+ expect ( error . reason ) . toBe (
187+ `Could not prepare [email protected] while downloading the t3 release ${ asset } (HTTP ${ status } from cdn.example).` , 188+ ) ;
189+ expect ( order ) . toEqual ( [ ] ) ;
190+ const fs = yield * FileSystem . FileSystem ;
191+ const path = yield * Path . Path ;
192+ expect ( yield * fs . readDirectory ( path . join ( baseDir , "runtime" , "versions" ) ) ) . toEqual ( [ ] ) ;
193+ failDownload = false ;
194+ expect ( ( yield * selfUpdate . update ( { targetVersion : "1.1.0" } ) ) . updateId ) . toBe ( "launcher-id" ) ;
195+ expect ( order ) . toEqual ( [ "extract" , "preflight" , "accept" ] ) ;
196+ } ) ,
197+ ) ;
198+
163199 // regression: #5181 make remote updates rollback-safe
164200 it . effect ( "rejects invalid versions and desktop-managed servers before staging" , ( ) =>
165201 Effect . gen ( function * ( ) {
0 commit comments