feat(#163): share a channel as a scannable QR (reference-app format)
Channel.toShareUri/fromShareUri/isValidShareUri encode the reference MeshCore app's format: meshcore://channel/add?name=<url-encoded>&secret=<hex32>. A "Share QR Code" action in the channel menu shows the QR via the existing QrCodeShareDialog, so it round-trips with the reference app. Encode/decode unit-tested against both real reference examples (#test, #echo). Scan-to-add follows. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>pull/201/head
parent
497931955f
commit
01207f5019
@ -0,0 +1,62 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:meshcore_open/models/channel.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('Channel share URI (#161)', () {
|
||||||
|
test('toShareUri matches the reference-app format', () {
|
||||||
|
final ch = Channel(
|
||||||
|
index: 0,
|
||||||
|
name: '#test',
|
||||||
|
psk: Channel.parsePskHex('9cd8fcf22a47333b591d96a2b848b73f'),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
ch.toShareUri(),
|
||||||
|
'meshcore://channel/add?name=%23test&secret=9cd8fcf22a47333b591d96a2b848b73f',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fromShareUri parses a real reference example', () {
|
||||||
|
final ch = Channel.fromShareUri(
|
||||||
|
'meshcore://channel/add?name=%23echo&secret=5d25cf40b1f5b4a7eb0cf9703634a948',
|
||||||
|
);
|
||||||
|
expect(ch, isNotNull);
|
||||||
|
expect(ch!.name, '#echo');
|
||||||
|
expect(ch.pskHex, '5d25cf40b1f5b4a7eb0cf9703634a948');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('round-trips name (incl. spaces) + psk', () {
|
||||||
|
final ch = Channel(
|
||||||
|
index: 3,
|
||||||
|
name: 'Private Ops',
|
||||||
|
psk: Channel.parsePskHex('0011223344556677889900aabbccddee'),
|
||||||
|
);
|
||||||
|
final back = Channel.fromShareUri(ch.toShareUri());
|
||||||
|
expect(back, isNotNull);
|
||||||
|
expect(back!.name, ch.name);
|
||||||
|
expect(back.pskHex, ch.pskHex);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejects malformed / wrong-scheme URIs', () {
|
||||||
|
// wrong scheme
|
||||||
|
expect(
|
||||||
|
Channel.fromShareUri(
|
||||||
|
'https://channel/add?name=x&secret=9cd8fcf22a47333b591d96a2b848b73f',
|
||||||
|
),
|
||||||
|
isNull,
|
||||||
|
);
|
||||||
|
// wrong host
|
||||||
|
expect(
|
||||||
|
Channel.fromShareUri('meshcore://contact/add?name=x&secret=00'),
|
||||||
|
isNull,
|
||||||
|
);
|
||||||
|
// missing secret
|
||||||
|
expect(Channel.fromShareUri('meshcore://channel/add?name=x'), isNull);
|
||||||
|
// secret not 16 bytes
|
||||||
|
expect(
|
||||||
|
Channel.fromShareUri('meshcore://channel/add?name=x&secret=abcd'),
|
||||||
|
isNull,
|
||||||
|
);
|
||||||
|
expect(Channel.isValidShareUri('not a uri at all'), isFalse);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in new issue