import 'dart:convert'; import 'dart:io'; import 'package:angel3_container/mirrors.dart'; import 'package:angel3_framework/angel3_framework.dart'; import 'package:angel3_framework/http.dart'; import 'package:http/http.dart' as http; import 'package:io/ansi.dart'; import 'package:logging/logging.dart'; import 'package:test/test.dart'; import 'common.dart'; @Middleware([interceptor]) testMiddlewareMetadata(RequestContext req, ResponseContext res) async { return "This should not be shown."; } @Middleware([interceptService]) class QueryService extends Service { @override @Middleware([interceptor]) read(id, [Map? params]) async => params; } void interceptor(RequestContext req, ResponseContext res) { res ..write('Middleware') ..close(); } bool interceptService(RequestContext req, ResponseContext res) { res.write("Service with "); return true; } main() { late Angel app; late Angel nested; late Angel todos; late String url; late http.Client client; setUp(() async { app = Angel(reflector: MirrorsReflector()); nested = Angel(reflector: MirrorsReflector()); todos = Angel(reflector: MirrorsReflector()); [app, nested, todos].forEach((Angel? app) { app?.logger = Logger('routing_test') ..onRecord.listen((rec) { if (rec.error != null) { stdout ..writeln(cyan.wrap(rec.toString())) ..writeln(cyan.wrap(rec.error.toString())) ..writeln(cyan.wrap(rec.stackTrace.toString())); } }); }); todos.get('/action/:action', (req, res) => res.json(req.params)); late Route ted; ted = nested.post('/ted/:route', (RequestContext req, res) { print('Params: ${req.params}'); print('Path: ${ted.path}, uri: ${req.path}'); print('matcher: ${ted.parser}'); return req.params; }); app.mount('/nes', nested); app.get('/meta', testMiddlewareMetadata); app.get('/intercepted', (req, res) => 'This should not be shown', middleware: [interceptor]); app.get('/hello', (req, res) => 'world'); app.get('/name/:first/last/:last', (req, res) => req.params); app.post( '/lambda', (RequestContext req, res) => req.parseBody().then((_) => req.bodyAsMap)); app.mount('/todos/:id', todos); app .get('/greet/:name', (RequestContext req, res) async => "Hello ${req.params['name']}") .name = 'Named routes'; app.get('/named', (req, ResponseContext res) async { await res.redirectTo('Named routes', {'name': 'tests'}); }); app.get('/log', (RequestContext req, res) async { print("Query: ${req.queryParameters}"); return "Logged"; }); app.get('/method', (req, res) => 'Only GET'); app.post('/method', (req, res) => 'Only POST'); app.use('/query', QueryService()); RequestHandler write(String message) { return (req, res) { res.write(message); return true; }; } app.chain([write('a')]).chain([write('b'), write('c')]).get( '/chained', (req, res) => res.close()); app.fallback((req, res) => 'MJ'); //app.dumpTree(header: "DUMPING ROUTES:", showMatchers: true); client = http.Client(); HttpServer server = await AngelHttp(app).startServer('127.0.0.1', 0); url = "http://${server.address.host}:${server.port}"; }); tearDown(() async { await app.close(); client.close(); }); test('Can match basic url', () async { var response = await client.get(Uri.parse("$url/hello")); expect(response.body, equals('"world"')); }); test('Can match url with multiple parameters', () async { var response = await client.get(Uri.parse('$url/name/HELLO/last/WORLD')); print('Response: ${response.body}'); var json_ = json.decode(response.body); expect(json_, const IsInstanceOf()); expect(json_['first'], equals('HELLO')); expect(json_['last'], equals('WORLD')); }); test('Chained routes', () async { var response = await client.get(Uri.parse("$url/chained")); expect(response.body, equals('abc')); }); test('Can nest another Angel instance', () async { var response = await client.post(Uri.parse('$url/nes/ted/foo')); var json_ = json.decode(response.body); expect(json_['route'], equals('foo')); }); test('Can parse parameters from a nested Angel instance', () async { var response = await client.get(Uri.parse('$url/todos/1337/action/test')); var json_ = json.decode(response.body); print('JSON: $json_'); expect(json_['id'], equals('1337')); expect(json_['action'], equals('test')); }); test('Can add and use named middleware', () async { var response = await client.get(Uri.parse('$url/intercepted')); expect(response.body, equals('Middleware')); }); test('Middleware via metadata', () async { // Metadata var response = await client.get(Uri.parse('$url/meta')); expect(response.body, equals('Middleware')); }); test('Can serialize function result as JSON', () async { Map headers = {'Content-Type': 'application/json'}; String postData = json.encode({'it': 'works'}); var response = await client.post(Uri.parse("$url/lambda"), headers: headers as Map, body: postData); print('Response: ${response.body}'); expect(json.decode(response.body)['it'], equals('works')); }); test('Fallback routes', () async { var response = await client.get(Uri.parse('$url/my_favorite_artist')); expect(response.body, equals('"MJ"')); }); /* TODO: Revisit this later test('Can name routes', () { Route foo = app.get('/framework/:id', null)..name = 'frm'; print('Foo: $foo'); String uri = foo.makeUri({'id': 'angel'}); print(uri); expect(uri, equals('framework/angel')); }); */ test('Redirect to named routes', () async { var response = await client.get(Uri.parse('$url/named')); print(response.body); expect(json.decode(response.body), equals('Hello tests')); }); test('Match routes, even with query params', () async { var response = await client .get(Uri.parse("$url/log?foo=bar&bar=baz&baz.foo=bar&baz.bar=foo")); print(response.body); expect(json.decode(response.body), equals('Logged')); response = await client.get(Uri.parse("$url/query/foo?bar=baz")); print(response.body); expect(response.body, equals("Service with Middleware")); }); test('only match route with matching method', () async { var response = await client.get(Uri.parse("$url/method")); print(response.body); expect(response.body, '"Only GET"'); response = await client.post(Uri.parse("$url/method")); print(response.body); expect(response.body, '"Only POST"'); response = await client.patch(Uri.parse("$url/method")); print(response.body); expect(response.body, '"MJ"'); }); }