@@ -558,6 +558,42 @@ struct HARTests {
558558 #expect( request. queryString [ 0 ] . name == " key " )
559559 #expect( request. queryString [ 0 ] . value == " " )
560560 }
561+
562+ @Test ( " headers order is deterministic across multiple conversions " )
563+ func headersOrderIsDeterministic( ) throws {
564+ // Create a URLRequest with multiple headers
565+ // Using enough headers to make nondeterministic ordering likely to be observable
566+ // This did reliably produce failures before adding the explicit sorting
567+ var urlRequest = URLRequest ( url: URL ( string: " https://example.com/api " ) !)
568+ urlRequest. setValue ( " application/json " , forHTTPHeaderField: " Accept " )
569+ urlRequest. setValue ( " gzip, deflate " , forHTTPHeaderField: " Accept-Encoding " )
570+ urlRequest. setValue ( " en-US,en;q=0.9 " , forHTTPHeaderField: " Accept-Language " )
571+ urlRequest. setValue ( " Bearer token123 " , forHTTPHeaderField: " Authorization " )
572+ urlRequest. setValue ( " keep-alive " , forHTTPHeaderField: " Connection " )
573+ urlRequest. setValue ( " application/json " , forHTTPHeaderField: " Content-Type " )
574+ urlRequest. setValue ( " example.com " , forHTTPHeaderField: " Host " )
575+ urlRequest. setValue ( " https://example.com " , forHTTPHeaderField: " Origin " )
576+ urlRequest. setValue ( " https://example.com/previous " , forHTTPHeaderField: " Referer " )
577+ urlRequest. setValue ( " Mozilla/5.0 " , forHTTPHeaderField: " User-Agent " )
578+
579+ // Convert to HAR.Request multiple times
580+ let iterations = 10
581+ var headerOrders : [ [ String ] ] = [ ]
582+
583+ for _ in 0 ..< iterations {
584+ let request = try HAR . Request ( from: urlRequest)
585+ let headerNames = request. headers. map { $0. name }
586+ headerOrders. append ( headerNames)
587+ }
588+
589+ // Check if all iterations produced the same header order
590+ let firstOrder = headerOrders [ 0 ]
591+ let allSame = headerOrders. allSatisfy { $0 == firstOrder }
592+ #expect(
593+ allSame,
594+ " Header order should be consistent across multiple conversions, but got varying orders: \( headerOrders) "
595+ )
596+ }
561597 }
562598
563599 // MARK: - Response Tests
@@ -634,6 +670,51 @@ struct HARTests {
634670 #expect( response. bodySize == data. count)
635671 }
636672
673+ @Test ( " headers order is deterministic across multiple conversions " )
674+ func headersOrderIsDeterministic( ) throws {
675+ // Create an HTTPURLResponse with multiple headers
676+ // Using enough headers to make nondeterministic ordering likely to be observable
677+ // This did reliably produce failures before adding the explicit sorting
678+ let url = URL ( string: " https://example.com/api " ) !
679+ let data = " { \" test \" :true} " . data ( using: . utf8) !
680+
681+ // Convert to HAR.Response multiple times
682+ let iterations = 10
683+ var headerOrders : [ [ String ] ] = [ ]
684+
685+ for _ in 0 ..< iterations {
686+ let httpResponse = HTTPURLResponse (
687+ url: url,
688+ statusCode: 200 ,
689+ httpVersion: " HTTP/1.1 " ,
690+ headerFields: [
691+ " Content-Type " : " application/json " ,
692+ " Cache-Control " : " no-cache, no-store, must-revalidate " ,
693+ " Content-Length " : " 12345 " ,
694+ " Date " : " Thu, 16 Jan 2026 12:00:00 GMT " ,
695+ " ETag " : " \" abc123 \" " ,
696+ " Expires " : " 0 " ,
697+ " Last-Modified " : " Thu, 16 Jan 2026 11:00:00 GMT " ,
698+ " Server " : " nginx/1.21.0 " ,
699+ " Vary " : " Accept-Encoding " ,
700+ " X-Frame-Options " : " SAMEORIGIN " ,
701+ ]
702+ ) !
703+
704+ let response = try HAR . Response ( from: httpResponse, data: data)
705+ let headerNames = response. headers. map { $0. name }
706+ headerOrders. append ( headerNames)
707+ }
708+
709+ // Check if all iterations produced the same header order
710+ let firstOrder = headerOrders [ 0 ]
711+ let allSame = headerOrders. allSatisfy { $0 == firstOrder }
712+ #expect(
713+ allSame,
714+ " Header order should be consistent across multiple conversions, but got varying orders: \( headerOrders) "
715+ )
716+ }
717+
637718 @Test ( " encodes binary data as base64 " )
638719 func initFromHTTPURLResponseBinary( ) throws {
639720 let url = URL ( string: " https://example.com/image.png " ) !
0 commit comments