111 lines
3.4 KiB
Dart
111 lines
3.4 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:jinzhi_flutter_app/common/router/deep_link_service.dart';
|
|
|
|
void main() {
|
|
setUp(DeepLinkService.resetForTest);
|
|
|
|
test('keeps FIFO events until the router is ready', () {
|
|
DeepLinkService.configureTrustedHttpsHosts({'links.example.com'});
|
|
DeepLinkService.handle('jinzhi://market');
|
|
DeepLinkService.handleUri(Uri.parse('https://links.example.com/news?id=1'));
|
|
expect(DeepLinkService.pendingCount, 2);
|
|
|
|
final navigated = <String>[];
|
|
DeepLinkService.attachRouter((location) {
|
|
navigated.add(location);
|
|
return true;
|
|
});
|
|
|
|
expect(navigated, ['/market', '/news?id=1']);
|
|
expect(DeepLinkService.pendingCount, 0);
|
|
});
|
|
|
|
test('rejects HTTPS links outside the exact trusted host allowlist', () {
|
|
DeepLinkService.configureTrustedHttpsHosts({'links.example.com'});
|
|
final navigated = <String>[];
|
|
DeepLinkService.attachRouter((location) {
|
|
navigated.add(location);
|
|
return true;
|
|
});
|
|
|
|
DeepLinkService.handleUri(Uri.parse('https://evil.example/news'));
|
|
DeepLinkService.handleUri(Uri.parse('https://sub.links.example.com/news'));
|
|
DeepLinkService.handleUri(Uri.parse('https://links.example.com:8443/news'));
|
|
DeepLinkService.handleUri(Uri.parse('https://links.example.com/news'));
|
|
|
|
expect(navigated, ['/news']);
|
|
expect(DeepLinkService.pendingCount, 0);
|
|
});
|
|
|
|
test('disables HTTPS deep links when no production host is configured', () {
|
|
final navigated = <String>[];
|
|
DeepLinkService.attachRouter((location) {
|
|
navigated.add(location);
|
|
return true;
|
|
});
|
|
|
|
DeepLinkService.handleUri(Uri.parse('https://links.example.com/news'));
|
|
DeepLinkService.handle('jinzhi://news');
|
|
|
|
expect(navigated, ['/news']);
|
|
});
|
|
|
|
test('deduplicates event ids even after the first event is consumed', () {
|
|
final navigated = <String>[];
|
|
DeepLinkService.attachRouter((location) {
|
|
navigated.add(location);
|
|
return true;
|
|
});
|
|
final payload = jsonEncode({
|
|
'event_id': 'same-event',
|
|
'deep_link': 'jinzhi://assets',
|
|
});
|
|
|
|
DeepLinkService.handle(payload);
|
|
DeepLinkService.handle(payload);
|
|
|
|
expect(navigated, ['/assets']);
|
|
});
|
|
|
|
test('uses bounded queue and discards expired events', () {
|
|
final old = DateTime.now().subtract(DeepLinkService.expiry * 2);
|
|
DeepLinkService.enqueue(
|
|
'jinzhi://assets',
|
|
source: DeepLinkSource.appLink,
|
|
receivedAt: old,
|
|
);
|
|
for (var index = 0; index < DeepLinkService.maxPending + 5; index++) {
|
|
DeepLinkService.enqueue(
|
|
'jinzhi://news?event_id=$index',
|
|
source: DeepLinkSource.push,
|
|
);
|
|
}
|
|
expect(DeepLinkService.pendingCount, DeepLinkService.maxPending);
|
|
|
|
final navigated = <String>[];
|
|
DeepLinkService.attachRouter((location) {
|
|
navigated.add(location);
|
|
return true;
|
|
});
|
|
expect(navigated, hasLength(DeepLinkService.maxPending));
|
|
expect(navigated, isNot(contains('/assets')));
|
|
});
|
|
|
|
test('auth guard leaves the head pending until auth changes', () {
|
|
var authenticated = false;
|
|
final navigated = <String>[];
|
|
DeepLinkService.attachRouter((location) {
|
|
navigated.add(location);
|
|
return true;
|
|
}, authGuard: (_) => authenticated);
|
|
DeepLinkService.handle('jinzhi://profile');
|
|
expect(DeepLinkService.pendingCount, 1);
|
|
|
|
authenticated = true;
|
|
DeepLinkService.notifyAuthChanged();
|
|
expect(navigated, ['/profile']);
|
|
});
|
|
}
|