103 lines
3.6 KiB
Dart
103 lines
3.6 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:drift/native.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:mews/application/controllers/browser_controller.dart';
|
|
import 'package:mews/application/key_store.dart';
|
|
import 'package:mews/database/database.dart';
|
|
import 'package:mews/protocols/tofu.dart';
|
|
|
|
import 'fake_fetch.dart';
|
|
|
|
void main() {
|
|
late MewsDatabase db;
|
|
late Directory keyDir;
|
|
late KeyStore keys;
|
|
|
|
setUp(() async {
|
|
db = MewsDatabase.test(NativeDatabase.memory());
|
|
keyDir = await Directory.systemTemp.createTemp('mews-keys');
|
|
keys = KeyStore(() async => keyDir);
|
|
});
|
|
|
|
tearDown(() async {
|
|
await db.close();
|
|
keyDir.deleteSync(recursive: true);
|
|
});
|
|
|
|
BrowserController browser(FakeFetch fetch) =>
|
|
BrowserController(db: db, fetch: fetch, keys: keys);
|
|
|
|
test('a bare hostname that answers on gemini is found there', () async {
|
|
final controller =
|
|
browser(FakeFetch({'gemini://capsule.example': '# Hello\n\nText.'}));
|
|
|
|
await controller.open('capsule.example');
|
|
|
|
expect(controller.error.value, isNull);
|
|
expect(controller.currentPage.value!.url, 'gemini://capsule.example');
|
|
expect(controller.currentPage.value!.title, 'Hello');
|
|
});
|
|
|
|
test('a bare hostname that answers on spartan is found there', () async {
|
|
final controller =
|
|
browser(FakeFetch({'spartan://capsule.example': '# Hello\n\nText.'}));
|
|
|
|
await controller.open('capsule.example');
|
|
|
|
expect(controller.error.value, isNull);
|
|
expect(controller.currentPage.value!.url, 'spartan://capsule.example');
|
|
});
|
|
|
|
test('an explicit scheme is not second-guessed', () async {
|
|
final controller =
|
|
browser(FakeFetch({'gemini://capsule.example': '# Hello\n\nText.'}));
|
|
|
|
// The reader named the web, so a missing web page is an error, not an
|
|
// invitation to try the capsule protocols.
|
|
await controller.open('https://capsule.example');
|
|
|
|
expect(controller.error.value, isA<LoadFailed>());
|
|
expect(controller.currentPage.value, isNull);
|
|
});
|
|
|
|
test('a changed certificate blocks until the reader trusts the new one', () async {
|
|
final presented = TofuPin(fingerprint: 'new', expires: DateTime(2036));
|
|
final controller = browser(FakeFetch(
|
|
{'gemini://capsule.example': '# Hello\n\nText.'},
|
|
changedCertificates: {'gemini://capsule.example': presented},
|
|
));
|
|
|
|
await controller.open('gemini://capsule.example');
|
|
expect(controller.error.value, isA<CertificateChanged>());
|
|
|
|
// Trusting records the presented certificate as the host's new pin.
|
|
await controller.trustPresentedCertificate();
|
|
expect(await db.allPins(), {'capsule.example:1965': presented});
|
|
});
|
|
|
|
test('a certificate request is answered by binding an identity', () async {
|
|
final controller = browser(FakeFetch(
|
|
{'gemini://capsule.example': '# Hello\n\nText.'},
|
|
certificateRequired: {'gemini://capsule.example'},
|
|
));
|
|
final id = await db.addClientCertificate('Reader', 'CERT PEM');
|
|
await keys.save(id, 'KEY PEM');
|
|
|
|
// The site asks before anything shows; the reader's answer is a choice,
|
|
// not an automatic presentation.
|
|
await controller.open('gemini://capsule.example');
|
|
expect(controller.error.value, isA<ClientCertificateRequired>());
|
|
|
|
await controller.chooseCertificate(id);
|
|
expect(await db.bindingFor('capsule.example', 1965), id);
|
|
expect(controller.error.value, isNull);
|
|
expect(controller.currentPage.value!.title, 'Hello');
|
|
|
|
// The binding is worn on the next visit too, without asking again.
|
|
await controller.open('gemini://capsule.example');
|
|
expect(controller.error.value, isNull);
|
|
expect(controller.currentPage.value!.title, 'Hello');
|
|
});
|
|
}
|