@@ -494,7 +494,8 @@ final class URLSessionHTTPClientTest: XCTestCase {
494494 let completionExpectation = XCTestExpectation ( description: " completion " )
495495
496496 let url = URL ( " https://downloader-tests.com/testServerError.zip " )
497- var request = LegacyHTTPClient . Request. download ( url: url, fileSystem: localFileSystem, destination: temporaryDirectory. appending ( " download " ) )
497+ let destination = temporaryDirectory. appending ( " download " )
498+ var request = LegacyHTTPClient . Request. download ( url: url, fileSystem: localFileSystem, destination: destination)
498499 request. options. validResponseCodes = [ 200 ]
499500
500501 httpClient. execute (
@@ -508,6 +509,8 @@ final class URLSessionHTTPClientTest: XCTestCase {
508509 XCTFail ( " unexpected success " )
509510 case . failure( let error) :
510511 XCTAssertEqual ( error as? HTTPClientError , HTTPClientError . badResponseStatusCode ( 500 ) )
512+ // Verify that no file was created at the destination for bad response codes
513+ XCTAssertFalse ( localFileSystem. exists ( destination) , " Destination file should not exist for bad response codes " )
511514 }
512515 completionExpectation. fulfill ( )
513516 }
@@ -998,13 +1001,14 @@ final class URLSessionHTTPClientTest: XCTestCase {
9981001
9991002 try await testWithTemporaryDirectory { temporaryDirectory in
10001003 let url = URL ( " https://async-downloader-tests.com/testServerError.zip " )
1004+ let destination = temporaryDirectory. appending ( " download " )
10011005 var options = HTTPClientRequest . Options ( )
10021006 options. validResponseCodes = [ 200 ]
10031007 let request = HTTPClient . Request. download (
10041008 url: url,
10051009 options: options,
10061010 fileSystem: localFileSystem,
1007- destination: temporaryDirectory . appending ( " download " )
1011+ destination: destination
10081012 )
10091013
10101014 MockURLProtocol . onRequest ( request) { request in
@@ -1022,6 +1026,68 @@ final class URLSessionHTTPClientTest: XCTestCase {
10221026 XCTFail ( " unexpected success " )
10231027 } catch {
10241028 XCTAssertEqual ( error as? HTTPClientError , HTTPClientError . badResponseStatusCode ( 500 ) )
1029+ // Verify that no file was created at the destination for bad response codes
1030+ XCTAssertFalse ( localFileSystem. exists ( destination) , " Destination file should not exist for bad response codes " )
1031+ }
1032+ }
1033+ }
1034+
1035+ func testAsyncDownloadServerErrorAndResponseBody( ) async throws {
1036+ #if !os(macOS)
1037+ // URLSession Download tests can only run on macOS
1038+ // as re-libs-foundation's URLSessionTask implementation which expects the temporaryFileURL property to be on the request.
1039+ // and there is no way to set it in a mock
1040+ // https://github.com/apple/swift-corelibs-foundation/pull/2593 tries to address the latter part
1041+ try XCTSkipIf ( true , " test is only supported on macOS " )
1042+ #endif
1043+ let configuration = URLSessionConfiguration . default
1044+ configuration. protocolClasses = [ MockURLProtocol . self]
1045+ let urlSession = URLSessionHTTPClient ( configuration: configuration)
1046+ let httpClient = HTTPClient ( implementation: urlSession. execute)
1047+
1048+ try await testWithTemporaryDirectory { temporaryDirectory in
1049+ let url = URL ( " https://async-downloader-tests.com/testServerErrorWithBody.zip " )
1050+ let destination = temporaryDirectory. appending ( " download " )
1051+ var options = HTTPClientRequest . Options ( )
1052+ options. validResponseCodes = [ 200 ]
1053+ let request = HTTPClient . Request. download (
1054+ url: url,
1055+ options: options,
1056+ fileSystem: localFileSystem,
1057+ destination: destination
1058+ )
1059+
1060+ // Create an error response body (e.g., JSON error message)
1061+ let errorJson = Data ( """
1062+ {
1063+ " errors " : [ {
1064+ " status " : 500,
1065+ " message " : " Internal Server Error "
1066+ } ]
1067+ }
1068+ """ . utf8)
1069+
1070+ MockURLProtocol . onRequest ( request) { request in
1071+ MockURLProtocol . sendResponse ( statusCode: 500 , headers: [ " Content-Type " : " application/json " , " Content-Length " : " \( errorJson. count) " ] , for: request)
1072+ // Send error response body - this should be downloaded to temp file but then deleted
1073+ MockURLProtocol . sendData ( errorJson, for: request)
1074+ MockURLProtocol . sendCompletion ( for: request)
1075+ }
1076+
1077+ do {
1078+ _ = try await httpClient. execute (
1079+ request,
1080+ progress: { _, _ in
1081+ // Progress may be called as data is downloaded, even for error responses
1082+ // This is expected behavior
1083+ }
1084+ )
1085+ XCTFail ( " unexpected success " )
1086+ } catch {
1087+ XCTAssertEqual ( error as? HTTPClientError , HTTPClientError . badResponseStatusCode ( 500 ) )
1088+ // Verify that no file was created at the destination for bad response codes
1089+ // even though a response body was sent
1090+ XCTAssertFalse ( localFileSystem. exists ( destination) , " Destination file should not exist for bad response codes, even when response body is present " )
10251091 }
10261092 }
10271093 }
0 commit comments