Files
FlClash/test/models/core_test.dart
T
chen08209 5984c161c5 fix(core): keep very large profiles from crashing the app
Changelog: Large profiles no longer crash; downloads trust core CAs
2026-10-16 22:54:53 +08:00

252 lines
7.3 KiB
Dart

import 'dart:convert';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:test/test.dart';
void main() {
group('SetupParams', () {
test('fromJson uses snake-case keys', () {
final json = {
'selected-map': {'G1': 'P1'},
'test-url': 'http://test.com',
'skip-cert-verify': true,
};
final params = SetupParams.fromJson(json);
expect(params.selectedMap, {'G1': 'P1'});
expect(params.testUrl, 'http://test.com');
expect(params.skipCertVerify, isTrue);
});
test('toJson uses snake-case keys', () {
const params = SetupParams(
selectedMap: {'G1': 'P1'},
testUrl: 'http://t.com',
);
final json = params.toJson();
expect(json['selected-map'], {'G1': 'P1'});
expect(json['test-url'], 'http://t.com');
expect(json['skip-cert-verify'], isFalse);
});
});
group('UpdateParams', () {
test('fromJson with all fields', () {
final json = {
'tun': {'enable': true},
'mixed-port': 7890,
'allow-lan': true,
'find-process-mode': 'off',
'mode': 'rule',
'log-level': 'info',
'ipv6': false,
'tcp-concurrent': false,
'external-controller': '',
'unified-delay': false,
};
final params = UpdateParams.fromJson(json);
expect(params.mixedPort, 7890);
expect(params.allowLan, true);
expect(params.mode, Mode.rule);
expect(params.logLevel, LogLevel.info);
});
});
group('InitParams', () {
test('fromJson and toJson', () {
final json = {'home-dir': '/data/clash', 'version': 3};
final params = InitParams.fromJson(json);
expect(params.homeDir, '/data/clash');
expect(params.version, 3);
final restored = jsonDecode(jsonEncode(params.toJson()));
expect(restored['home-dir'], '/data/clash');
expect(restored['version'], 3);
});
});
group('VpnOptions.effective', () {
const options = VpnOptions(
enable: true,
port: 7890,
ipv6: false,
dnsHijacking: false,
accessControlProps: AccessControlProps(
enable: true,
mode: AccessControlMode.acceptSelected,
acceptList: ['a.app'],
rejectList: ['b.app'],
),
allowBypass: true,
systemProxy: true,
bypassDomain: ['localhost'],
stack: 'mixed',
routeAddress: ['0.0.0.0/0'],
mtu: 9000,
);
test('tells apart every option the tunnel is built from', () {
final changes = [
options.copyWith(port: 7891),
options.copyWith(ipv6: true),
options.copyWith(dnsHijacking: true),
options.copyWith(allowBypass: false),
options.copyWith(systemProxy: false),
options.copyWith(bypassDomain: const ['example.com']),
options.copyWith(stack: 'gvisor'),
options.copyWith(routeAddress: const ['10.0.0.0/8']),
options.copyWith(mtu: 1500),
options.copyWith(congestionController: 'bbr'),
options.copyWith(
accessControlProps: options.accessControlProps.copyWith(
acceptList: const ['c.app'],
),
),
options.copyWith(enable: false),
];
for (final changed in changes) {
expect(changed.effective, isNot(options.effective));
}
});
test('ignores how the app list is shown and the list not applied', () {
final changed = options.copyWith(
accessControlProps: options.accessControlProps.copyWith(
rejectList: const ['c.app'],
sort: AccessSortType.name,
isFilterSystemApp: false,
),
);
expect(changed.effective, options.effective);
});
test('ignores the proxy port and bypass list without a system proxy', () {
final withoutProxy = options.copyWith(systemProxy: false);
expect(
withoutProxy.copyWith(port: 7891, bypassDomain: const []).effective,
withoutProxy.effective,
);
});
test('ignores the access lists while access control is off', () {
final open = options.copyWith(
accessControlProps: options.accessControlProps.copyWith(enable: false),
);
expect(
open
.copyWith(
accessControlProps: open.accessControlProps.copyWith(
mode: AccessControlMode.rejectSelected,
acceptList: const [],
),
)
.effective,
open.effective,
);
});
test('a proxy-only run reads none of the tunnel options', () {
final proxyOnly = options.copyWith(enable: false);
expect(
proxyOnly.copyWith(ipv6: true, stack: 'gvisor', mtu: 1500).effective,
proxyOnly.effective,
);
});
});
group('ChangeProxyParams', () {
test('fromJson and toJson use snake-case', () {
final json = {'group-name': 'Proxy', 'proxy-name': 'auto'};
final params = ChangeProxyParams.fromJson(json);
expect(params.groupName, 'Proxy');
expect(params.proxyName, 'auto');
final out = params.toJson();
expect(out['group-name'], 'Proxy');
expect(out['proxy-name'], 'auto');
});
});
group('Delay', () {
test('fromJson and toJson', () {
final json = {'name': 'P1', 'url': 'test.com', 'value': 42};
final delay = Delay.fromJson(json);
expect(delay.name, 'P1');
expect(delay.url, 'test.com');
expect(delay.value, 42);
final out = delay.toJson();
expect(out['value'], 42);
});
test('null value', () {
final delay = Delay.fromJson({'name': 'P1', 'url': 'test.com'});
expect(delay.value, null);
});
});
group('ProxiesData', () {
test('fromJson with proxies and all list', () {
final json = {
'proxies': {
'G1': {
'type': 'Selector',
'now': 'auto',
'all': ['auto', 'P1'],
},
},
'all': ['G1'],
};
final data = ProxiesData.fromJson(json);
expect(data.all, ['G1']);
expect(data.proxies['G1'], isA<Map>());
});
});
group('ExternalProvider', () {
test('fromJson with subscription info', () {
final json = {
'name': 'TestProvider',
'type': 'Proxy',
'count': 10,
'vehicle-type': 'HTTP',
'update-at': '2024-01-01T00:00:00.000Z',
'subscription-info': {
'Upload': 100,
'Download': 200,
'Total': 1000,
'Expire': 1700000000,
},
};
final provider = ExternalProvider.fromJson(json);
expect(provider.name, 'TestProvider');
expect(provider.count, 10);
expect(provider.subscriptionInfo!.upload, 100);
expect(provider.subscriptionInfo!.download, 200);
expect(provider.subscriptionInfo!.total, 1000);
expect(provider.subscriptionInfo!.expire, 1700000000);
});
test('updatingKey uses provider_ prefix', () {
final provider = ExternalProvider(
name: 'MyProvider',
type: 'Proxy',
count: 5,
vehicleType: 'HTTP',
updateAt: DateTime.now(),
);
expect(provider.updatingKey, 'provider_MyProvider');
});
});
group('CoreEvent', () {
test('fromJson with type and data', () {
final event = CoreEvent.fromJson({'type': 'log', 'data': 'test log'});
expect(event.type, CoreEventType.log);
expect(event.data, 'test log');
});
});
}