From 1a5528cb7ffe395b89dfa46a599f6ebae3bfae26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asc=C3=AAnio=20Sanderson?= Date: Mon, 13 Jan 2025 20:43:14 -0300 Subject: [PATCH 1/4] test: covers base cases --- pubspec.lock | 32 ++++++++++++++----- pubspec.yaml | 2 ++ test/main_test.dart | 78 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 test/main_test.dart diff --git a/pubspec.lock b/pubspec.lock index f340311..f27360b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,23 +5,23 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77" + sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" url: "https://pub.dev" source: hosted - version: "73.0.0" + version: "76.0.0" _macros: dependency: transitive description: dart source: sdk - version: "0.3.2" + version: "0.3.3" analyzer: dependency: transitive description: name: analyzer - sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a" + sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" url: "https://pub.dev" source: hosted - version: "6.8.0" + version: "6.11.0" args: dependency: transitive description: @@ -118,6 +118,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + http_methods: + dependency: transitive + description: + name: http_methods + sha256: "6bccce8f1ec7b5d701e7921dca35e202d425b57e317ba1a37f2638590e29e566" + url: "https://pub.dev" + source: hosted + version: "1.1.1" http_mock_adapter: dependency: "direct main" description: @@ -186,10 +194,10 @@ packages: dependency: transitive description: name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" url: "https://pub.dev" source: hosted - version: "0.1.2-main.4" + version: "0.1.3-main.0" matcher: dependency: transitive description: @@ -255,7 +263,7 @@ packages: source: hosted version: "2.1.4" shelf: - dependency: transitive + dependency: "direct dev" description: name: shelf sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 @@ -270,6 +278,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + shelf_router: + dependency: "direct dev" + description: + name: shelf_router + sha256: f5e5d492440a7fb165fe1e2e1a623f31f734d3370900070b2b1e0d0428d59864 + url: "https://pub.dev" + source: hosted + version: "1.1.4" shelf_static: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 1bd5ddf..6f63e9c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,4 +14,6 @@ dependencies: dev_dependencies: lints: ^4.0.0 + shelf: ^1.4.2 + shelf_router: ^1.1.4 test: ^1.25.5 diff --git a/test/main_test.dart b/test/main_test.dart new file mode 100644 index 0000000..960c754 --- /dev/null +++ b/test/main_test.dart @@ -0,0 +1,78 @@ +import 'dart:io'; + +import 'package:pop_network/pop_network.dart' hide Response; +import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart'; +import 'package:shelf_router/shelf_router.dart'; +import 'package:test/test.dart'; + +Future main() async { + late HttpServer server; + late ApiManager apiManager; + + final address = InternetAddress.loopbackIPv4.address; + const port = 6969; + final baseUrl = 'http://${address}:$port'; + + setUp(() async { + server = await createServer( + address: address, + port: port, + ); + apiManager = ApiManager( + baseUrl: baseUrl, + ); + }); + + tearDown(() async { + await server.close(); + }); + + group('base cases', () { + test('GET works', () async { + final response = await apiManager.get('/get'); + expect(response.data, 'OK'); + }); + + test('POST works', () async { + final response = await apiManager.post('/post'); + expect(response.data, 'OK'); + }); + + test('PATCH works', () async { + final response = await apiManager.patch('/patch'); + expect(response.data, 'OK'); + }); + + test('PUT works', () async { + final response = await apiManager.put('/put'); + expect(response.data, 'OK'); + }); + + test('DELETE works', () async { + final response = await apiManager.delete('/delete'); + expect(response.data, 'OK'); + }); + + test('download works', () async { + final fileName = 'data.json'; + final filePath = '${Directory.systemTemp}/$fileName'; + await apiManager.download('/get', filePath); + expect(File(filePath).exists(), completion(true)); + expect(File(filePath).readAsString(), completion('OK')); + }); + }); +} + +Future createServer({ + required String address, + required int port, +}) async { + final router = Router(); + router.get('/get', (_) => Response.ok('OK')); + router.post('/post', (_) => Response.ok('OK')); + router.patch('/patch', (_) => Response.ok('OK')); + router.put('/put', (_) => Response.ok('OK')); + router.delete('/delete', (_) => Response.ok('OK')); + return serve(router, address, port); +} From bcd6a3fbb8b2cdc005f155d11ccb200d65dd75b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asc=C3=AAnio=20Sanderson?= Date: Mon, 13 Jan 2025 20:58:21 -0300 Subject: [PATCH 2/4] test: covers mocked cases --- test/main_test.dart | 124 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 96 insertions(+), 28 deletions(-) diff --git a/test/main_test.dart b/test/main_test.dart index 960c754..1c1c22e 100644 --- a/test/main_test.dart +++ b/test/main_test.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io'; import 'package:pop_network/pop_network.dart' hide Response; @@ -7,28 +8,36 @@ import 'package:shelf_router/shelf_router.dart'; import 'package:test/test.dart'; Future main() async { - late HttpServer server; - late ApiManager apiManager; - final address = InternetAddress.loopbackIPv4.address; const port = 6969; final baseUrl = 'http://${address}:$port'; - setUp(() async { - server = await createServer( - address: address, - port: port, - ); - apiManager = ApiManager( - baseUrl: baseUrl, - ); - }); - - tearDown(() async { - await server.close(); - }); + Future createServer(Router router) async { + return serve(router, address, port); + } group('base cases', () { + late ApiManager apiManager; + late HttpServer server; + + setUp(() async { + apiManager = ApiManager( + baseUrl: baseUrl, + ); + server = await createServer( + Router() + ..get('/get', (_) => Response.ok('OK')) + ..post('/post', (_) => Response.ok('OK')) + ..patch('/patch', (_) => Response.ok('OK')) + ..put('/put', (_) => Response.ok('OK')) + ..delete('/delete', (_) => Response.ok('OK')), + ); + }); + + tearDown(() { + server.close(); + }); + test('GET works', () async { final response = await apiManager.get('/get'); expect(response.data, 'OK'); @@ -62,17 +71,76 @@ Future main() async { expect(File(filePath).readAsString(), completion('OK')); }); }); -} -Future createServer({ - required String address, - required int port, -}) async { - final router = Router(); - router.get('/get', (_) => Response.ok('OK')); - router.post('/post', (_) => Response.ok('OK')); - router.patch('/patch', (_) => Response.ok('OK')); - router.put('/put', (_) => Response.ok('OK')); - router.delete('/delete', (_) => Response.ok('OK')); - return serve(router, address, port); + group('mocked cases', () { + const replyParams = const MockReplyParams(mockPath: '/foo/bar/baz.json'); + const data = {'message': 'OK'}; + + late ApiManager apiManager; + late HttpServer server; + + setUp(() async { + apiManager = ApiManager( + baseUrl: baseUrl, + loadMockAsset: (assetPath) async { + if (assetPath == replyParams.mockPath) { + return jsonEncode(data); + } + throw Exception('Unknown asset $assetPath'); + }, + ); + server = await createServer( + Router() + ..get('/get', (_) => Response.ok('OK')) + ..post('/post', (_) => Response.ok('OK')) + ..patch('/patch', (_) => Response.ok('OK')) + ..put('/put', (_) => Response.ok('OK')) + ..delete('/delete', (_) => Response.ok('OK')), + ); + }); + + tearDown(() { + server.close(); + }); + + test('GET can be mocked', () async { + final response = await apiManager.get( + '/get', + mockReplyParams: replyParams, + ); + expect(response.data, data); + }); + + test('POST can be mocked', () async { + final response = await apiManager.post( + '/post', + mockReplyParams: replyParams, + ); + expect(response.data, data); + }); + + test('PATCH can be mocked', () async { + final response = await apiManager.patch( + '/patch', + mockReplyParams: replyParams, + ); + expect(response.data, data); + }); + + test('PUT can be mocked', () async { + final response = await apiManager.put( + '/put', + mockReplyParams: replyParams, + ); + expect(response.data, data); + }); + + test('DELETE can be mocked', () async { + final response = await apiManager.delete( + '/delete', + mockReplyParams: replyParams, + ); + expect(response.data, data); + }); + }); } From d34d693fd92a1903f72016bc89c9fda3e880654a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asc=C3=AAnio=20Sanderson?= Date: Mon, 13 Jan 2025 21:48:31 -0300 Subject: [PATCH 3/4] test: remove temporary file on tearDown --- test/main_test.dart | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/test/main_test.dart b/test/main_test.dart index 1c1c22e..a456cc8 100644 --- a/test/main_test.dart +++ b/test/main_test.dart @@ -20,6 +20,8 @@ Future main() async { late ApiManager apiManager; late HttpServer server; + final temporaryFile = File('${Directory.systemTemp}/data.json'); + setUp(() async { apiManager = ApiManager( baseUrl: baseUrl, @@ -34,8 +36,11 @@ Future main() async { ); }); - tearDown(() { - server.close(); + tearDown(() async { + await Future.wait([ + if (await temporaryFile.exists()) temporaryFile.delete(), + server.close(), + ]); }); test('GET works', () async { @@ -64,11 +69,9 @@ Future main() async { }); test('download works', () async { - final fileName = 'data.json'; - final filePath = '${Directory.systemTemp}/$fileName'; - await apiManager.download('/get', filePath); - expect(File(filePath).exists(), completion(true)); - expect(File(filePath).readAsString(), completion('OK')); + await apiManager.download('/get', temporaryFile.path); + expect(temporaryFile.exists(), completion(true)); + expect(temporaryFile.readAsString(), completion('OK')); }); }); @@ -99,8 +102,8 @@ Future main() async { ); }); - tearDown(() { - server.close(); + tearDown(() async { + await server.close(); }); test('GET can be mocked', () async { From 98135b15653f4227b56ef4b0d12fd2cf34546cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asc=C3=AAnio=20Sanderson?= Date: Tue, 14 Jan 2025 12:08:52 -0300 Subject: [PATCH 4/4] feat: validates that custom http clients are created --- test/main_test.dart | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/main_test.dart b/test/main_test.dart index a456cc8..2d85dc3 100644 --- a/test/main_test.dart +++ b/test/main_test.dart @@ -146,4 +146,40 @@ Future main() async { expect(response.data, data); }); }); + + group('custom HttpClient', () { + late CustomHttpClientFactory httpClientFactory; + late ApiManager apiManager; + late HttpServer server; + + setUp(() async { + httpClientFactory = CustomHttpClientFactory(); + apiManager = ApiManager( + baseUrl: baseUrl, + createHttpClient: httpClientFactory, + ); + server = await createServer( + Router()..get('/get', (_) => Response.ok('OK')), + ); + }); + + tearDown(() async { + await server.close(); + }); + + test('works', () async { + final response = await apiManager.get('/get'); + expect(response.data, 'OK'); + expect(httpClientFactory.called, isTrue); + }); + }); +} + +final class CustomHttpClientFactory { + bool called = false; + + HttpClient call() { + called = true; + return HttpClient(); + } }