network: Fix bad HAR data (#9900)
diff --git a/packages/devtools_app/lib/src/screens/network/har_data_entry.dart b/packages/devtools_app/lib/src/screens/network/har_data_entry.dart index bd9316d..0176d96 100644 --- a/packages/devtools_app/lib/src/screens/network/har_data_entry.dart +++ b/packages/devtools_app/lib/src/screens/network/har_data_entry.dart
@@ -6,6 +6,7 @@ import 'dart:typed_data'; import '../../screens/network/utils/http_utils.dart'; +import '../../shared/http/constants.dart' as shared_http; import '../../shared/http/http_request_data.dart'; import 'constants.dart'; @@ -45,6 +46,15 @@ final requestPostData = responseData[NetworkEventKeys.postData.name]; final responseContent = responseData[NetworkEventKeys.content.name]; + final statusValue = responseData[NetworkEventKeys.status.name]; + final statusCode = switch (statusValue) { + int() => statusValue, + String() => int.tryParse(statusValue), + _ => null, + }; + responseData[shared_http.HttpRequestDataKeys.statusCode.name] = + statusCode ?? -1; + return HarDataEntry( DartIOHttpRequestData.fromJson( modifiedRequestData, @@ -148,7 +158,7 @@ }, // Response NetworkEventKeys.response.name: <String, Object?>{ - NetworkEventKeys.status.name: e.status, + NetworkEventKeys.status.name: int.tryParse(e.status ?? '') ?? -1, NetworkEventKeys.statusText.name: e.general[NetworkEventKeys.reasonPhrase.name] ?? '', NetworkEventKeys.httpVersion.name:
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index d428e86..381298f 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -40,7 +40,8 @@ ## Network profiler updates -TODO: Remove this section if there are not any updates. +- Fixed exported response status in HAR files so that they parse as integers + instead of strings. [#9900](https://github.com/flutter/devtools/pull/9900) ## Logging updates
diff --git a/packages/devtools_app/test/screens/network/har_network_test.dart b/packages/devtools_app/test/screens/network/har_network_test.dart index 98a8a78..a1c735f 100644 --- a/packages/devtools_app/test/screens/network/har_network_test.dart +++ b/packages/devtools_app/test/screens/network/har_network_test.dart
@@ -8,7 +8,6 @@ import 'package:devtools_app/src/screens/network/constants.dart'; import 'package:devtools_app/src/screens/network/har_data_entry.dart'; import 'package:devtools_app/src/screens/network/har_network_data.dart'; - import 'package:flutter_test/flutter_test.dart'; void main() { @@ -58,10 +57,25 @@ }); group('HarDataEntry', () { + final entryJson = + ((jsonData['log'] as Map<String, Object?>)['entries'] as List).first + as Map<String, Object?>; + + Map<String, Object?> copyWithStatus(Object? status) { + final copy = jsonDecode(jsonEncode(entryJson)) as Map<String, Object?>; + final response = copy['response'] as Map<String, Object?>; + response['status'] = status; + return copy; + } + + Map<String, Object?> copyWithRequestError(String error) { + final copy = jsonDecode(jsonEncode(entryJson)) as Map<String, Object?>; + final request = copy['request'] as Map<String, Object?>; + request['error'] = error; + return copy; + } + test('fromJson parses correctly', () { - final entryJson = - ((jsonData['log'] as Map<String, Object?>)['entries'] as List).first - as Map<String, Object?>; final harDataEntry = HarDataEntry.fromJson(entryJson); expect( @@ -71,12 +85,10 @@ expect(harDataEntry.request.method, 'GET'); expect(harDataEntry.request.requestHeaders, isNotEmpty); expect(harDataEntry.request.requestCookies, isEmpty); + expect(harDataEntry.request.status, '200'); }); test('toJson serializes correctly', () { - final entryJson = - ((jsonData['log'] as Map<String, Object?>)['entries'] as List).first - as Map<String, Object?>; final harDataEntry = HarDataEntry.fromJson(entryJson); final json = HarDataEntry.toJson(harDataEntry.request); @@ -89,6 +101,11 @@ expect(request?['cookies'], isEmpty); expect(json['cache'], isEmpty); + final response = json['response'] as Map<String, Object?>?; + expect(response, isNotNull); + expect(response?['status'], 200); + expect(response?['statusText'], ''); + final timings = json['timings'] as Map<String, Object?>?; expect(timings?['blocked'], NetworkEventDefaults.blocked); expect(timings?['dns'], NetworkEventDefaults.dns); @@ -98,5 +115,68 @@ expect(timings?['ssl'], NetworkEventDefaults.ssl); expect(json['comment'], ''); }); + + test('handles "Error" status in JSON', () { + final errorJson = copyWithStatus('Error'); + final harDataEntry = HarDataEntry.fromJson(errorJson); + expect(harDataEntry.request.status, '-1'); + + final serialized = HarDataEntry.toJson(harDataEntry.request); + expect((serialized['response'] as Map)['status'], -1); + }); + + test('handles "Cancelled" status in JSON', () { + final cancelledJson = copyWithStatus('Cancelled'); + final harDataEntry = HarDataEntry.fromJson(cancelledJson); + expect(harDataEntry.request.status, '-1'); + + final serialized = HarDataEntry.toJson(harDataEntry.request); + expect((serialized['response'] as Map)['status'], -1); + }); + + test('handles -1 integer status in JSON', () { + final minusOneJson = copyWithStatus(-1); + final harDataEntry = HarDataEntry.fromJson(minusOneJson); + expect(harDataEntry.request.status, '-1'); + + final serialized = HarDataEntry.toJson(harDataEntry.request); + expect((serialized['response'] as Map)['status'], -1); + }); + + test('handles invalid string status in JSON', () { + final invalidJson = copyWithStatus('foo'); + final harDataEntry = HarDataEntry.fromJson(invalidJson); + expect(harDataEntry.request.status, '-1'); + + final serialized = HarDataEntry.toJson(harDataEntry.request); + expect((serialized['response'] as Map)['status'], -1); + }); + + test('handles null status in JSON', () { + final nullJson = copyWithStatus(null); + final harDataEntry = HarDataEntry.fromJson(nullJson); + expect(harDataEntry.request.status, '-1'); + + final serialized = HarDataEntry.toJson(harDataEntry.request); + expect((serialized['response'] as Map)['status'], -1); + }); + + test('handles request connection error', () { + final errorJson = copyWithRequestError('connection failed'); + final harDataEntry = HarDataEntry.fromJson(errorJson); + expect(harDataEntry.request.status, 'Error'); + + final serialized = HarDataEntry.toJson(harDataEntry.request); + expect((serialized['response'] as Map)['status'], -1); + }); + + test('handles request cancellation error', () { + final cancelledJson = copyWithRequestError('cancelled'); + final harDataEntry = HarDataEntry.fromJson(cancelledJson); + expect(harDataEntry.request.status, 'Cancelled'); + + final serialized = HarDataEntry.toJson(harDataEntry.request); + expect((serialized['response'] as Map)['status'], -1); + }); }); }