import 'package:flutter_test/flutter_test.dart'; import 'package:marianum_mobile/auth_link/device_binding.dart'; import 'package:marianum_mobile/auth_link/guardian_login_link.dart'; import 'package:marianum_mobile/auth_link/pending_guardian_request.dart'; void main() { final live = Uri.parse('https://connect.marianum-fulda.de'); group('GuardianLoginLink.parse', () { test('accepts a link of the active server', () { final link = GuardianLoginLink.parse( Uri.parse( 'https://connect.marianum-fulda.de/app/guardian-login?rid=r1<=t1', ), apiBase: live, ); expect(link?.requestId, 'r1'); expect(link?.linkToken, 't1'); }); test('rejects links of another server', () { expect( GuardianLoginLink.parse( Uri.parse( 'https://connect-beta.marianum-fulda.de/app/guardian-login?rid=r<=t', ), apiBase: live, ), isNull, ); }); test('respects a path prefix of a custom server', () { final custom = Uri.parse('https://dev.example.org/connect/'); expect( GuardianLoginLink.parse( Uri.parse( 'https://dev.example.org/connect/app/guardian-login?rid=r<=t', ), apiBase: custom, ), isNotNull, ); }); test('rejects other paths, plain http and missing parameters', () { for (final raw in [ 'https://connect.marianum-fulda.de/app/other?rid=r<=t', 'http://connect.marianum-fulda.de/app/guardian-login?rid=r<=t', 'https://connect.marianum-fulda.de/app/guardian-login?rid=r', 'https://connect.marianum-fulda.de/app/guardian-login?lt=t', ]) { expect( GuardianLoginLink.parse(Uri.parse(raw), apiBase: live), isNull, reason: raw, ); } }); }); group('DeviceBinding', () { test('challenge is the base64url SHA-256 of the secret, unpadded', () { // RFC 7636 appendix B test vector. expect( DeviceBinding.challengeFor( 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk', ), 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM', ); }); test('secrets are random and URL-safe', () { final a = DeviceBinding.generateSecret(); final b = DeviceBinding.generateSecret(); expect(a, isNot(b)); expect(a, matches(RegExp(r'^[A-Za-z0-9_-]{43}$'))); }); }); group('PendingGuardianRequest', () { final request = PendingGuardianRequest( requestId: 'r1', email: 'e@x.de', deviceSecret: 's', expiresAt: DateTime.utc(2026, 9, 19, 12, 15), resendAvailableAt: DateTime.utc(2026, 9, 19, 12, 1), ); test('round-trips through JSON', () { final copy = PendingGuardianRequest.fromJson(request.toJson())!; expect(copy.requestId, 'r1'); expect(copy.email, 'e@x.de'); expect(copy.expiresAt, request.expiresAt); expect(copy.codeLength, 6); }); test('corrupt JSON reads as absent', () { expect(PendingGuardianRequest.fromJson({'requestId': 1}), isNull); }); test('expires at expiresAt', () { expect(request.isExpired(DateTime.utc(2026, 9, 19, 12, 14)), isFalse); expect(request.isExpired(DateTime.utc(2026, 9, 19, 12, 15)), isTrue); }); }); }