diff --git a/lib/utils/id.dart b/lib/utils/id.dart index b27a186..9feea8f 100644 --- a/lib/utils/id.dart +++ b/lib/utils/id.dart @@ -1,8 +1,9 @@ import 'dart:math'; import 'dart:typed_data'; -// 64 characters (2^6). This intentionally excludes "_" because Loon paths use -// "__" as their segment delimiter. +// 64 characters (2^6). Generated IDs are used as "__"-delimited path +// segments throughout the store, so "_" is excluded to prevent generated IDs +// from introducing path boundaries. const String _alphabet = '-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~'; final Uint8List _alphabytes = Uint8List.fromList(_alphabet.codeUnits); diff --git a/test/core/id_test.dart b/test/core/id_test.dart index 6a34d69..9cd694e 100644 --- a/test/core/id_test.dart +++ b/test/core/id_test.dart @@ -2,12 +2,29 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:loon/utils/id.dart'; void main() { - group('IDs', () { - test('generated IDs do not contain the path delimiter', () { - for (var i = 0; i < 1000; i++) { - expect(generateSecureId(), isNot(contains('__'))); - expect(generateFastId(), isNot(contains('__'))); - } + for (final entry in { + 'generateSecureId': generateSecureId, + 'generateFastId': generateFastId, + }.entries) { + final name = entry.key; + final generate = entry.value; + + group(name, () { + test('generates IDs of the requested length', () { + expect(generate(), hasLength(21)); + expect(generate(8), hasLength(8)); + }); + + test('uses only path-safe characters', () { + final pathSafeId = RegExp(r'^[-0-9A-Za-z~]+$'); + + for (var i = 0; i < 1000; i++) { + final id = generate(); + + expect(id, isNot(contains('__'))); + expect(pathSafeId.hasMatch(id), true, reason: 'id "$id"'); + } + }); }); - }); + } }