1616namespace SimpleSAML \Test \Module \oidc \unit \Repositories ;
1717
1818use DateTimeImmutable ;
19+ use PDOStatement ;
1920use PHPUnit \Framework \MockObject \MockObject ;
2021use PHPUnit \Framework \MockObject \Stub ;
2122use PHPUnit \Framework \TestCase ;
2728use SimpleSAML \Module \oidc \ModuleConfig ;
2829use SimpleSAML \Module \oidc \Repositories \UserRepository ;
2930use SimpleSAML \Module \oidc \Services \DatabaseMigration ;
31+ use SimpleSAML \Module \oidc \Utils \ProtocolCache ;
32+ use Symfony \Component \VarDumper \Cloner \Data ;
3033
3134/**
3235 * @covers \SimpleSAML\Module\oidc\Repositories\UserRepository
@@ -37,6 +40,17 @@ class UserRepositoryTest extends TestCase
3740 protected Stub $ helpersStub ;
3841 protected MockObject $ userEntityFactoryMock ;
3942 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+ ];
4054
4155 /**
4256 * @throws \Exception
@@ -53,27 +67,44 @@ protected function setUp(): void
5367 ];
5468
5569 Configuration::loadFromArray ($ config , '' , 'simplesaml ' );
56- (new DatabaseMigration ())->migrate ();
70+ $ this ->database = Database::getInstance ();
71+ (new DatabaseMigration ($ this ->database ))->migrate ();
5772
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);
5976 $ this ->helpersStub = $ this ->createStub (Helpers::class);
6077 $ this ->userEntityFactoryMock = $ this ->createMock (UserEntityFactory::class);
6178 $ this ->userEntityMock = $ this ->createMock (UserEntity::class);
79+ $ this ->protocolCacheMock = $ this ->createMock (ProtocolCache::class);
80+ }
6281
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 ;
6495
65- self :: $ repository = new UserRepository (
96+ return new UserRepository (
6697 $ moduleConfig ,
6798 $ database ,
68- null ,
69- $ this -> helpersStub ,
70- $ this -> userEntityFactoryMock ,
99+ $ protocolCache ,
100+ $ helpers ,
101+ $ userEntityFactory ,
71102 );
72103 }
73104
74105 public function testGetTableName (): void
75106 {
76- $ this ->assertSame ('phpunit_oidc_user ' , self :: $ repository ->getTableName ());
107+ $ this ->assertSame ('phpunit_oidc_user ' , $ this -> mock () ->getTableName ());
77108 }
78109
79110 /**
@@ -82,17 +113,21 @@ public function testGetTableName(): void
82113 */
83114 public function testCanAddFindDelete (): void
84115 {
116+ $ repository = $ this ->mock ();
117+
85118 $ createdUpdatedAt = new DateTimeImmutable ();
86- self ::$ repository ->add (new UserEntity ('uniqueid ' , $ createdUpdatedAt , $ createdUpdatedAt ));
119+ $ userEntity = new UserEntity ('uniqueid ' , $ createdUpdatedAt , $ createdUpdatedAt );
120+
121+ $ repository ->add ($ userEntity );
87122
88- $ this ->userEntityMock ->method ('getIdentifier ' )->willReturn ('uniqueid ' );
89123 $ this ->userEntityFactoryMock ->expects ($ this ->once ())
90124 ->method ('fromState ' )
91125 ->with ($ this ->callback (function (array $ state ) {
92126 return $ state ['id ' ] === 'uniqueid ' ;
93127 }))
94- ->willReturn ($ this ->userEntityMock );
95- $ user = self ::$ repository ->getUserEntityByIdentifier ('uniqueid ' );
128+ ->willReturn ($ userEntity );
129+
130+ $ user = $ repository ->getUserEntityByIdentifier ('uniqueid ' );
96131
97132 $ this ->assertNotNull ($ user );
98133 $ this ->assertSame ($ user ->getIdentifier (), 'uniqueid ' );
@@ -103,7 +138,7 @@ public function testCanAddFindDelete(): void
103138 */
104139 public function testNotFound (): void
105140 {
106- $ user = self :: $ repository ->getUserEntityByIdentifier ('unknownid ' );
141+ $ user = $ this -> mock () ->getUserEntityByIdentifier ('unknownid ' );
107142
108143 $ this ->assertNull ($ user );
109144 }
@@ -114,19 +149,156 @@ public function testNotFound(): void
114149 */
115150 public function testUpdate (): void
116151 {
117- $ user = self ::$ repository ->getUserEntityByIdentifier ('uniqueid ' );
152+ $ repository = $ this ->mock ();
153+
154+ $ user = $ repository ->getUserEntityByIdentifier ('uniqueid ' );
118155 $ user ->setClaims (['uid ' => ['johndoe ' ]]);
119- self :: $ repository ->update ($ user );
156+ $ repository ->update ($ user );
120157
121- $ user2 = self :: $ repository ->getUserEntityByIdentifier ('uniqueid ' );
158+ $ user2 = $ repository ->getUserEntityByIdentifier ('uniqueid ' );
122159 $ this ->assertNotSame ($ user , $ user2 );
123160 }
124161
125162 public function testCanDelete (): void
126163 {
164+ $ repository = $ this ->mock ();
165+
127166 $ 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 );
131303 }
132304}
0 commit comments