BitmaskedBehaviorTest.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. <?php
  2. namespace Tools\Test\TestCase\Model\Behavior;
  3. use App\Model\Entity\BitmaskedComment;
  4. use Cake\ORM\TableRegistry;
  5. use RuntimeException;
  6. use Tools\TestSuite\TestCase;
  7. class BitmaskedBehaviorTest extends TestCase {
  8. /**
  9. * @var array
  10. */
  11. public $fixtures = [
  12. 'plugin.Tools.BitmaskedComments',
  13. ];
  14. /**
  15. * @var \Tools\Model\Table\Table|\Tools\Model\Behavior\BitmaskedBehavior
  16. */
  17. protected $Comments;
  18. /**
  19. * @return void
  20. */
  21. public function setUp(): void {
  22. parent::setUp();
  23. $this->Comments = TableRegistry::getTableLocator()->get('BitmaskedComments');
  24. $this->Comments->addBehavior('Tools.Bitmasked', ['mappedField' => 'statuses']);
  25. }
  26. /**
  27. * @return void
  28. */
  29. public function testConfig() {
  30. $this->Comments->removeBehavior('Bitmasked');
  31. $this->Comments->addBehavior('Tools.Bitmasked', []);
  32. $bits = $this->Comments->behaviors()->Bitmasked->getConfig('bits');
  33. $expected = BitmaskedComment::statuses();
  34. $this->assertSame($expected, $bits);
  35. }
  36. /**
  37. * @return void
  38. */
  39. public function testFieldMethodMissing() {
  40. $this->Comments->removeBehavior('Bitmasked');
  41. $this->expectException(RuntimeException::class);
  42. $this->expectExceptionMessage('Bits not found for field my_field, expected pluralized static method myFields() on the entity.');
  43. $this->Comments->addBehavior('Tools.Bitmasked', ['field' => 'my_field']);
  44. }
  45. /**
  46. * @return void
  47. */
  48. public function testEncodeBitmask() {
  49. $res = $this->Comments->encodeBitmask([BitmaskedComment::STATUS_PUBLISHED, BitmaskedComment::STATUS_APPROVED]);
  50. $expected = BitmaskedComment::STATUS_PUBLISHED | BitmaskedComment::STATUS_APPROVED;
  51. $this->assertSame($expected, $res);
  52. }
  53. /**
  54. * @return void
  55. */
  56. public function testDecodeBitmask() {
  57. $res = $this->Comments->decodeBitmask(BitmaskedComment::STATUS_PUBLISHED | BitmaskedComment::STATUS_APPROVED);
  58. $expected = [BitmaskedComment::STATUS_PUBLISHED, BitmaskedComment::STATUS_APPROVED];
  59. $this->assertSame($expected, $res);
  60. }
  61. /**
  62. * @return void
  63. */
  64. public function testFind() {
  65. $res = $this->Comments->find('all')->toArray();
  66. $this->assertTrue(!empty($res) && is_array($res));
  67. $this->assertTrue(!empty($res[1]['statuses']) && is_array($res[1]['statuses']));
  68. }
  69. /**
  70. * @return void
  71. */
  72. public function testFindBitmasked() {
  73. $res = $this->Comments->find('bits', ['bits' => []])->toArray();
  74. $this->assertCount(1, $res);
  75. $this->assertSame([], $res[0]->statuses);
  76. $res = $this->Comments->find('bits', ['bits' => [BitmaskedComment::STATUS_ACTIVE, BitmaskedComment::STATUS_APPROVED]])->toArray();
  77. $this->assertCount(1, $res);
  78. $this->assertSame([BitmaskedComment::STATUS_ACTIVE, BitmaskedComment::STATUS_APPROVED], $res[0]->statuses);
  79. }
  80. /**
  81. * @return void
  82. */
  83. public function testFindBitmaskedContain() {
  84. $options = [
  85. 'bits' => [],
  86. 'type' => 'contain',
  87. ];
  88. $res = $this->Comments->find('bits', $options)->toArray();
  89. $this->assertCount(1, $res);
  90. $this->assertSame([], $res[0]->statuses);
  91. $options = [
  92. 'bits' => [BitmaskedComment::STATUS_APPROVED],
  93. 'type' => 'contain',
  94. ];
  95. $res = $this->Comments->find('bits', $options)->toArray();
  96. $this->assertCount(3, $res);
  97. }
  98. /**
  99. * @return void
  100. */
  101. public function testSaveBasic() {
  102. $data = [
  103. 'comment' => 'test save',
  104. 'statuses' => [],
  105. ];
  106. $entity = $this->Comments->newEntity($data);
  107. $res = $this->Comments->save($entity);
  108. $this->assertTrue((bool)$res);
  109. $this->assertSame(0, $entity->get('status'));
  110. $data = [
  111. 'comment' => 'test save',
  112. 'statuses' => [BitmaskedComment::STATUS_PUBLISHED, BitmaskedComment::STATUS_APPROVED],
  113. ];
  114. $entity = $this->Comments->newEntity($data);
  115. $res = $this->Comments->save($entity);
  116. $this->assertTrue((bool)$res);
  117. $is = $entity->get('status');
  118. $this->assertSame(BitmaskedComment::STATUS_PUBLISHED | BitmaskedComment::STATUS_APPROVED, $is);
  119. // save + find
  120. $entity = $this->Comments->newEntity($data);
  121. $this->assertEmpty($entity->getErrors());
  122. $res = $this->Comments->save($entity);
  123. $this->assertTrue((bool)$res);
  124. $res = $this->Comments->find()->where(['statuses IN' => $data['statuses']])->first();
  125. $this->assertTrue(!empty($res));
  126. $expected = BitmaskedComment::STATUS_APPROVED | BitmaskedComment::STATUS_PUBLISHED; // 6
  127. $this->assertEquals($expected, $res['status']);
  128. $expected = $data['statuses'];
  129. $this->assertEquals($expected, $res['statuses']);
  130. // model.field syntax
  131. $res = $this->Comments->find()->where(['BitmaskedComments.statuses IN' => $data['statuses']])->first();
  132. $this->assertTrue((bool)$res);
  133. // explicit
  134. $activeApprovedAndPublished = BitmaskedComment::STATUS_ACTIVE | BitmaskedComment::STATUS_APPROVED | BitmaskedComment::STATUS_PUBLISHED;
  135. $data = [
  136. 'comment' => 'another post comment',
  137. 'status' => $activeApprovedAndPublished,
  138. ];
  139. $entity = $this->Comments->newEntity($data);
  140. $res = $this->Comments->save($entity);
  141. $this->assertTrue((bool)$res);
  142. $res = $this->Comments->find()->where(['status' => $activeApprovedAndPublished])->first();
  143. $this->assertTrue((bool)$res);
  144. $this->assertEquals($activeApprovedAndPublished, $res['status']);
  145. $expected = [BitmaskedComment::STATUS_ACTIVE, BitmaskedComment::STATUS_PUBLISHED, BitmaskedComment::STATUS_APPROVED];
  146. $this->assertEquals($expected, $res['statuses']);
  147. }
  148. /**
  149. * Assert that you can manually trigger "notEmpty" rule with null instead of 0 for "not null" db fields
  150. *
  151. * @return void
  152. */
  153. public function testSaveWithDefaultValue() {
  154. $data = [
  155. 'comment' => 'test save',
  156. 'statuses' => [],
  157. ];
  158. $entity = $this->Comments->newEntity($data);
  159. $res = $this->Comments->save($entity);
  160. $this->assertTrue((bool)$res);
  161. $this->assertSame(0, $entity->get('status'));
  162. $this->skipIf(true, '//FIXME');
  163. // Now let's set the default value
  164. $this->Comments->removeBehavior('Bitmasked');
  165. $this->Comments->addBehavior('Tools.Bitmasked', ['mappedField' => 'statuses', 'defaultValue' => '']);
  166. $data = [
  167. 'comment' => 'test save',
  168. 'statuses' => [],
  169. ];
  170. $entity = $this->Comments->newEntity($data);
  171. $res = $this->Comments->save($entity);
  172. $this->assertFalse($res);
  173. $this->assertSame('', $entity->get('status'));
  174. }
  175. /**
  176. * Assert that it also works with beforeSave event callback.
  177. *
  178. * @return void
  179. */
  180. public function testSaveOnBeforeSave() {
  181. $this->Comments->removeBehavior('Bitmasked');
  182. $this->Comments->addBehavior('Tools.Bitmasked', ['mappedField' => 'statuses', 'on' => 'beforeSave']);
  183. $data = [
  184. 'comment' => 'test save',
  185. 'statuses' => [BitmaskedComment::STATUS_PUBLISHED, BitmaskedComment::STATUS_APPROVED],
  186. ];
  187. $entity = $this->Comments->newEntity($data);
  188. $this->assertEmpty($entity->getErrors());
  189. $res = $this->Comments->save($entity);
  190. $this->assertTrue((bool)$res);
  191. $this->assertSame(BitmaskedComment::STATUS_PUBLISHED | BitmaskedComment::STATUS_APPROVED, $res['status']);
  192. }
  193. /**
  194. * @return void
  195. */
  196. public function testIs() {
  197. $res = $this->Comments->isBit(BitmaskedComment::STATUS_PUBLISHED);
  198. $expected = ['BitmaskedComments.status' => 2];
  199. $this->assertEquals($expected, $res);
  200. }
  201. /**
  202. * @return void
  203. */
  204. public function testIsNot() {
  205. $res = $this->Comments->isNotBit(BitmaskedComment::STATUS_PUBLISHED);
  206. $expected = ['NOT' => ['BitmaskedComments.status' => 2]];
  207. $this->assertEquals($expected, $res);
  208. }
  209. /**
  210. * @return void
  211. */
  212. public function testContains() {
  213. $config = $this->Comments->getConnection()->config();
  214. $isPostgres = strpos($config['driver'], 'Postgres') !== false;
  215. $res = $this->Comments->containsBit(BitmaskedComment::STATUS_PUBLISHED);
  216. $expected = ['(BitmaskedComments.status & 2 = 2)'];
  217. if ($isPostgres) {
  218. $expected = ['("BitmaskedComments"."status" & 2 = 2)'];
  219. }
  220. $this->assertEquals($expected, $res);
  221. $conditions = $res;
  222. $res = $this->Comments->find('all', ['conditions' => $conditions])->toArray();
  223. $this->assertTrue(!empty($res) && count($res) === 3);
  224. // multiple (AND)
  225. $res = $this->Comments->containsBit([BitmaskedComment::STATUS_PUBLISHED, BitmaskedComment::STATUS_ACTIVE]);
  226. $expected = ['(BitmaskedComments.status & 3 = 3)'];
  227. if ($isPostgres) {
  228. $expected = ['("BitmaskedComments"."status" & 3 = 3)'];
  229. }
  230. $this->assertEquals($expected, $res);
  231. $conditions = $res;
  232. $res = $this->Comments->find('all', ['conditions' => $conditions])->toArray();
  233. $this->assertTrue(!empty($res) && count($res) === 2);
  234. }
  235. /**
  236. * @return void
  237. */
  238. public function testNotContains() {
  239. $config = $this->Comments->getConnection()->config();
  240. $isPostgres = strpos($config['driver'], 'Postgres') !== false;
  241. $res = $this->Comments->containsNotBit(BitmaskedComment::STATUS_PUBLISHED);
  242. $expected = ['(BitmaskedComments.status & 2 != 2)'];
  243. if ($isPostgres) {
  244. $expected = ['("BitmaskedComments"."status" & 2 != 2)'];
  245. }
  246. $this->assertEquals($expected, $res);
  247. $conditions = $res;
  248. $res = $this->Comments->find('all', ['conditions' => $conditions])->toArray();
  249. $this->assertTrue(!empty($res) && count($res) === 4);
  250. // multiple (AND)
  251. $res = $this->Comments->containsNotBit([BitmaskedComment::STATUS_PUBLISHED, BitmaskedComment::STATUS_ACTIVE]);
  252. $expected = ['(BitmaskedComments.status & 3 != 3)'];
  253. if ($isPostgres) {
  254. $expected = ['("BitmaskedComments"."status" & 3 != 3)'];
  255. }
  256. $this->assertEquals($expected, $res);
  257. $conditions = $res;
  258. $res = $this->Comments->find('all', ['conditions' => $conditions])->toArray();
  259. $this->assertTrue(!empty($res) && count($res) === 5);
  260. }
  261. }