nandi/frqpublic Fork 0
56551a8157331acb8601841b63f2dc7424339722
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

live_send.dart · 76 lines · 2.7 KBDart Blame HistoryRaw
A message in #test, from Nim 35994d4 nandi yesterday1/// The spike's end to end: connect to a real freeq, join #test, say a line.
2///
3/// Not in the test suite on purpose. It needs a network, a DNS server and a
4/// running freeq, and it sends a message to a public channel — none of which
5/// belongs in something CI runs on every push. `just nim-live` runs it when
6/// somebody means to.
7///
8/// Everything below goes through the FFI, so what it proves is the whole
9/// stack: Nim's socket, Nim's TLS, Nim's IRC registration, Nim's state, and
10/// the Dart boundary over all of it.
11import 'dart:io';
12import 'package:frq_core/frq_core.dart' as core;
13
14Future<void> main(List<String> args) async {
15 final host = args.isNotEmpty ? args[0] : 'irc.freeq.at';
16 final nick = args.length > 1
17 ? args[1]
18 : 'frq-spike-${DateTime.now().millisecondsSinceEpoch % 10000}';
19 final text = args.length > 2
20 ? args[2]
21 : 'frq nim spike: hello from Nim over dart:ffi';
22
23 core.resetUi();
24 print('$host as $nick');
25
26 core.dispatch('nick.change', nick);
27 core.dispatch('host.change', host);
28 core.dispatch('connect');
29
30 // Poll exactly as the renderer does — same call, same cadence — so this
31 // exercises the path the app uses rather than a special one for testing.
32 var tree = core.poll();
33 final deadline = DateTime.now().add(const Duration(seconds: 25));
34 while (DateTime.now().isBefore(deadline)) {
35 await Future<void>.delayed(const Duration(milliseconds: 100));
36 tree = core.poll();
37 if (_find(tree, 'title').any((t) => t.prop('label', '') == '#test')) break;
38 final err = _find(tree, 'label')
39 .map((l) => l.prop('label', ''))
40 .where((l) => l.startsWith(''));
41 if (err.isNotEmpty) {
42 print('${err.first}');
43 exit(1);
44 }
45 }
46
47 if (!_find(tree, 'title').any((t) => t.prop('label', '') == '#test')) {
48 print('✗ never registered — still on ${_find(tree, "title").map((t) => t.prop("label", ""))}');
49 print(' run with FRQ_TRACE=1 to see the wire');
50 exit(1);
51 }
52 print('✓ registered and joined #test');
53
54 core.dispatch('draft.change', text);
55 tree = core.dispatch('send');
56
57 final said = _find(tree, 'label').map((l) => l.prop('label', ''));
58 if (said.contains(text)) {
59 print('✓ sent: $text');
60 } else {
61 print('✗ the line did not reach the backlog');
62 exit(1);
63 }
64
65 // Give the server a moment to echo anything back, then leave cleanly so the
66 // reader thread is joined rather than killed with the process.
67 await Future<void>.delayed(const Duration(seconds: 3));
68 for (final m in _find(core.poll(), 'label')) {
69 print(' | ${m.prop("label", "")}');
70 }
71 core.dispatch('disconnect');
72 print('✓ disconnected');
73}
74
75List<core.UiNode> _find(core.UiNode n, String tag) =>
76 [if (n.tag == tag) n, for (final c in n.children) ..._find(c, tag)];