import 'package:halkyon_keys/halkyon_keys.dart'; import 'package:sodium/sodium_sumo.dart'; import 'package:test/test.dart'; Future main() async { final sodium = await SodiumSumoInit.init(); group('row IDs keyed by the account', () { final account = AccountKeys.generate(sodium); final secrets = ( encryption: account.encryption.secretKey.extractBytes(), signing: account.signing.secretKey.extractBytes(), ); test('every device of the account names a row alike: its keys, unlocked ' 'anew, give the same ID', () { final elsewhere = AccountKeys.fromSecretKeys( sodium, encryptionSecretKey: secrets.encryption, signingSecretKey: secrets.signing, ); expect( keyedRowId(sodium, elsewhere, 'train/day/2026-10-30'), keyedRowId(sodium, account, 'train/day/2026-10-30'), ); }); test('another name, or the same name of another account, gives another ' 'ID', () { final other = AccountKeys.generate(sodium); final id = keyedRowId(sodium, account, 'train/day/2026-10-30'); expect(keyedRowId(sodium, account, 'train/day/2026-10-31'), isNot(id)); expect(keyedRowId(sodium, account, 'train/night/2026-10-30'), isNot(id)); expect(keyedRowId(sodium, other, 'train/day/2026-10-30'), isNot(id)); }); test("the ID is a UUID of version 8, RFC 9562's for IDs of one's own " 'making, and not a name hashed in the open (version 5)', () { final id = keyedRowId(sodium, account, 'train/day/2026-10-30'); expect( id, matches( RegExp( '^[0-9a-f]{8}-[0-9a-f]{4}-8[0-9a-f]{3}-[89ab][0-9a-f]{3}-' r'[0-9a-f]{12}$', ), ), ); }); test('a row without a name has no ID', () { expect(() => keyedRowId(sodium, account, ''), throwsArgumentError); }); }); }