chore: Halkyon Learn moves into apps/learn

Its server, client, app and curator, and the tools that write its
assets (maps, Anki fixtures, icons), as they are; paths are fixed in
the commits that follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-19 20:08:06 +02:00
commit 649499be23
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -0,0 +1,132 @@
import 'dart:io';
import 'package:halkyon_learn_server/src/profile/link_verification.dart';
import 'package:test/test.dart';
void main() {
group('a public address', () {
test('is one anybody on the internet reaches', () {
for (final address in ['93.184.216.34', '1.1.1.1', '2606:4700::1111']) {
expect(
isPublicAddress(InternetAddress(address)),
isTrue,
reason: address,
);
}
});
test(
'is never the instance itself, its networks, or addresses kept for '
"special uses: the instance mustn't be made to look around itself",
() {
for (final address in [
'127.0.0.1', // loopback
'0.0.0.0', // this network
'10.0.0.1', // private
'172.16.5.4',
'172.30.80.2', // the instance's edge network, Caddy
'192.168.1.1',
'169.254.169.254', // link-local, cloud metadata
'100.64.0.1', // shared address space (carrier-grade NAT)
'192.0.0.8', // IETF protocol assignments
'192.0.2.1', // documentation
'198.51.100.1',
'203.0.113.1',
'198.18.0.1', // benchmarking
'224.0.0.1', // multicast
'240.0.0.1', // reserved
'255.255.255.255', // broadcast
'::1',
'::',
'fe80::1',
'fc00::1', // unique local
'fd12:3456::1',
'ff02::1', // multicast
'2001:db8::1', // documentation
'::ffff:127.0.0.1', // IPv4 inside IPv6
'::ffff:10.0.0.1',
'64:ff9b::7f00:1', // NAT64 of 127.0.0.1
]) {
expect(
isPublicAddress(InternetAddress(address)),
isFalse,
reason: address,
);
}
},
);
});
group('a page links back', () {
final profile = Uri.parse('https://halkyon.eu/@Raisondetredev');
final page = Uri.parse('https://example.org/about');
bool linksBack(String html, {Uri? at}) =>
linksBackTo(html, pageUrl: at ?? page, profileUrl: profile);
test('with an <a> or a <link> marked rel="me" to the profile page, its '
"host's and its handle's case and a trailing slash aside", () {
for (final html in [
'<a rel="me" href="https://halkyon.eu/@Raisondetredev">Halkyon</a>',
'<head><link rel="me" href="https://halkyon.eu/@raisondetredev/"></head>',
'<a href="https://halkyon.eu/@raisondetredev" rel="noopener me">x</a>',
"<A REL='Me' HREF='https://HALKYON.EU/@RAISONDETREDEV'>x</A>",
'<a rel="me" href="https://halkyon.eu/@raisondetredev?from=site#top">',
]) {
expect(linksBack(html), isTrue, reason: html);
}
expect(
linksBack(
'<a rel="me" href="/@raisondetredev">x</a>',
at: Uri.parse('https://halkyon.eu/about'),
),
isTrue,
reason: 'a relative link, from the page it is on',
);
});
test('not with a link without rel="me", to another page, another '
'person, another host or over plain HTTP', () {
for (final html in [
'<a href="https://halkyon.eu/@raisondetredev">x</a>',
'<a rel="nofollow" href="https://halkyon.eu/@raisondetredev">x</a>',
'<a rel="meme" href="https://halkyon.eu/@raisondetredev">x</a>',
'<a rel="me" href="https://halkyon.eu/@raisondetredev2">x</a>',
'<a rel="me" href="https://halkyon.eu/@raisondetredev/studysets">x</a>',
'<a rel="me" href="https://learn.halkyon.eu/@raisondetredev">x</a>',
'<a rel="me" href="http://halkyon.eu/@raisondetredev">x</a>',
'<p>rel="me" href="https://halkyon.eu/@raisondetredev"</p>',
]) {
expect(linksBack(html), isFalse, reason: html);
}
});
});
group('the fetcher refuses, without connecting,', () {
final fetcher = SafeLinkFetcher(userAgent: 'test');
test('what is no HTTPS link on the usual port', () async {
for (final link in [
'http://example.org/',
'ftp://example.org/',
'https://example.org:8443/',
]) {
expect(await fetcher.fetch(Uri.parse(link)), isNull, reason: link);
}
});
test(
'a host that is, or resolves to, an address that is not public',
() async {
for (final link in [
'https://127.0.0.1/',
'https://[::1]/',
'https://localhost/',
'https://169.254.169.254/latest/meta-data/',
]) {
expect(await fetcher.fetch(Uri.parse(link)), isNull, reason: link);
}
},
);
});
}