IconControllerTest.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. namespace Tests\Api\v1\Controllers;
  3. use App\Api\v1\Controllers\IconController;
  4. use App\Facades\IconStore;
  5. use App\Models\TwoFAccount;
  6. use App\Models\User;
  7. use App\Services\LogoService;
  8. use Illuminate\Http\Testing\FileFactory;
  9. use Illuminate\Http\UploadedFile;
  10. use Illuminate\Support\Facades\Http;
  11. use Illuminate\Support\Facades\Storage;
  12. use PHPUnit\Framework\Attributes\CoversClass;
  13. use PHPUnit\Framework\Attributes\Test;
  14. use Tests\Classes\LocalFile;
  15. use Tests\Data\HttpRequestTestData;
  16. use Tests\Data\OtpTestData;
  17. use Tests\FeatureTestCase;
  18. /**
  19. * IconController test class
  20. */
  21. #[CoversClass(IconController::class)]
  22. class IconControllerTest extends FeatureTestCase
  23. {
  24. /**
  25. * @var \App\Models\User|\Illuminate\Contracts\Auth\Authenticatable
  26. */
  27. protected $user;
  28. public function setUp() : void
  29. {
  30. parent::setUp();
  31. Storage::fake('icons');
  32. Storage::fake('logos');
  33. Http::preventStrayRequests();
  34. Http::fake([
  35. LogoService::TFA_IMG_URL . '*' => Http::response(HttpRequestTestData::SVG_LOGO_BODY, 200),
  36. LogoService::TFA_URL => Http::response(HttpRequestTestData::TFA_JSON_BODY, 200),
  37. ]);
  38. Http::fake([
  39. OtpTestData::EXTERNAL_IMAGE_URL_DECODED => Http::response((new FileFactory)->image('file.png', 10, 10)->tempFile, 200),
  40. ]);
  41. $this->user = User::factory()->create();
  42. }
  43. #[Test]
  44. public function test_upload_icon_returns_filename_using_the_iconStore()
  45. {
  46. $iconName = 'testIcon.jpg';
  47. $file = UploadedFile::fake()->image($iconName);
  48. $response = $this->actingAs($this->user, 'api-guard')
  49. ->json('POST', '/api/v1/icons', [
  50. 'icon' => $file,
  51. ])
  52. ->assertCreated()
  53. ->assertJsonStructure([
  54. 'filename',
  55. ]);
  56. }
  57. #[Test]
  58. public function test_upload_icon_stores_it_to_database()
  59. {
  60. $file = UploadedFile::fake()->image('testIcon.jpg');
  61. $response = $this->actingAs($this->user, 'api-guard')
  62. ->json('POST', '/api/v1/icons', [
  63. 'icon' => $file,
  64. ]);
  65. }
  66. #[Test]
  67. public function test_upload_with_invalid_data_returns_validation_error()
  68. {
  69. $response = $this->actingAs($this->user, 'api-guard')
  70. ->json('POST', '/api/v1/icons', [
  71. 'icon' => null,
  72. ])
  73. ->assertStatus(422);
  74. }
  75. #[Test]
  76. public function test_upload_infected_svg_data_stores_stores_sanitized_svg_content()
  77. {
  78. $file = LocalFile::fake()->infectedSvgIconFile();
  79. $response = $this->actingAs($this->user, 'api-guard')
  80. ->json('POST', '/api/v1/icons', [
  81. 'icon' => $file,
  82. ])
  83. ->assertCreated();
  84. $svgContent = IconStore::get($response->getData()->filename);
  85. $this->assertStringNotContainsString(OtpTestData::ICON_SVG_MALICIOUS_CODE, $svgContent);
  86. }
  87. #[Test]
  88. public function test_fetch_logo_returns_filename()
  89. {
  90. $response = $this->actingAs($this->user, 'api-guard')
  91. ->json('POST', '/api/v1/icons/default', [
  92. 'service' => 'service',
  93. ])
  94. ->assertStatus(201)
  95. ->assertJsonStructure([
  96. 'filename',
  97. ]);
  98. }
  99. #[Test]
  100. public function test_fetch_logo_with_infected_svg_data_stores_sanitized_svg_content()
  101. {
  102. $response = $this->actingAs($this->user, 'api-guard')
  103. ->json('POST', '/api/v1/icons/default', [
  104. 'service' => 'service',
  105. ])
  106. ->assertStatus(201)
  107. ->assertJsonStructure([
  108. 'filename',
  109. ]);
  110. $svgContent = IconStore::get($response->getData()->filename);
  111. $this->assertStringNotContainsString(OtpTestData::ICON_SVG_MALICIOUS_CODE, $svgContent);
  112. }
  113. #[Test]
  114. public function test_fetch_unknown_logo_returns_nothing()
  115. {
  116. $response = $this->actingAs($this->user, 'api-guard')
  117. ->json('POST', '/api/v1/icons/default', [
  118. 'service' => 'unknown_company',
  119. ])
  120. ->assertNoContent();
  121. }
  122. #[Test]
  123. public function test_delete_icon_returns_success_using_the_iconStore()
  124. {
  125. IconStore::spy();
  126. $iconName = 'testIcon.jpg';
  127. $response = $this->actingAs($this->user, 'api-guard')
  128. ->json('DELETE', '/api/v1/icons/' . $iconName)
  129. ->assertNoContent(204);
  130. IconStore::shouldHaveReceived('delete')->once()->with($iconName);
  131. }
  132. #[Test]
  133. public function test_delete_invalid_icon_returns_success()
  134. {
  135. $response = $this->actingAs($this->user, 'api-guard')
  136. ->json('DELETE', '/api/v1/icons/null')
  137. ->assertNoContent(204);
  138. }
  139. #[Test]
  140. public function test_delete_icon_of_another_user_is_forbidden()
  141. {
  142. $anotherUser = User::factory()->create();
  143. TwoFAccount::factory()->for($anotherUser)->create([
  144. 'icon' => 'testIcon.jpg',
  145. ]);
  146. $response = $this->actingAs($this->user, 'api-guard')
  147. ->json('DELETE', '/api/v1/icons/testIcon.jpg')
  148. ->assertForbidden()
  149. ->assertJsonStructure([
  150. 'message',
  151. ]);
  152. }
  153. }