22
33namespace App \Tests \Functional \User ;
44
5+ use App \Service \Registration \GenerateSignedUriService ;
56use App \Tests \Functional \ApiTestCase ;
67use DataFixtures \Data \UserFixtures ;
8+ use DataFixtures \Factory \UserFactory ;
79use PHPUnit \Framework \Attributes \DataProvider ;
8- use PHPUnit \Framework \Attributes \Group ;
910use PHPUnit \Framework \Attributes \Test ;
11+ use Symfony \Component \Clock \MockClock ;
1012use Symfony \Component \HttpFoundation \Response ;
13+ use Symfony \Component \HttpFoundation \UriSigner ;
14+ use Symfony \Component \Routing \Generator \UrlGeneratorInterface ;
1115
1216class RegistrationTest extends ApiTestCase
1317{
1418 #[Test]
1519 public function asAnonymousICanRegisterMyself (): void
1620 {
17- $ this ->client ->request ('POST ' , '/users ' , [
18- 'headers ' => [
19- 'Content-Type ' => 'application/json ' ,
20- ],
21- 'body ' => json_encode ([
22- 'email ' => 'new_account+alias@esports-videos.com ' ,
23- 'username ' => 'new_account ' ,
24- 'password ' => UserFixtures::DEFAULT_PASSWORD ,
25- 'country ' => 'FR ' ,
26- ], JSON_THROW_ON_ERROR ),
21+ $ this ->register ([
22+ 'email ' => 'new_account+alias@esports-videos.com ' ,
23+ 'username ' => 'new_account ' ,
24+ 'password ' => UserFixtures::DEFAULT_PASSWORD ,
2725 ]);
2826
2927 self ::assertResponseStatusCodeSame (Response::HTTP_CREATED );
@@ -35,8 +33,7 @@ public function asAnonymousICanRegisterMyself(): void
3533 */
3634 #[Test]
3735 #[DataProvider('invalidData ' )]
38- #[Group('debug ' )]
39- public function asAnonymousICannotRegisterWithInvalidPassword (?string $ email , ?string $ password , ?string $ username , ?string $ countryCode , array $ violations = []): void
36+ public function asAnonymousICannotRegisterWithInvalidData (?string $ email , ?string $ password , ?string $ username , ?string $ countryCode , array $ violations = []): void
4037 {
4138 $ data = array_filter ([
4239 'email ' => $ email ,
@@ -45,12 +42,7 @@ public function asAnonymousICannotRegisterWithInvalidPassword(?string $email, ?s
4542 'country ' => $ countryCode ,
4643 ], static fn ($ value ) => null !== $ value );
4744
48- $ this ->client ->request ('POST ' , '/users ' , [
49- 'headers ' => [
50- 'Content-Type ' => 'application/json ' ,
51- ],
52- 'body ' => json_encode ($ data , JSON_THROW_ON_ERROR ),
53- ]);
45+ $ this ->register ($ data );
5446
5547 self ::assertResponseStatusCodeSame (Response::HTTP_UNPROCESSABLE_ENTITY );
5648 self ::assertResponseHeaderSame ('content-type ' , 'application/problem+json; charset=utf-8 ' );
@@ -63,19 +55,111 @@ public function asAnonymousICannotRegisterWithInvalidPassword(?string $email, ?s
6355 self ::assertEmailCount (0 );
6456 }
6557
58+ #[Test]
59+ public function asAnonymousICanVerifyMyAccount (): void
60+ {
61+ $ container = static ::getContainer ();
62+ $ generateSignedUriService = $ container ->get (GenerateSignedUriService::class);
63+ $ user = UserFactory::find (UserFixtures::UNVERIFIED_USER_ULID );
64+
65+ $ this ->client ->request ('GET ' , $ generateSignedUriService ->generateSignedUri ($ user ));
66+
67+ self ::assertResponseStatusCodeSame (Response::HTTP_OK );
68+ }
69+
70+ #[Test]
71+ public function asAnonymousICantVerifyWithExpiredLink (): void
72+ {
73+ $ container = static ::getContainer ();
74+ $ clock = new MockClock (new \DateTimeImmutable ('-48 hours ' ));
75+
76+ $ generateSignedUriService = new GenerateSignedUriService (
77+ $ container ->get (UriSigner::class),
78+ $ container ->get (UrlGeneratorInterface::class),
79+ $ clock
80+ );
81+ $ user = UserFactory::find (UserFixtures::UNVERIFIED_USER_ULID );
82+
83+ $ signedUri = $ generateSignedUriService ->generateSignedUri ($ user );
84+
85+ $ this ->client ->request ('GET ' , $ signedUri );
86+ self ::assertResponseStatusCodeSame (Response::HTTP_BAD_REQUEST );
87+ }
88+
89+ #[Test]
90+ public function asAnonymousICantVerifyWithSameLinkTwice (): void
91+ {
92+ $ container = static ::getContainer ();
93+ /** @var GenerateSignedUriService $generateSignedUriService */
94+ $ generateSignedUriService = $ container ->get (GenerateSignedUriService::class);
95+ $ user = UserFactory::find (UserFixtures::UNVERIFIED_USER_ULID );
96+
97+ $ signedUri = $ generateSignedUriService ->generateSignedUri ($ user );
98+
99+ $ this ->client ->request ('GET ' , $ signedUri );
100+ self ::assertResponseStatusCodeSame (Response::HTTP_OK );
101+
102+ $ this ->client ->request ('GET ' , $ signedUri );
103+ self ::assertResponseStatusCodeSame (Response::HTTP_GONE );
104+ }
105+
106+ #[Test]
107+ public function asAnonymousICantVerifyAnAccountAlreadyValidated (): void
108+ {
109+ $ container = static ::getContainer ();
110+ $ generateSignedUriService = $ container ->get (GenerateSignedUriService::class);
111+ $ user = UserFactory::find (UserFixtures::ADMIN_ULID );
112+
113+ $ this ->client ->request ('GET ' , $ generateSignedUriService ->generateSignedUri ($ user ));
114+
115+ self ::assertResponseStatusCodeSame (Response::HTTP_GONE );
116+ }
117+
118+ #[Test]
119+ public function asAnonymousICantVerifyAnAccountWithInvalidHash (): void
120+ {
121+ $ this ->client ->request ('GET ' , sprintf ('/users/%s/verify/email?_hash=invalid ' , UserFixtures::UNVERIFIED_USER_ULID ));
122+
123+ self ::assertResponseStatusCodeSame (Response::HTTP_BAD_REQUEST );
124+ }
125+
126+ #[Test]
127+ public function asAnonymousICannotRegisterWithInvalidMimeType (): void
128+ {
129+ $ this ->client ->request ('POST ' , '/users ' , [
130+ 'headers ' => [
131+ 'Content-Type ' => 'text/plain ' ,
132+ ],
133+ 'body ' => '{"email":"foo@example.com"} ' ,
134+ ]);
135+
136+ self ::assertResponseStatusCodeSame (Response::HTTP_UNSUPPORTED_MEDIA_TYPE );
137+ }
138+
66139 public static function invalidData (): \Generator
67140 {
68- // Empty array
69- yield [
141+ yield 'empty_values ' => [
70142 'email ' => null ,
71143 'password ' => null ,
72144 'countryCode ' => null ,
73145 'username ' => null ,
74- 'violations ' => [],
146+ 'violations ' => [
147+ [
148+ 'propertyPath ' => 'email ' ,
149+ 'message ' => 'This value should not be blank. ' ,
150+ ],
151+ [
152+ 'propertyPath ' => 'username ' ,
153+ 'message ' => 'This value should not be blank. ' ,
154+ ],
155+ [
156+ 'propertyPath ' => 'password ' ,
157+ 'message ' => 'This value should not be blank. ' ,
158+ ],
159+ ],
75160 ];
76161
77- // Email is omitted
78- yield [
162+ yield 'omitted_email ' => [
79163 'email ' => null ,
80164 'password ' => UserFixtures::DEFAULT_PASSWORD ,
81165 'countryCode ' => 'FR ' ,
@@ -88,8 +172,7 @@ public static function invalidData(): \Generator
88172 ],
89173 ];
90174
91- // Password is omitted
92- yield [
175+ yield 'omitted_password ' => [
93176 'email ' => 'correct_email@esports-videos.com ' ,
94177 'password ' => null ,
95178 'countryCode ' => 'FR ' ,
@@ -102,8 +185,7 @@ public static function invalidData(): \Generator
102185 ],
103186 ];
104187
105- // Invalid Email
106- yield [
188+ yield 'invalid_email ' => [
107189 'email ' => 'invalidEmail ' ,
108190 'password ' => UserFixtures::DEFAULT_PASSWORD ,
109191 'countryCode ' => 'FR ' ,
@@ -116,8 +198,7 @@ public static function invalidData(): \Generator
116198 ],
117199 ];
118200
119- // Invalid Country code
120- yield [
201+ yield 'invalid_country_code ' => [
121202 'email ' => 'correct_email@esports-videos.com ' ,
122203 'password ' => UserFixtures::DEFAULT_PASSWORD ,
123204 'countryCode ' => 'BAD ' ,
@@ -130,8 +211,7 @@ public static function invalidData(): \Generator
130211 ],
131212 ];
132213
133- // Invalid Password
134- yield [
214+ yield 'invalid_password ' => [
135215 'email ' => 'correct_email@esports-videos.com ' ,
136216 'password ' => 'badPassword ' ,
137217 'countryCode ' => 'FR ' ,
@@ -143,5 +223,31 @@ public static function invalidData(): \Generator
143223 ],
144224 ],
145225 ];
226+
227+ yield 'email_already_exists ' => [
228+ 'email ' => 'admin@esports-videos.com ' ,
229+ 'password ' => UserFixtures::DEFAULT_PASSWORD ,
230+ 'countryCode ' => 'FR ' ,
231+ 'username ' => 'correct_username ' ,
232+ 'violations ' => [
233+ [
234+ 'propertyPath ' => 'email ' ,
235+ 'message ' => 'This value is already used. ' ,
236+ ],
237+ ],
238+ ];
239+ }
240+
241+ /**
242+ * @param array<string, mixed> $data
243+ */
244+ private function register (array $ data ): void
245+ {
246+ $ this ->client ->request ('POST ' , '/users ' , [
247+ 'headers ' => [
248+ 'Content-Type ' => 'application/json ' ,
249+ ],
250+ 'body ' => json_encode ($ data , JSON_THROW_ON_ERROR ),
251+ ]);
146252 }
147253}
0 commit comments