import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'fake_protocol_server.dart'; import 'package:mews/protocols/spartan.dart'; Uri open(String path, FakeSpartanServer server) => Uri.parse('spartan://localhost:${server.port}$path'); void main() { late FakeSpartanServer server; late StreamSubscription answers; final client = SpartanClient(timeout: const Duration(seconds: 5)); tearDown(() async { await answers.cancel(); await server.close(); }); test('a success answers with status, meta and body', () async { server = FakeSpartanServer(); await server.start(); var requested = ''; answers = server.answer((line, upload, socket) { requested = line; socket.add(utf8.encode('2 text/gemini\r\nhello')); }); final response = await client.fetch(open('/page', server)); expect(response.status, 2); expect(response.meta, 'text/gemini'); expect(utf8.decode(response.body!), 'hello'); expect(requested, 'localhost /page 0'); }); test('an upload rides behind the request line', () async { server = FakeSpartanServer(); await server.start(); var requested = ''; var uploaded = ''; answers = server.answer((line, upload, socket) { requested = line; uploaded = utf8.decode(upload); socket.add(utf8.encode('2 text/gemini\r\nok')); }); final response = await client.fetch(open('/echo', server), upload: utf8.encode('payload')); expect(response.status, 2); expect(requested, 'localhost /echo 7'); expect(uploaded, 'payload'); }); test('a query is part of the path', () async { server = FakeSpartanServer(); await server.start(); var requested = ''; answers = server.answer((line, upload, socket) { requested = line; socket.add(utf8.encode('2 text/gemini\r\nok')); }); await client.fetch(open('/search?q=phones', server)); expect(requested, 'localhost /search?q=phones 0'); }); test('a redirect is followed to the final address', () async { server = FakeSpartanServer(); await server.start(); final requests = []; answers = server.answer((line, upload, socket) async { requests.add(line); if (line.contains('/here')) { socket.add(utf8.encode('3 ${open('/there', server)}\r\n')); } else { socket.add(utf8.encode('2 text/gemini\r\narrived')); } }); final response = await client.fetch(open('/here', server)); expect(response.status, 2); expect(utf8.decode(response.body!), 'arrived'); expect(response.url.path, '/there'); expect(requests, hasLength(2)); }); test('a redirect loop gives up after five hops', () async { server = FakeSpartanServer(); await server.start(); final requests = []; answers = server.answer((line, upload, socket) async { requests.add(line); socket.add(utf8.encode('3 ${open('/loop', server)}\r\n')); }); await expectLater( client.fetch(open('/loop', server)), throwsA(isA()), ); expect(requests, hasLength(6)); }); test('an error status carries its message', () async { server = FakeSpartanServer(); await server.start(); answers = server.answer((line, upload, socket) => socket.add(utf8.encode('4 Nothing lives here\r\n'))); final response = await client.fetch(open('/gone', server)); expect(response.status, 4); expect(response.meta, 'Nothing lives here'); expect(response.body, isNull); }); test('an unreachable host is one failure', () async { final nowhere = Uri.parse('spartan://localhost:1/'); await expectLater(client.fetch(nowhere), throwsA(isA())); }); }