ForumApiTest.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <?php
  2. use PHPUnit\Framework\Attributes\Group;
  3. /**
  4. * Discussions for the app: every discussion names its category (with the
  5. * icon the site uses when Studio sets none), and the unit meta sends the
  6. * reactions element.
  7. *
  8. * The test makes its own discussion and image, and restores the category
  9. * icon row it changes, so it runs on any site with Discussions installed.
  10. */
  11. #[Group('integration')]
  12. class ForumApiTest extends BxDolIntegrationTestCase
  13. {
  14. protected $_aGetBackup;
  15. protected $_aContent;
  16. protected $_aCategoryRow;
  17. protected $_iImageId = 0;
  18. protected function setUp(): void
  19. {
  20. parent::setUp();
  21. $this->_aGetBackup = $_GET;
  22. if (!BxDolModuleQuery::getInstance()->isEnabledByName('bx_forum'))
  23. $this->markTestSkipped('Discussions are not installed.');
  24. $oDb = BxDolDb::getInstance();
  25. $iCategory = (int)$oDb->getOne("SELECT `Value` FROM `sys_form_pre_values` WHERE `Key` = 'bx_forum_cats' AND `Value` <> '' ORDER BY `Order` LIMIT 1");
  26. $this->assertGreaterThan(0, $iCategory, 'Discussions install at least one category.');
  27. $iAuthor = (int)BxDolProfile::getInstanceAccountProfile($this->bxAccount('admin')->id())->id();
  28. $iNow = time();
  29. $oDb->query("INSERT INTO `bx_forum_discussions` SET `author` = :author, `added` = :now, `changed` = :now, `lr_timestamp` = :now, `thumb` = 0, `thumb_data` = '', `title` = 'ForumApiTest discussion', `cat` = :cat, `multicat` = '', `text` = '<p>Fixture</p>', `text_comments` = '', `lr_profile_id` = :author, `lr_comment_id` = 0, `labels` = ''", [
  30. 'author' => $iAuthor,
  31. 'now' => $iNow,
  32. 'cat' => $iCategory,
  33. ]);
  34. $this->_aContent = $oDb->getRow("SELECT * FROM `bx_forum_discussions` WHERE `id` = :id", ['id' => (int)$oDb->lastId()]);
  35. // The tests change the category's icon row; keep the original to restore it.
  36. $this->_aCategoryRow = $oDb->getRow("SELECT * FROM `bx_forum_categories` WHERE `category` = :category", ['category' => $iCategory]);
  37. }
  38. protected function tearDown(): void
  39. {
  40. $oDb = BxDolDb::getInstance();
  41. if (!empty($this->_aContent)) {
  42. $iCategory = (int)$this->_aContent['cat'];
  43. $oDb->query("DELETE FROM `bx_forum_categories` WHERE `category` = :category", ['category' => $iCategory]);
  44. if (!empty($this->_aCategoryRow))
  45. $oDb->query("INSERT INTO `bx_forum_categories` SET `category` = :category, `visible_for_levels` = :levels, `icon` = :icon", [
  46. 'category' => $iCategory,
  47. 'levels' => (int)$this->_aCategoryRow['visible_for_levels'],
  48. 'icon' => $this->_aCategoryRow['icon'],
  49. ]);
  50. $oDb->query("DELETE FROM `bx_forum_discussions` WHERE `id` = :id", ['id' => (int)$this->_aContent['id']]);
  51. }
  52. if ($this->_iImageId)
  53. BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES)->deleteFile($this->_iImageId, 0);
  54. $oDb->cleanMemoryAll();
  55. $_GET = $this->_aGetBackup ?? [];
  56. parent::tearDown();
  57. }
  58. protected function setCategoryIcon(?string $sIcon): void
  59. {
  60. $oDb = BxDolDb::getInstance();
  61. $iCategory = (int)$this->_aContent['cat'];
  62. $oDb->query("DELETE FROM `bx_forum_categories` WHERE `category` = :category", ['category' => $iCategory]);
  63. if ($sIcon !== null)
  64. $oDb->query("INSERT INTO `bx_forum_categories` SET `category` = :category, `icon` = :icon", ['category' => $iCategory, 'icon' => $sIcon]);
  65. $oDb->cleanMemoryAll();
  66. }
  67. protected function decode(array $aContent): array
  68. {
  69. $aResult = BxDolModule::getInstance('bx_forum')->decodeDataApi($aContent);
  70. $this->assertIsArray($aResult);
  71. return $aResult;
  72. }
  73. public function testCategoryWithoutIconRowFallsBackToFolder(): void
  74. {
  75. $this->setCategoryIcon(null);
  76. $aResult = $this->decode($this->_aContent);
  77. $this->assertArrayHasKey('category', $aResult);
  78. $this->assertNotSame('', $aResult['category']['name']);
  79. $this->assertSame('folder', $aResult['category']['icon']);
  80. }
  81. public function testCategoryIconFromStudio(): void
  82. {
  83. $this->setCategoryIcon('💬');
  84. $this->assertSame('💬', $this->decode($this->_aContent)['category']['icon']);
  85. }
  86. public function testUploadedIconIsSentAsItsUrl(): void
  87. {
  88. // tempnam() creates its own file; the image goes next to it with a .png name.
  89. $sTmp = tempnam(sys_get_temp_dir(), 'bxforum');
  90. $sPath = $sTmp . '.png';
  91. $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
  92. try {
  93. copy(BX_DIRECTORY_PATH_ROOT . 'template/images/acl-account.png', $sPath);
  94. $this->_iImageId = (int)$oStorage->storeFileFromPath($sPath, false, 0);
  95. }
  96. finally {
  97. @unlink($sPath);
  98. @unlink($sTmp);
  99. }
  100. if (!$this->_iImageId)
  101. $this->fail('Store the icon image: ' . $oStorage->getErrorString());
  102. $this->setCategoryIcon((string)$this->_iImageId);
  103. $sUrl = $oStorage->getFileUrlById($this->_iImageId);
  104. $this->assertNotEmpty($sUrl);
  105. $this->assertSame($sUrl, $this->decode($this->_aContent)['category']['icon']);
  106. }
  107. public function testMissingUploadedIconIsNeverTheBareId(): void
  108. {
  109. // A storage id with no file behind it: no URL, and never the id.
  110. $this->setCategoryIcon('2147483000');
  111. $this->assertSame('', $this->decode($this->_aContent)['category']['icon']);
  112. }
  113. public function testNoCategoryNoKey(): void
  114. {
  115. $aResult = $this->decode(array_merge($this->_aContent, ['cat' => 0]));
  116. $this->assertArrayNotHasKey('category', $aResult);
  117. }
  118. public function testUnitMetaSendsReactionsElement(): void
  119. {
  120. $this->bxLoginAsAdmin();
  121. $_GET['api'] = 1;
  122. // Reactions on for this test only (in memory, the site setting is untouched).
  123. $aSystems = &BxDolVote::getSystems();
  124. $iIsOn = $aSystems['bx_forum_reactions']['is_on'] ?? null;
  125. $this->assertNotNull($iIsOn, 'Discussions install the bx_forum_reactions object.');
  126. $aSystems['bx_forum_reactions']['is_on'] = 1;
  127. $sVoteKey = 'BxDolVote!bx_forum_reactions' . (int)$this->_aContent['id'];
  128. unset($GLOBALS['bxDolClasses'][$sVoteKey]);
  129. $oMenu = BxDolMenu::getObjectInstance('bx_forum_snippet_meta_main');
  130. $this->assertInstanceOf(BxBaseModGeneralMenuSnippetMeta::class, $oMenu);
  131. $oIsApi = new ReflectionProperty($oMenu, '_bIsApi');
  132. $bIsApi = $oIsApi->getValue($oMenu);
  133. try {
  134. $oIsApi->setValue($oMenu, true);
  135. $oMenu->setContentId((int)$this->_aContent['id']);
  136. $aItem = $this->bxCallProtected($oMenu, '_getMenuItemReactions', ['id' => 1, 'name' => 'reactions']);
  137. }
  138. finally {
  139. // The menu and vote instances are shared: put them back as they were.
  140. $oIsApi->setValue($oMenu, $bIsApi);
  141. $aSystems['bx_forum_reactions']['is_on'] = $iIsOn;
  142. unset($GLOBALS['bxDolClasses'][$sVoteKey]);
  143. }
  144. $this->assertIsArray($aItem);
  145. $this->assertSame('element', $aItem['display_type']);
  146. $this->assertSame('reactions', $aItem['data']['type']);
  147. $this->assertSame((int)$this->_aContent['id'], (int)$aItem['data']['object_id']);
  148. }
  149. }