Files
MrbWebLibre/apps/weblibre/test/features/app_links/effective_routing_test.dart
T

186 lines
6.1 KiB
Dart

/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter_test/flutter_test.dart';
import 'package:weblibre/features/app_links/domain/services/effective_routing.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/models/site_assignment.dart';
import 'package:weblibre/features/proxy/data/proxy_connection.dart';
SiteAssignment _assignment(String site, {String? contextId}) =>
SiteAssignment(id: site, contextualIdentity: contextId, assignedSite: site);
void main() {
group('resolveContainerAssignment', () {
test('explicit proxy connection wins', () {
final assignment = resolveContainerAssignment(
contextId: 'ctx',
proxyConnectionId: const TorProxyConnectionId(),
bypassGlobalProxy: false,
);
expect(assignment, isA<ExplicitProxyAssignment>());
});
test('bypassGlobalProxy with no proxy is direct scoped to the context', () {
final assignment = resolveContainerAssignment(
contextId: 'ctx',
proxyConnectionId: null,
bypassGlobalProxy: true,
);
expect(assignment, isA<DirectProxyAssignment>());
expect((assignment as DirectProxyAssignment).scopeId, 'ctx');
});
test('no proxy and no bypass inherits', () {
final assignment = resolveContainerAssignment(
contextId: 'ctx',
proxyConnectionId: null,
bypassGlobalProxy: false,
);
expect(assignment, isA<InheritProxyAssignment>());
});
});
group('resolveIsolationContextRouting', () {
test('any explicit proxy wins (lowest sorted id)', () {
final routing = resolveIsolationContextRouting([
ProxyAssignment.inherit(),
ProxyAssignment.explicit('zeta'),
ProxyAssignment.explicit('alpha'),
ProxyAssignment.direct('scope'),
]);
expect(routing.chosen, isA<ExplicitProxyAssignment>());
expect((routing.chosen as ExplicitProxyAssignment).proxyId, 'alpha');
expect(routing.distinctAssignmentCount, 4);
});
test('direct wins only when no container inherits', () {
final routing = resolveIsolationContextRouting([
ProxyAssignment.direct('scopeB'),
ProxyAssignment.direct('scopeA'),
]);
expect(routing.chosen, isA<DirectProxyAssignment>());
expect((routing.chosen as DirectProxyAssignment).scopeId, 'scopeA');
});
test('direct plus inherit collapses to inherit', () {
final routing = resolveIsolationContextRouting([
ProxyAssignment.direct('scope'),
ProxyAssignment.inherit(),
]);
expect(routing.chosen, isA<InheritProxyAssignment>());
expect(routing.distinctAssignmentCount, 2);
expect(routing.assignmentLabels, ['inherit', 'direct:scope']);
});
});
group('isAssignmentProtected', () {
test('explicit is always protected', () {
expect(
isAssignmentProtected(
ProxyAssignment.explicit('p'),
protectGeneralContext: false,
),
isTrue,
);
});
test('direct is never protected', () {
expect(
isAssignmentProtected(
ProxyAssignment.direct('s'),
protectGeneralContext: true,
),
isFalse,
);
});
test('inherit follows the general context', () {
expect(
isAssignmentProtected(
ProxyAssignment.inherit(),
protectGeneralContext: true,
),
isTrue,
);
expect(
isAssignmentProtected(
ProxyAssignment.inherit(),
protectGeneralContext: false,
),
isFalse,
);
});
});
group('protectedTargetPatternForSite', () {
test('wildcard entry includes subdomains and ignores port', () {
final pattern = protectedTargetPatternForSite(
Uri.parse('https://*.example.com'),
);
expect(pattern.scheme, 'https');
expect(pattern.hostOrSuffix, 'example.com');
expect(pattern.includeSubdomains, isTrue);
expect(pattern.port, isNull);
});
test('exact entry preserves effective port', () {
final defaultPort = protectedTargetPatternForSite(
Uri.parse('https://example.com'),
);
expect(defaultPort.hostOrSuffix, 'example.com');
expect(defaultPort.includeSubdomains, isFalse);
expect(defaultPort.port, 443);
final explicitPort = protectedTargetPatternForSite(
Uri.parse('http://example.com:8080'),
);
expect(explicitPort.port, 8080);
});
});
group('computeProtectedTargetPatterns', () {
test('keeps only assignments in a protected or strict container', () {
final patterns = computeProtectedTargetPatterns(
assignments: [
_assignment('https://proxied.example', contextId: 'proxied'),
_assignment('https://direct.example', contextId: 'direct'),
_assignment('https://strict.example', contextId: 'strict'),
_assignment('https://unassigned.example', contextId: null),
],
protectedOrStrictContextIds: {'proxied', 'strict'},
);
final hosts = patterns.map((p) => p.hostOrSuffix).toSet();
expect(hosts, {'proxied.example', 'strict.example'});
});
test('deduplicates identical patterns', () {
final patterns = computeProtectedTargetPatterns(
assignments: [
_assignment('https://dup.example', contextId: 'a'),
_assignment('https://dup.example', contextId: 'a'),
],
protectedOrStrictContextIds: {'a'},
);
expect(patterns.length, 1);
});
});
}