You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dateKeys-dart/test/locator_uri_test.dart

141 lines
5.1 KiB

// The addresses of a locator against Go (spec §44.1): checkAddressUri and
// LocatorAddress.host on the 247 addresses of testdata/vectors/locator.json
// and on the 2 800 of tool/locator_go_vectors.go, with the texts of
// locator.CheckURI and Address.Host; the parser of IP addresses against
// netip.ParseAddr and String; and the classification of publicIP on the
// bytes of addresses, as checkResolvedIp checks the IP that a name resolves
// to. Everything here runs on the VM and compiled to JavaScript, from the
// constant of locator_uris.g.dart.
library;
import 'package:datekeys/src/bytes.dart';
import 'package:datekeys/src/ipaddr.dart';
import 'package:datekeys/src/locator.dart';
import 'package:test/test.dart';
import 'locator_support.dart';
import 'vectors/locator_uris.g.dart';
import 'wordkey_support.dart' show fromWtf8;
void main() {
final v = decodeJson(locatorUrisJson);
void expectUri(List<Object?> c) {
final uri = c[0]! as String;
final want = textOf(v, c[1]);
expect(errorText(() => checkAddressUri(uri)), want, reason: uri);
final host = LocatorAddress(uri).host;
expect(host, c[2], reason: uri);
// As FuzzCheckURI of Go: the host shown is in the address as it is
// written, with no decoding.
if (want.isEmpty) expect(uri.contains(host) && host.isNotEmpty, isTrue);
}
group('checkAddressUri', () {
test('the addresses of locator.json, with the texts of Go', () {
final cases = rows(v, 'testdata');
expect(cases, hasLength(247));
cases.forEach(expectUri);
});
test('the addresses at the edges of §44.1 and drawn from a seed', () {
final cases = rows(v, 'uris');
expect(cases, hasLength(2800));
cases.forEach(expectUri);
// Every rule rejects some and some pass.
final texts = {for (final c in cases) c[1]};
expect(texts.length, greaterThan(20));
});
test('an address that is not ASCII is measured and quoted in UTF-8', () {
// Go counts the bytes of the string and quotes its first byte that is
// not of RFC 3986 as a rune, 0xc3 as U+00C3 in quotes.
final c = rows(
v,
'uris',
).firstWhere((c) => c[0] == 'https://ejemplo.org/\u00e9');
expectUri(c);
expect(textOf(v, c[1]), contains("'\u00c3'"));
// A lone surrogate is three bytes that are not UTF-8, as Go reads
// those bytes.
for (final c in rows(v, 'wtf8')) {
final uri = fromWtf8(fromHex(c[0]! as String))!;
expect(errorText(() => checkAddressUri(uri)), textOf(v, c[1]));
expect(LocatorAddress(uri).host, '');
}
});
test('a doubled opening bracket and a CID with one more character whose '
'bits are zero are refused, as by Go since the draft v0.13', () {
// checkHost of Go takes one pair of brackets, and isCIDv1 refuses 5 or
// more bits left over: neither is the form that spec §44.1 asks for.
for (final uri in [
'https://[[2000::]/',
'ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdia',
]) {
final c = rows(v, 'uris').firstWhere((c) => c[0] == uri);
expect(c[1], isNot(0), reason: uri);
expectUri(c);
}
});
});
group('IP addresses', () {
test('parseIpAddress accepts what netip.ParseAddr accepts', () {
final cases = rows(v, 'ips');
expect(cases.length, greaterThan(1500));
for (final c in cases) {
final s = c[0]! as String;
final a = parseIpAddress(s);
if (c[1] == false) {
expect(a, isNull, reason: s);
continue;
}
expect(a, isNotNull, reason: s);
expect(toHex(a!.bytes), c[2], reason: s);
expect(a.zone, c[3], reason: s);
expect('$a', c[4], reason: s);
expect(isPublicIp(a), c[5], reason: s);
}
});
test(
'checkResolvedIp checks the bytes of an address as publicIP, but NAT64',
() {
for (final c in rows(v, 'public')) {
final b = fromHex(c[0]! as String);
if (c[1] == null) {
expect(() => checkResolvedIp(b), throwsArgumentError);
continue;
}
final a = IpAddress.fromBytes(b);
expect('$a', c[2]);
expect(isPublicIp(a), c[1], reason: '$a');
// An address of NAT64 counts by the IPv4 address it holds (spec
// v0.13): resolved_ip_test.dart runs those.
if (b.length == 16 &&
toHex(b.sublist(0, 12)) == '0064ff9b0000000000000000') {
continue;
}
expect(
errorText(() => checkResolvedIp(b)),
c[1] == true
? ''
: 'locator: an https address whose name resolves to ${c[2]}, '
'an IP address that is not public',
);
}
},
);
test('an IPv4 address mapped in IPv6 is not public', () {
expect(
() => checkResolvedIp([...List.filled(10, 0), 0xff, 0xff, 8, 8, 8, 8]),
throwsA(isA<LocatorException>()),
);
checkResolvedIp([8, 8, 8, 8]);
expect(() => checkResolvedIp([8, 8, 8, 256]), throwsArgumentError);
});
});
}

Powered by TurnKey Linux.