diff --git a/chopper/lib/src/interceptors/http_logging_interceptor.dart b/chopper/lib/src/interceptors/http_logging_interceptor.dart index 95bb6b2c..d8f5ee0f 100644 --- a/chopper/lib/src/interceptors/http_logging_interceptor.dart +++ b/chopper/lib/src/interceptors/http_logging_interceptor.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:chopper/src/chain/chain.dart'; import 'package:chopper/src/chopper_log_record.dart'; import 'package:chopper/src/interceptors/interceptor.dart'; +import 'package:chopper/src/request.dart'; import 'package:chopper/src/response.dart'; import 'package:chopper/src/utils.dart'; import 'package:http/http.dart' as http; @@ -75,12 +76,16 @@ enum Level { @immutable class HttpLoggingInterceptor implements Interceptor { /// {@macro http_logging_interceptor} - HttpLoggingInterceptor({this.level = Level.body, Logger? logger}) - : _logger = logger ?? chopperLogger, + HttpLoggingInterceptor({ + this.level = Level.body, + this.onlyErrors = false, + Logger? logger, + }) : _logger = logger ?? chopperLogger, _logBody = level == Level.body, _logHeaders = level == Level.body || level == Level.headers; final Level level; + final bool onlyErrors; final Logger _logger; final bool _logBody; final bool _logHeaders; @@ -88,103 +93,131 @@ class HttpLoggingInterceptor implements Interceptor { @override FutureOr> intercept( Chain chain) async { - final request = chain.request; - if (level == Level.none) return chain.proceed(request); + final Request request = chain.request; + + final Stopwatch stopWatch = Stopwatch()..start(); + + final Response response = await chain.proceed(request); + + stopWatch.stop(); + + if (level == Level.none || (onlyErrors && response.statusCode < 400)) { + return response; + } + final http.BaseRequest baseRequest = await request.toBaseRequest(); - String startRequestMessage = - '--> ${baseRequest.method} ${baseRequest.url.toString()}'; - String bodyRequestMessage = ''; + final StringBuffer startRequestMessage = StringBuffer( + '--> ${baseRequest.method} ${baseRequest.url.toString()}', + ); + final StringBuffer bodyRequestMessage = StringBuffer(); if (baseRequest is http.Request) { if (baseRequest.body.isNotEmpty) { - bodyRequestMessage = baseRequest.body; + bodyRequestMessage.write(baseRequest.body); if (!_logHeaders) { - startRequestMessage += ' (${baseRequest.bodyBytes.length}-byte body)'; + startRequestMessage.write( + ' (${baseRequest.bodyBytes.length}-byte body)', + ); } } } // Always start on a new line _logger.info(ChopperLogRecord('', request: request)); - _logger.info(ChopperLogRecord(startRequestMessage, request: request)); + _logger.info( + ChopperLogRecord(startRequestMessage.toString(), request: request), + ); if (_logHeaders) { baseRequest.headers.forEach( - (k, v) => _logger.info(ChopperLogRecord('$k: $v', request: request)), + (String k, String v) => _logger.info( + ChopperLogRecord('$k: $v', request: request), + ), ); if (baseRequest.contentLength != null && baseRequest.headers['content-length'] == null) { - _logger.info(ChopperLogRecord( - 'content-length: ${baseRequest.contentLength}', - request: request, - )); + _logger.info( + ChopperLogRecord( + 'content-length: ${baseRequest.contentLength}', + request: request, + ), + ); } } if (_logBody && bodyRequestMessage.isNotEmpty) { _logger.info(ChopperLogRecord('', request: request)); - _logger.info(ChopperLogRecord(bodyRequestMessage, request: request)); + _logger.info( + ChopperLogRecord(bodyRequestMessage.toString(), request: request), + ); } if (_logHeaders || _logBody) { - _logger.info(ChopperLogRecord( - '--> END ${baseRequest.method}', - request: request, - )); + _logger.info( + ChopperLogRecord('--> END ${baseRequest.method}', request: request), + ); } - final stopWatch = Stopwatch()..start(); - - final response = await chain.proceed(request); - - stopWatch.stop(); - if (level == Level.none) return response; - final baseResponse = response.base; + final http.BaseResponse baseResponse = response.base; - String bytes = ''; - String reasonPhrase = response.statusCode.toString(); - String bodyResponseMessage = ''; + final StringBuffer bytes = StringBuffer(); + final StringBuffer reasonPhrase = StringBuffer( + response.statusCode.toString(), + ); + final StringBuffer bodyResponseMessage = StringBuffer(); if (baseResponse is http.Response) { if (baseResponse.reasonPhrase != null) { - reasonPhrase += - ' ${baseResponse.reasonPhrase != reasonPhrase ? baseResponse.reasonPhrase : ''}'; + if (baseResponse.reasonPhrase != reasonPhrase.toString()) { + reasonPhrase.write(' ${baseResponse.reasonPhrase}'); + } } if (baseResponse.body.isNotEmpty) { - bodyResponseMessage = baseResponse.body; + bodyResponseMessage.write(baseResponse.body); if (!_logBody && !_logHeaders) { - bytes = ', ${response.bodyBytes.length}-byte body'; + bytes.write(', ${response.bodyBytes.length}-byte body'); } } } // Always start on a new line _logger.info(ChopperLogRecord('', response: response)); - _logger.info(ChopperLogRecord( - '<-- $reasonPhrase ${baseResponse.request?.method} ${baseResponse.request?.url.toString()} (${stopWatch.elapsedMilliseconds}ms$bytes)', - response: response, - )); + _logger.info( + ChopperLogRecord( + '<-- $reasonPhrase ${baseResponse.request?.method} ${baseResponse.request?.url.toString()} (${stopWatch.elapsedMilliseconds}ms$bytes)', + response: response, + ), + ); if (_logHeaders) { baseResponse.headers.forEach( - (k, v) => _logger.info(ChopperLogRecord('$k: $v', response: response)), + (String k, String v) => _logger.info( + ChopperLogRecord('$k: $v', response: response), + ), ); if (baseResponse.contentLength != null && baseResponse.headers['content-length'] == null) { - _logger.info(ChopperLogRecord( - 'content-length: ${baseResponse.contentLength}', - response: response, - )); + _logger.info( + ChopperLogRecord( + 'content-length: ${baseResponse.contentLength}', + response: response, + ), + ); } } if (_logBody && bodyResponseMessage.isNotEmpty) { _logger.info(ChopperLogRecord('', response: response)); - _logger.info(ChopperLogRecord(bodyResponseMessage, response: response)); + _logger.info( + ChopperLogRecord( + bodyResponseMessage.toString(), + response: response, + ), + ); } if (_logBody || _logHeaders) { diff --git a/chopper/test/http_logging_interceptor_test.dart b/chopper/test/http_logging_interceptor_test.dart index b2ba8aaa..22dc5799 100644 --- a/chopper/test/http_logging_interceptor_test.dart +++ b/chopper/test/http_logging_interceptor_test.dart @@ -8,7 +8,7 @@ import 'package:test/test.dart'; import 'helpers/fake_chain.dart'; void main() { - final fakeRequest = Request( + final Request fakeRequest = Request( 'POST', Uri.parse('/'), Uri.parse('base'), @@ -16,289 +16,671 @@ void main() { headers: {'foo': 'bar'}, ); - group('http logging requests', () { - test('Http logger interceptor none level request', () async { - final logger = HttpLoggingInterceptor(level: Level.none); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest)); - - expect( - logs, - equals( - [], - ), - ); + group('standard', () { + group('http logging requests', () { + test('Http logger interceptor none level request', () async { + final logger = HttpLoggingInterceptor(level: Level.none); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); + + expect( + logs, + equals( + [], + ), + ); + }); + + test('Http logger interceptor basic level request', () async { + final logger = HttpLoggingInterceptor(level: Level.basic); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); + + expect( + logs, + containsAll( + [ + '', + '--> POST base/ (4-byte body)', + ], + ), + ); + }); + + test('Http logger interceptor basic level request', () async { + final logger = HttpLoggingInterceptor(level: Level.headers); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); + + expect( + logs, + containsAll( + [ + '', + '--> POST base/', + 'foo: bar', + 'content-type: text/plain; charset=utf-8', + 'content-length: 4', + '--> END POST', + ], + ), + ); + }); + + test('Http logger interceptor body level request', () async { + final logger = HttpLoggingInterceptor(level: Level.body); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); + + expect( + logs, + containsAll( + [ + '', + '--> POST base/', + 'foo: bar', + 'content-type: text/plain; charset=utf-8', + 'content-length: 4', + '', + 'test', + '--> END POST', + ], + ), + ); + }); }); - test('Http logger interceptor basic level request', () async { - final logger = HttpLoggingInterceptor(level: Level.basic); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest)); - - expect( - logs, - containsAll( - [ - '', - '--> POST base/ (4-byte body)', - ], - ), - ); - }); + group('http logging interceptor response logging', () { + late Response fakeResponse; - test('Http logger interceptor basic level request', () async { - final logger = HttpLoggingInterceptor(level: Level.headers); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest)); - - expect( - logs, - containsAll( - [ - '', - '--> POST base/', - 'foo: bar', - 'content-type: text/plain; charset=utf-8', - 'content-length: 4', - '--> END POST', - ], - ), - ); + setUp(() async { + fakeResponse = Response( + http.Response( + 'responseBodyBase', + 200, + headers: {'foo': 'bar'}, + request: await fakeRequest.toBaseRequest(), + ), + 'responseBody', + ); + }); + + test('Http logger interceptor none level response', () async { + final logger = HttpLoggingInterceptor(level: Level.none); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); + + expect( + logs, + equals( + [], + ), + ); + }); + + test('Http logger interceptor basic level response', () async { + final logger = HttpLoggingInterceptor(level: Level.basic); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 200 POST base/ (0ms, 16-byte body)', + ], + ), + ); + }); + + test('Http logger interceptor headers level response', () async { + final logger = HttpLoggingInterceptor(level: Level.headers); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 200 POST base/ (0ms)', + 'foo: bar', + 'content-length: 16', + '<-- END HTTP', + ], + ), + ); + }); + + test('Http logger interceptor body level response', () async { + final logger = HttpLoggingInterceptor(level: Level.body); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 200 POST base/ (0ms)', + 'foo: bar', + 'content-length: 16', + '', + 'responseBodyBase', + '<-- END HTTP', + ], + ), + ); + }); }); - test('Http logger interceptor body level request', () async { - final logger = HttpLoggingInterceptor(level: Level.body); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest)); - - expect( - logs, - containsAll( - [ - '', - '--> POST base/', - 'foo: bar', - 'content-type: text/plain; charset=utf-8', - 'content-length: 4', - '', - 'test', - '--> END POST', - ], - ), - ); + group('headers content-length not overridden', () { + late Response fakeResponse; + + setUp(() async { + fakeResponse = Response( + http.Response( + 'responseBodyBase', + 200, + headers: { + 'foo': 'bar', + 'content-length': '42', + }, + request: await fakeRequest.toBaseRequest(), + ), + 'responseBody', + ); + }); + + test('request header level content-length', () async { + final logger = HttpLoggingInterceptor(level: Level.headers); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + + await logger.intercept(FakeChain(fakeRequest.copyWith( + headers: {...fakeRequest.headers, 'content-length': '42'}))); + + expect( + logs, + containsAll( + [ + '', + '--> POST base/', + 'foo: bar', + 'content-length: 42', + 'content-type: text/plain; charset=utf-8', + '--> END POST', + ], + ), + ); + }); + + test('request body level content-length', () async { + final logger = HttpLoggingInterceptor(level: Level.body); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + + await logger.intercept(FakeChain(fakeRequest.copyWith( + headers: {...fakeRequest.headers, 'content-length': '42'}))); + + expect( + logs, + containsAll( + [ + '', + '--> POST base/', + 'foo: bar', + 'content-length: 42', + 'content-type: text/plain; charset=utf-8', + '', + 'test', + '--> END POST', + ], + ), + ); + }); + + test('response header level content-length', () async { + final logger = HttpLoggingInterceptor(level: Level.headers); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 200 POST base/ (0ms)', + 'foo: bar', + 'content-length: 42', + '<-- END HTTP', + ], + ), + ); + }); + test('response body level content-length', () async { + final logger = HttpLoggingInterceptor(level: Level.body); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 200 POST base/ (0ms)', + 'foo: bar', + 'content-length: 42', + '', + 'responseBodyBase', + '<-- END HTTP', + ], + ), + ); + }); }); }); - group('http logging interceptor response logging', () { - late Response fakeResponse; - - setUp(() async { - fakeResponse = Response( - http.Response( - 'responseBodyBase', - 200, - headers: {'foo': 'bar'}, - request: await fakeRequest.toBaseRequest(), - ), - 'responseBody', - ); - }); + group('only errors', () { + group('http logging requests', () { + test('Http logger interceptor none level request', () async { + final logger = + HttpLoggingInterceptor(level: Level.none, onlyErrors: true); - test('Http logger interceptor none level response', () async { - final logger = HttpLoggingInterceptor(level: Level.none); + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest)); + expect(logs, equals([])); + }); - expect( - logs, - equals( - [], - ), - ); - }); + test('Http logger interceptor basic level request', () async { + final logger = + HttpLoggingInterceptor(level: Level.basic, onlyErrors: true); - test('Http logger interceptor basic level response', () async { - final logger = HttpLoggingInterceptor(level: Level.basic); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); - - expect( - logs, - containsAll( - [ - '', - '<-- 200 POST base/ (0ms, 16-byte body)', - ], - ), - ); - }); + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); - test('Http logger interceptor headers level response', () async { - final logger = HttpLoggingInterceptor(level: Level.headers); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); - - expect( - logs, - containsAll( - [ - '', - '<-- 200 POST base/ (0ms)', - 'foo: bar', - 'content-length: 16', - '<-- END HTTP', - ], - ), - ); - }); + expect(logs, equals([])); + }); - test('Http logger interceptor body level response', () async { - final logger = HttpLoggingInterceptor(level: Level.body); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); - - expect( - logs, - containsAll( - [ - '', - '<-- 200 POST base/ (0ms)', - 'foo: bar', - 'content-length: 16', - '', - 'responseBodyBase', - '<-- END HTTP', - ], - ), - ); - }); - }); + test('Http logger interceptor basic level request', () async { + final logger = + HttpLoggingInterceptor(level: Level.headers, onlyErrors: true); - group('headers content-length not overridden', () { - late Response fakeResponse; - - setUp(() async { - fakeResponse = Response( - http.Response( - 'responseBodyBase', - 200, - headers: { - 'foo': 'bar', - 'content-length': '42', - }, - request: await fakeRequest.toBaseRequest(), - ), - 'responseBody', - ); - }); + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); - test('request header level content-length', () async { - final logger = HttpLoggingInterceptor(level: Level.headers); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - - await logger.intercept(FakeChain(fakeRequest.copyWith( - headers: {...fakeRequest.headers, 'content-length': '42'}))); - - expect( - logs, - containsAll( - [ - '', - '--> POST base/', - 'foo: bar', - 'content-length: 42', - 'content-type: text/plain; charset=utf-8', - '--> END POST', - ], - ), - ); - }); + expect(logs, equals([])); + }); - test('request body level content-length', () async { - final logger = HttpLoggingInterceptor(level: Level.body); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - - await logger.intercept(FakeChain(fakeRequest.copyWith( - headers: {...fakeRequest.headers, 'content-length': '42'}))); - - expect( - logs, - containsAll( - [ - '', - '--> POST base/', - 'foo: bar', - 'content-length: 42', - 'content-type: text/plain; charset=utf-8', - '', - 'test', - '--> END POST', - ], - ), - ); + test('Http logger interceptor body level request', () async { + final logger = + HttpLoggingInterceptor(level: Level.body, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); + + expect(logs, equals([])); + }); }); - test('response header level content-length', () async { - final logger = HttpLoggingInterceptor(level: Level.headers); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); - - expect( - logs, - containsAll( - [ - '', - '<-- 200 POST base/ (0ms)', - 'foo: bar', - 'content-length: 42', - '<-- END HTTP', - ], - ), - ); + group('HTTP 200', () { + group('http logging interceptor response logging', () { + late Response fakeResponse; + + setUp(() async { + fakeResponse = Response( + http.Response( + 'responseBodyBase', + 200, + headers: {'foo': 'bar'}, + request: await fakeRequest.toBaseRequest(), + ), + 'responseBody', + ); + }); + + test('Http logger interceptor none level response', () async { + final logger = + HttpLoggingInterceptor(level: Level.none, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); + + expect( + logs, + equals( + [], + ), + ); + }); + + test('Http logger interceptor basic level response', () async { + final logger = + HttpLoggingInterceptor(level: Level.basic, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect(logs, equals([])); + }); + + test('Http logger interceptor headers level response', () async { + final logger = + HttpLoggingInterceptor(level: Level.headers, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect(logs, equals([])); + }); + + test('Http logger interceptor body level response', () async { + final logger = + HttpLoggingInterceptor(level: Level.body, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect(logs, equals([])); + }); + }); + + group('headers content-length not overridden', () { + late Response fakeResponse; + + setUp(() async { + fakeResponse = Response( + http.Response( + 'responseBodyBase', + 200, + headers: { + 'foo': 'bar', + 'content-length': '42', + }, + request: await fakeRequest.toBaseRequest(), + ), + 'responseBody', + ); + }); + + test('request header level content-length', () async { + final logger = + HttpLoggingInterceptor(level: Level.headers, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + + await logger.intercept(FakeChain(fakeRequest.copyWith( + headers: {...fakeRequest.headers, 'content-length': '42'}))); + + expect(logs, equals([])); + }); + + test('request body level content-length', () async { + final logger = + HttpLoggingInterceptor(level: Level.body, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + + await logger.intercept(FakeChain(fakeRequest.copyWith( + headers: {...fakeRequest.headers, 'content-length': '42'}))); + + expect(logs, equals([])); + }); + + test('response header level content-length', () async { + final logger = + HttpLoggingInterceptor(level: Level.headers, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect(logs, equals([])); + }); + test('response body level content-length', () async { + final logger = + HttpLoggingInterceptor(level: Level.body, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect(logs, equals([])); + }); + }); }); - test('response body level content-length', () async { - final logger = HttpLoggingInterceptor(level: Level.body); - - final logs = []; - chopperLogger.onRecord.listen((r) => logs.add(r.message)); - await logger.intercept(FakeChain(fakeRequest, response: fakeResponse)); - - expect( - logs, - containsAll( - [ - '', - '<-- 200 POST base/ (0ms)', - 'foo: bar', - 'content-length: 42', - '', - 'responseBodyBase', - '<-- END HTTP', - ], - ), - ); + + group('HTTP 400', () { + group('http logging interceptor response logging', () { + late Response fakeResponse; + + setUp(() async { + fakeResponse = Response( + http.Response( + 'responseBodyBase', + 400, + headers: {'foo': 'bar'}, + request: await fakeRequest.toBaseRequest(), + ), + 'responseBody', + ); + }); + + test('Http logger interceptor none level response', () async { + final logger = + HttpLoggingInterceptor(level: Level.none, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger.intercept(FakeChain(fakeRequest)); + + expect( + logs, + equals( + [], + ), + ); + }); + + test('Http logger interceptor basic level response', () async { + final logger = + HttpLoggingInterceptor(level: Level.basic, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 400 POST base/ (0ms, 16-byte body)', + ], + ), + ); + }); + + test('Http logger interceptor headers level response', () async { + final logger = + HttpLoggingInterceptor(level: Level.headers, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 400 POST base/ (0ms)', + 'foo: bar', + 'content-length: 16', + '<-- END HTTP', + ], + ), + ); + }); + + test('Http logger interceptor body level response', () async { + final logger = + HttpLoggingInterceptor(level: Level.body, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 400 POST base/ (0ms)', + 'foo: bar', + 'content-length: 16', + '', + 'responseBodyBase', + '<-- END HTTP', + ], + ), + ); + }); + }); + + group('headers content-length not overridden', () { + late Response fakeResponse; + + setUp(() async { + fakeResponse = Response( + http.Response( + 'responseBodyBase', + 400, + headers: { + 'foo': 'bar', + 'content-length': '42', + }, + request: await fakeRequest.toBaseRequest(), + ), + 'responseBody', + ); + }); + + test('request header level content-length', () async { + final logger = + HttpLoggingInterceptor(level: Level.headers, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + + await logger.intercept(FakeChain(fakeRequest.copyWith( + headers: {...fakeRequest.headers, 'content-length': '42'}))); + + expect(logs, equals([])); + }); + + test('request body level content-length', () async { + final logger = + HttpLoggingInterceptor(level: Level.body, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + + await logger.intercept(FakeChain(fakeRequest.copyWith( + headers: {...fakeRequest.headers, 'content-length': '42'}))); + + expect(logs, equals([])); + }); + + test('response header level content-length', () async { + final logger = + HttpLoggingInterceptor(level: Level.headers, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 400 POST base/ (0ms)', + 'foo: bar', + 'content-length: 42', + '<-- END HTTP', + ], + ), + ); + }); + test('response body level content-length', () async { + final logger = + HttpLoggingInterceptor(level: Level.body, onlyErrors: true); + + final logs = []; + chopperLogger.onRecord.listen((r) => logs.add(r.message)); + await logger + .intercept(FakeChain(fakeRequest, response: fakeResponse)); + + expect( + logs, + containsAll( + [ + '', + '<-- 400 POST base/ (0ms)', + 'foo: bar', + 'content-length: 42', + '', + 'responseBodyBase', + '<-- END HTTP', + ], + ), + ); + }); + }); }); }); }