Files
jinzhi/test/common/router/deep_link_service_test.dart
T

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']);
});
}