16
16
namespace SimpleSAML \Test \Module \oidc \unit \Repositories ;
17
17
18
18
use DateTimeImmutable ;
19
+ use PDOStatement ;
19
20
use PHPUnit \Framework \MockObject \MockObject ;
20
21
use PHPUnit \Framework \MockObject \Stub ;
21
22
use PHPUnit \Framework \TestCase ;
27
28
use SimpleSAML \Module \oidc \ModuleConfig ;
28
29
use SimpleSAML \Module \oidc \Repositories \UserRepository ;
29
30
use SimpleSAML \Module \oidc \Services \DatabaseMigration ;
31
+ use SimpleSAML \Module \oidc \Utils \ProtocolCache ;
32
+ use Symfony \Component \VarDumper \Cloner \Data ;
30
33
31
34
/**
32
35
* @covers \SimpleSAML\Module\oidc\Repositories\UserRepository
@@ -37,6 +40,17 @@ class UserRepositoryTest extends TestCase
37
40
protected Stub $ helpersStub ;
38
41
protected MockObject $ userEntityFactoryMock ;
39
42
protected MockObject $ userEntityMock ;
43
+ protected MockObject $ moduleConfigMock ;
44
+ protected ?MockObject $ protocolCacheMock ;
45
+ protected MockObject $ databaseMock ;
46
+ protected MockObject $ pdoStatementMock ;
47
+ protected Database $ database ;
48
+ protected array $ userEntityState = [
49
+ 'id ' => 'uniqueid ' ,
50
+ 'claims ' => '[] ' ,
51
+ 'updated_at ' => '2024-11-04 11:07:26 ' ,
52
+ 'created_at ' => '2024-11-04 11:07:26 ' ,
53
+ ];
40
54
41
55
/**
42
56
* @throws \Exception
@@ -53,27 +67,44 @@ protected function setUp(): void
53
67
];
54
68
55
69
Configuration::loadFromArray ($ config , '' , 'simplesaml ' );
56
- (new DatabaseMigration ())->migrate ();
70
+ $ this ->database = Database::getInstance ();
71
+ (new DatabaseMigration ($ this ->database ))->migrate ();
57
72
58
- $ moduleConfig = new ModuleConfig ();
73
+ $ this ->databaseMock = $ this ->createMock (Database::class);
74
+ $ this ->pdoStatementMock = $ this ->createMock (PDOStatement::class);
75
+ $ this ->moduleConfigMock = $ this ->createMock (ModuleConfig::class);
59
76
$ this ->helpersStub = $ this ->createStub (Helpers::class);
60
77
$ this ->userEntityFactoryMock = $ this ->createMock (UserEntityFactory::class);
61
78
$ this ->userEntityMock = $ this ->createMock (UserEntity::class);
79
+ $ this ->protocolCacheMock = $ this ->createMock (ProtocolCache::class);
80
+ }
62
81
63
- $ database = Database::getInstance ();
82
+ protected function mock (
83
+ ModuleConfig |MockObject $ moduleConfig = null ,
84
+ Database |MockObject $ database = null ,
85
+ ProtocolCache |MockObject $ protocolCache = null ,
86
+ Helpers |MockObject $ helpers = null ,
87
+ UserEntityFactory |MockObject $ userEntityFactory = null
88
+ ): UserRepository
89
+ {
90
+ $ moduleConfig ??= $ this ->moduleConfigMock ;
91
+ $ database ??= $ this ->database ; // Let's use real database instance for tests by default.
92
+ $ protocolCache ??= null ; // Let's not use cache for tests by default.
93
+ $ helpers ??= $ this ->helpersStub ;
94
+ $ userEntityFactory ??= $ this ->userEntityFactoryMock ;
64
95
65
- self :: $ repository = new UserRepository (
96
+ return new UserRepository (
66
97
$ moduleConfig ,
67
98
$ database ,
68
- null ,
69
- $ this -> helpersStub ,
70
- $ this -> userEntityFactoryMock ,
99
+ $ protocolCache ,
100
+ $ helpers ,
101
+ $ userEntityFactory ,
71
102
);
72
103
}
73
104
74
105
public function testGetTableName (): void
75
106
{
76
- $ this ->assertSame ('phpunit_oidc_user ' , self :: $ repository ->getTableName ());
107
+ $ this ->assertSame ('phpunit_oidc_user ' , $ this -> mock () ->getTableName ());
77
108
}
78
109
79
110
/**
@@ -82,17 +113,21 @@ public function testGetTableName(): void
82
113
*/
83
114
public function testCanAddFindDelete (): void
84
115
{
116
+ $ repository = $ this ->mock ();
117
+
85
118
$ createdUpdatedAt = new DateTimeImmutable ();
86
- self ::$ repository ->add (new UserEntity ('uniqueid ' , $ createdUpdatedAt , $ createdUpdatedAt ));
119
+ $ userEntity = new UserEntity ('uniqueid ' , $ createdUpdatedAt , $ createdUpdatedAt );
120
+
121
+ $ repository ->add ($ userEntity );
87
122
88
- $ this ->userEntityMock ->method ('getIdentifier ' )->willReturn ('uniqueid ' );
89
123
$ this ->userEntityFactoryMock ->expects ($ this ->once ())
90
124
->method ('fromState ' )
91
125
->with ($ this ->callback (function (array $ state ) {
92
126
return $ state ['id ' ] === 'uniqueid ' ;
93
127
}))
94
- ->willReturn ($ this ->userEntityMock );
95
- $ user = self ::$ repository ->getUserEntityByIdentifier ('uniqueid ' );
128
+ ->willReturn ($ userEntity );
129
+
130
+ $ user = $ repository ->getUserEntityByIdentifier ('uniqueid ' );
96
131
97
132
$ this ->assertNotNull ($ user );
98
133
$ this ->assertSame ($ user ->getIdentifier (), 'uniqueid ' );
@@ -103,7 +138,7 @@ public function testCanAddFindDelete(): void
103
138
*/
104
139
public function testNotFound (): void
105
140
{
106
- $ user = self :: $ repository ->getUserEntityByIdentifier ('unknownid ' );
141
+ $ user = $ this -> mock () ->getUserEntityByIdentifier ('unknownid ' );
107
142
108
143
$ this ->assertNull ($ user );
109
144
}
@@ -114,19 +149,156 @@ public function testNotFound(): void
114
149
*/
115
150
public function testUpdate (): void
116
151
{
117
- $ user = self ::$ repository ->getUserEntityByIdentifier ('uniqueid ' );
152
+ $ repository = $ this ->mock ();
153
+
154
+ $ user = $ repository ->getUserEntityByIdentifier ('uniqueid ' );
118
155
$ user ->setClaims (['uid ' => ['johndoe ' ]]);
119
- self :: $ repository ->update ($ user );
156
+ $ repository ->update ($ user );
120
157
121
- $ user2 = self :: $ repository ->getUserEntityByIdentifier ('uniqueid ' );
158
+ $ user2 = $ repository ->getUserEntityByIdentifier ('uniqueid ' );
122
159
$ this ->assertNotSame ($ user , $ user2 );
123
160
}
124
161
125
162
public function testCanDelete (): void
126
163
{
164
+ $ repository = $ this ->mock ();
165
+
127
166
$ this ->userEntityMock ->method ('getIdentifier ' )->willReturn ('uniqueid ' );
128
- $ this ->assertNotNull (self ::$ repository ->getUserEntityByIdentifier ('uniqueid ' ));
129
- self ::$ repository ->delete ($ this ->userEntityMock );
130
- $ this ->assertNull (self ::$ repository ->getUserEntityByIdentifier ('uniqueid ' ));
167
+ $ this ->assertNotNull ($ repository ->getUserEntityByIdentifier ('uniqueid ' ));
168
+ $ repository ->delete ($ this ->userEntityMock );
169
+ $ this ->assertNull ($ repository ->getUserEntityByIdentifier ('uniqueid ' ));
170
+ }
171
+
172
+ public function testCanGetWhenUserEntityIsCached (): void
173
+ {
174
+ $ this ->protocolCacheMock ->expects ($ this ->once ())
175
+ ->method ('get ' )
176
+ ->willReturn ($ this ->userEntityState );
177
+
178
+ $ this ->databaseMock ->expects ($ this ->never ())->method ('read ' );
179
+
180
+ $ this ->userEntityFactoryMock ->expects ($ this ->once ())
181
+ ->method ('fromState ' )
182
+ ->with ($ this ->callback (function (array $ state ) {
183
+ return $ state ['id ' ] === 'uniqueid ' ;
184
+ }))
185
+ ->willReturn ($ this ->userEntityMock );
186
+
187
+ $ repository = $ this ->mock (
188
+ database: $ this ->databaseMock ,
189
+ protocolCache: $ this ->protocolCacheMock ,
190
+ );
191
+
192
+ $ this ->assertSame (
193
+ $ this ->userEntityMock ,
194
+ $ repository ->getUserEntityByIdentifier ('uniqueid ' ),
195
+ );
196
+ }
197
+
198
+ public function testCanGetWhenUserEntityIsNotCached (): void
199
+ {
200
+ $ this ->protocolCacheMock ->expects ($ this ->once ())
201
+ ->method ('get ' )
202
+ ->willReturn (null );
203
+
204
+ $ this ->pdoStatementMock ->method ('fetchAll ' )->willReturn ([$ this ->userEntityState ]);
205
+
206
+ $ this ->databaseMock ->expects ($ this ->once ())
207
+ ->method ('read ' )
208
+ ->willReturn ($ this ->pdoStatementMock );
209
+
210
+ $ this ->userEntityFactoryMock ->expects ($ this ->once ())
211
+ ->method ('fromState ' )
212
+ ->with ($ this ->callback (function (array $ state ) {
213
+ return $ state ['id ' ] === 'uniqueid ' ;
214
+ }))
215
+ ->willReturn ($ this ->userEntityMock );
216
+
217
+ $ repository = $ this ->mock (
218
+ database: $ this ->databaseMock ,
219
+ protocolCache: $ this ->protocolCacheMock ,
220
+ );
221
+
222
+ $ this ->assertSame (
223
+ $ this ->userEntityMock ,
224
+ $ repository ->getUserEntityByIdentifier ('uniqueid ' ),
225
+ );
226
+ }
227
+
228
+ public function testWillAddToDatabaseAndCache (): void
229
+ {
230
+ $ this ->moduleConfigMock ->method ('getProtocolUserEntityCacheDuration ' )
231
+ ->willReturn (new \DateInterval ('PT1H ' ));
232
+
233
+ $ this ->userEntityMock ->expects ($ this ->exactly (2 ))
234
+ ->method ('getState ' )
235
+ ->willReturn ($ this ->userEntityState );
236
+
237
+ $ this ->protocolCacheMock ->expects ($ this ->once ())
238
+ ->method ('set ' )
239
+ ->with ($ this ->userEntityState );
240
+
241
+ $ this ->databaseMock ->expects ($ this ->once ())
242
+ ->method ('write ' )
243
+ ->with (
244
+ $ this ->isType ('string ' ),
245
+ $ this ->userEntityState ,
246
+ );
247
+
248
+ $ this ->mock (
249
+ database: $ this ->databaseMock ,
250
+ protocolCache: $ this ->protocolCacheMock ,
251
+ )->add ($ this ->userEntityMock );
252
+ }
253
+
254
+ public function testWillUpdateDatabaseAndCache (): void
255
+ {
256
+ $ this ->moduleConfigMock ->method ('getProtocolUserEntityCacheDuration ' )
257
+ ->willReturn (new \DateInterval ('PT1H ' ));
258
+
259
+ $ this ->userEntityMock ->expects ($ this ->exactly (2 ))
260
+ ->method ('getState ' )
261
+ ->willReturn ($ this ->userEntityState );
262
+
263
+ $ this ->protocolCacheMock ->expects ($ this ->once ())
264
+ ->method ('set ' )
265
+ ->with ($ this ->userEntityState );
266
+
267
+ $ this ->databaseMock ->expects ($ this ->once ())
268
+ ->method ('write ' )
269
+ ->with (
270
+ $ this ->isType ('string ' ),
271
+ $ this ->userEntityState ,
272
+ );
273
+
274
+ $ this ->mock (
275
+ database: $ this ->databaseMock ,
276
+ protocolCache: $ this ->protocolCacheMock ,
277
+ )->update ($ this ->userEntityMock );
278
+ }
279
+
280
+ public function testWillDeleteFromDatabaseAndCache (): void
281
+ {
282
+ $ this ->userEntityMock ->expects ($ this ->exactly (2 ))
283
+ ->method ('getIdentifier ' )
284
+ ->willReturn ('uniqueid ' );
285
+
286
+ $ this ->protocolCacheMock ->expects ($ this ->once ())
287
+ ->method ('delete ' )
288
+ ->with ($ this ->stringContains ('uniqueid ' ));
289
+
290
+ $ this ->databaseMock ->expects ($ this ->once ())
291
+ ->method ('write ' )
292
+ ->with (
293
+ $ this ->stringContains ('DELETE ' ),
294
+ $ this ->callback (function (array $ params ) {
295
+ return $ params ['id ' ] === 'uniqueid ' ;
296
+ })
297
+ );
298
+
299
+ $ this ->mock (
300
+ database: $ this ->databaseMock ,
301
+ protocolCache: $ this ->protocolCacheMock ,
302
+ )->delete ($ this ->userEntityMock );
131
303
}
132
304
}
0 commit comments