11import 'package:flutter_test/flutter_test.dart' ;
22import 'package:ht_authentication_client/ht_authentication_client.dart' ;
3- import 'package:ht_authentication_repository/src/ ht_authentication_repository.dart' ;
3+ import 'package:ht_authentication_repository/ht_authentication_repository.dart' ; // Import the public API
44import 'package:mocktail/mocktail.dart' ;
55import 'package:rxdart/rxdart.dart' ;
66
7+ // Mocks
78class MockHtAuthenticationClient extends Mock
89 implements HtAuthenticationClient {}
910
11+ class MockUser extends Mock
12+ implements User {} // Add MockUser if needed for tests
13+
14+ // Helper to create exceptions with mock StackTrace
15+ AuthException _createException (
16+ Object error,
17+ AuthException Function (Object , StackTrace ) constructor,
18+ ) {
19+ return constructor (error, StackTrace .current);
20+ }
21+
1022void main () {
1123 group ('HtAuthenticationRepository' , () {
1224 late HtAuthenticationClient client;
1325 late HtAuthenticationRepository repository;
26+ late BehaviorSubject <User > userSubject; // Declare userSubject here
1427
1528 setUp (() {
1629 client = MockHtAuthenticationClient ();
17- when (() => client.user).thenAnswer ((_) => const Stream .empty ());
30+ userSubject = BehaviorSubject <User >(); // Initialize userSubject
31+ // Mock client.user to return the stream from userSubject
32+ when (() => client.user).thenAnswer ((_) => userSubject.stream);
1833 repository = HtAuthenticationRepository (authenticationClient: client);
1934 });
2035
36+ // Add tearDown to close the subject
37+ tearDown (() {
38+ userSubject.close ();
39+ });
40+
2141 test ('constructor' , () {
2242 verify (() => client.user).called (1 );
2343 });
@@ -28,75 +48,139 @@ void main() {
2848 });
2949
3050 test ('emits new user when client emits' , () {
31- final user = User (uid: 'test-uid' );
32- final stream = BehaviorSubject <User >.seeded (user);
33- when (() => client.user).thenAnswer ((_) => stream);
34- repository = HtAuthenticationRepository (authenticationClient: client);
51+ final user1 = MockUser ();
52+ final user2 = User (uid: 'test-uid' ); // Use a concrete User or MockUser
53+
54+ // Expect the stream from the repository (which gets from userSubject)
55+ expectLater (
56+ repository.user,
57+ // Seeded with default User(), then user1, then user2
58+ emitsInOrder (< Matcher > [isA <User >(), equals (user1), equals (user2)]),
59+ );
3560
36- expect (repository.user, emitsInOrder ([isA <User >(), user]));
61+ // Add users to the subject the repository is listening to
62+ userSubject
63+ ..add (user1)
64+ ..add (user2);
3765 });
3866 });
3967
4068 group ('currentUser' , () {
41- test ('returns current user' , () {
42- expect (repository.currentUser, isA <User >());
69+ // Renamed test for clarity
70+ test ('reflects the latest user emitted by the stream' , () async {
71+ final user = MockUser ();
72+ // Optionally mock properties for better assertion
73+ when (() => user.uid).thenReturn ('mock-user-id' );
74+
75+ // Add the mock user to the stream the repository is listening to
76+ userSubject.add (user);
77+
78+ // Wait specifically for the 'user' we added to be emitted,
79+ // ignoring the initial default user.
80+ await expectLater (repository.user, emitsThrough (user));
81+
82+ // Now assert against the synchronous getter, which should be updated
83+ expect (repository.currentUser, equals (user));
84+ // Optionally assert properties
85+ expect (repository.currentUser.uid, equals ('mock-user-id' ));
4386 });
4487 });
4588
46- group ('signInWithEmailAndPassword' , () {
89+ group ('sendSignInLinkToEmail' , () {
90+ const email = 'test@example.com' ;
91+
4792 test ('calls client method' , () async {
4893 when (
49- () => client.signInWithEmailAndPassword (
50- email: any (named: 'email' ),
51- password: any (named: 'password' ),
52- ),
94+ () => client.sendSignInLinkToEmail (email: email),
5395 ).thenAnswer ((_) async {});
5496
55- await repository.signInWithEmailAndPassword (
56- email: 'test@example.com' ,
57- password: 'password' ,
97+ await repository.sendSignInLinkToEmail (email: email);
98+
99+ verify (() => client.sendSignInLinkToEmail (email: email)).called (1 );
100+ });
101+
102+ test ('throws SendSignInLinkException on client error' , () async {
103+ final exception = _createException (
104+ Exception (),
105+ SendSignInLinkException .new ,
106+ );
107+ when (
108+ () => client.sendSignInLinkToEmail (email: email),
109+ ).thenThrow (exception);
110+
111+ expect (
112+ () => repository.sendSignInLinkToEmail (email: email),
113+ throwsA (isA <SendSignInLinkException >()),
114+ );
115+ });
116+
117+ test ('throws SendSignInLinkException on general error' , () async {
118+ when (
119+ () => client.sendSignInLinkToEmail (email: email),
120+ ).thenThrow (Exception ());
121+
122+ expect (
123+ () => repository.sendSignInLinkToEmail (email: email),
124+ throwsA (isA <SendSignInLinkException >()),
58125 );
126+ });
127+ });
128+
129+ group ('signInWithEmailLink' , () {
130+ const email = 'test@example.com' ;
131+ const link = 'https://example.com/link' ;
132+
133+ test ('calls client method' , () async {
134+ when (
135+ () => client.signInWithEmailLink (email: email, emailLink: link),
136+ ).thenAnswer ((_) async {});
137+
138+ await repository.signInWithEmailLink (email: email, emailLink: link);
59139
60140 verify (
61- () => client.signInWithEmailAndPassword (
62- email: 'test@example.com' ,
63- password: 'password' ,
64- ),
141+ () => client.signInWithEmailLink (email: email, emailLink: link),
65142 ).called (1 );
66143 });
67144
68- test ('throws EmailSignInException on client error' , () async {
69- final exception = EmailSignInException (Exception (), StackTrace .empty);
145+ test ('throws InvalidSignInLinkException on client error' , () async {
146+ final exception = _createException (
147+ Exception (),
148+ InvalidSignInLinkException .new ,
149+ );
70150 when (
71- () => client.signInWithEmailAndPassword (
72- email: any (named: 'email' ),
73- password: any (named: 'password' ),
74- ),
151+ () => client.signInWithEmailLink (email: email, emailLink: link),
75152 ).thenThrow (exception);
76153
77154 expect (
78- () => repository.signInWithEmailAndPassword (
79- email: 'test@example.com' ,
80- password: 'password' ,
81- ),
82- throwsA (isA <EmailSignInException >()),
155+ () => repository.signInWithEmailLink (email: email, emailLink: link),
156+ throwsA (isA <InvalidSignInLinkException >()),
83157 );
84158 });
85159
86- test ('throws EmailSignInException on general error' , () async {
160+ test ('throws UserNotFoundException on client error' , () async {
161+ final exception = _createException (
162+ Exception (),
163+ UserNotFoundException .new ,
164+ );
87165 when (
88- () => client.signInWithEmailAndPassword (
89- email: any (named: 'email' ),
90- password: any (named: 'password' ),
91- ),
166+ () => client.signInWithEmailLink (email: email, emailLink: link),
167+ ).thenThrow (exception);
168+
169+ expect (
170+ () => repository.signInWithEmailLink (email: email, emailLink: link),
171+ throwsA (isA <UserNotFoundException >()),
172+ );
173+ });
174+
175+ test ('throws InvalidSignInLinkException on general error' , () async {
176+ when (
177+ () => client.signInWithEmailLink (email: email, emailLink: link),
92178 ).thenThrow (Exception ());
93179
180+ // Repository wraps general errors into InvalidSignInLinkException
94181 expect (
95- () => repository.signInWithEmailAndPassword (
96- email: 'test@example.com' ,
97- password: 'password' ,
98- ),
99- throwsA (isA <EmailSignInException >()),
182+ () => repository.signInWithEmailLink (email: email, emailLink: link),
183+ throwsA (isA <InvalidSignInLinkException >()),
100184 );
101185 });
102186 });
@@ -111,7 +195,10 @@ void main() {
111195 });
112196
113197 test ('throws GoogleSignInException on client error' , () async {
114- final exception = GoogleSignInException (Exception (), StackTrace .empty);
198+ final exception = _createException (
199+ Exception (),
200+ GoogleSignInException .new ,
201+ );
115202 when (() => client.signInWithGoogle ()).thenThrow (exception);
116203
117204 expect (
@@ -140,9 +227,9 @@ void main() {
140227 });
141228
142229 test ('throws AnonymousLoginException on client error' , () async {
143- final exception = AnonymousLoginException (
230+ final exception = _createException (
144231 Exception (),
145- StackTrace .empty ,
232+ AnonymousLoginException . new ,
146233 );
147234 when (() => client.signInAnonymously ()).thenThrow (exception);
148235
@@ -172,7 +259,7 @@ void main() {
172259 });
173260
174261 test ('throws LogoutException on client error' , () async {
175- final exception = LogoutException (Exception (), StackTrace .empty );
262+ final exception = _createException (Exception (), LogoutException . new );
176263 when (() => client.signOut ()).thenThrow (exception);
177264
178265 expect (() => repository.signOut (), throwsA (isA <LogoutException >()));
@@ -195,7 +282,10 @@ void main() {
195282 });
196283
197284 test ('throws DeleteAccountException on client error' , () async {
198- final exception = DeleteAccountException (Exception (), StackTrace .empty);
285+ final exception = _createException (
286+ Exception (),
287+ DeleteAccountException .new ,
288+ );
199289 when (() => client.deleteAccount ()).thenThrow (exception);
200290
201291 expect (
0 commit comments