BroadcasterTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <?php
  2. namespace Illuminate\Tests\Broadcasting;
  3. use Exception;
  4. use Illuminate\Broadcasting\Broadcasters\Broadcaster;
  5. use Illuminate\Container\Container;
  6. use Illuminate\Contracts\Routing\BindingRegistrar;
  7. use Illuminate\Database\Eloquent\Model;
  8. use Illuminate\Http\Request;
  9. use Mockery as m;
  10. use PHPUnit\Framework\TestCase;
  11. use Symfony\Component\HttpKernel\Exception\HttpException;
  12. class BroadcasterTest extends TestCase
  13. {
  14. /**
  15. * @var \Illuminate\Tests\Broadcasting\FakeBroadcaster
  16. */
  17. public $broadcaster;
  18. protected function setUp(): void
  19. {
  20. parent::setUp();
  21. $this->broadcaster = new FakeBroadcaster;
  22. }
  23. protected function tearDown(): void
  24. {
  25. m::close();
  26. Container::setInstance(null);
  27. }
  28. public function testExtractingParametersWhileCheckingForUserAccess()
  29. {
  30. $callback = function ($user, BroadcasterTestEloquentModelStub $model, $nonModel) {
  31. //
  32. };
  33. $parameters = $this->broadcaster->extractAuthParameters('asd.{model}.{nonModel}', 'asd.1.something', $callback);
  34. $this->assertEquals(['model.1.instance', 'something'], $parameters);
  35. $callback = function ($user, BroadcasterTestEloquentModelStub $model, BroadcasterTestEloquentModelStub $model2, $something) {
  36. //
  37. };
  38. $parameters = $this->broadcaster->extractAuthParameters('asd.{model}.{model2}.{nonModel}', 'asd.1.uid.something', $callback);
  39. $this->assertEquals(['model.1.instance', 'model.uid.instance', 'something'], $parameters);
  40. $callback = function ($user) {
  41. //
  42. };
  43. $parameters = $this->broadcaster->extractAuthParameters('asd', 'asd', $callback);
  44. $this->assertEquals([], $parameters);
  45. $callback = function ($user, $something) {
  46. //
  47. };
  48. $parameters = $this->broadcaster->extractAuthParameters('asd', 'asd', $callback);
  49. $this->assertEquals([], $parameters);
  50. /*
  51. * Test Explicit Binding...
  52. */
  53. $container = new Container;
  54. Container::setInstance($container);
  55. $binder = m::mock(BindingRegistrar::class);
  56. $binder->shouldReceive('getBindingCallback')->times(2)->with('model')->andReturn(function () {
  57. return 'bound';
  58. });
  59. $container->instance(BindingRegistrar::class, $binder);
  60. $callback = function ($user, $model) {
  61. //
  62. };
  63. $parameters = $this->broadcaster->extractAuthParameters('something.{model}', 'something.1', $callback);
  64. $this->assertEquals(['bound'], $parameters);
  65. Container::setInstance(new Container);
  66. }
  67. public function testCanUseChannelClasses()
  68. {
  69. $parameters = $this->broadcaster->extractAuthParameters('asd.{model}.{nonModel}', 'asd.1.something', DummyBroadcastingChannel::class);
  70. $this->assertEquals(['model.1.instance', 'something'], $parameters);
  71. }
  72. public function testUnknownChannelAuthHandlerTypeThrowsException()
  73. {
  74. $this->expectException(Exception::class);
  75. $this->broadcaster->extractAuthParameters('asd.{model}.{nonModel}', 'asd.1.something', 123);
  76. }
  77. public function testCanRegisterChannelsAsClasses()
  78. {
  79. $this->broadcaster->channel('something', function () {
  80. //
  81. });
  82. $this->broadcaster->channel('somethingelse', DummyBroadcastingChannel::class);
  83. }
  84. public function testNotFoundThrowsHttpException()
  85. {
  86. $this->expectException(HttpException::class);
  87. $callback = function ($user, BroadcasterTestEloquentModelNotFoundStub $model) {
  88. //
  89. };
  90. $this->broadcaster->extractAuthParameters('asd.{model}', 'asd.1', $callback);
  91. }
  92. public function testCanRegisterChannelsWithoutOptions()
  93. {
  94. $this->broadcaster->channel('somechannel', function () {
  95. //
  96. });
  97. }
  98. public function testCanRegisterChannelsWithOptions()
  99. {
  100. $options = ['a' => ['b', 'c']];
  101. $this->broadcaster->channel('somechannel', function () {
  102. //
  103. }, $options);
  104. }
  105. public function testCanRetrieveChannelsOptions()
  106. {
  107. $options = ['a' => ['b', 'c']];
  108. $this->broadcaster->channel('somechannel', function () {
  109. //
  110. }, $options);
  111. $this->assertEquals(
  112. $options,
  113. $this->broadcaster->retrieveChannelOptions('somechannel')
  114. );
  115. }
  116. public function testCanRetrieveChannelsOptionsUsingAChannelNameContainingArgs()
  117. {
  118. $options = ['a' => ['b', 'c']];
  119. $this->broadcaster->channel('somechannel.{id}.test.{text}', function () {
  120. //
  121. }, $options);
  122. $this->assertEquals(
  123. $options,
  124. $this->broadcaster->retrieveChannelOptions('somechannel.23.test.mytext')
  125. );
  126. }
  127. public function testCanRetrieveChannelsOptionsWhenMultipleChannelsAreRegistered()
  128. {
  129. $options = ['a' => ['b', 'c']];
  130. $this->broadcaster->channel('somechannel', function () {
  131. //
  132. });
  133. $this->broadcaster->channel('someotherchannel', function () {
  134. //
  135. }, $options);
  136. $this->assertEquals(
  137. $options,
  138. $this->broadcaster->retrieveChannelOptions('someotherchannel')
  139. );
  140. }
  141. public function testDontRetrieveChannelsOptionsWhenChannelDoesntExists()
  142. {
  143. $options = ['a' => ['b', 'c']];
  144. $this->broadcaster->channel('somechannel', function () {
  145. //
  146. }, $options);
  147. $this->assertEquals(
  148. [],
  149. $this->broadcaster->retrieveChannelOptions('someotherchannel')
  150. );
  151. }
  152. public function testRetrieveUserWithoutGuard()
  153. {
  154. $this->broadcaster->channel('somechannel', function () {
  155. //
  156. });
  157. $request = m::mock(Request::class);
  158. $request->shouldReceive('user')
  159. ->once()
  160. ->withNoArgs()
  161. ->andReturn(new DummyUser);
  162. $this->assertInstanceOf(
  163. DummyUser::class,
  164. $this->broadcaster->retrieveUser($request, 'somechannel')
  165. );
  166. }
  167. public function testRetrieveUserWithOneGuardUsingAStringForSpecifyingGuard()
  168. {
  169. $this->broadcaster->channel('somechannel', function () {
  170. //
  171. }, ['guards' => 'myguard']);
  172. $request = m::mock(Request::class);
  173. $request->shouldReceive('user')
  174. ->once()
  175. ->with('myguard')
  176. ->andReturn(new DummyUser);
  177. $this->assertInstanceOf(
  178. DummyUser::class,
  179. $this->broadcaster->retrieveUser($request, 'somechannel')
  180. );
  181. }
  182. public function testRetrieveUserWithMultipleGuardsAndRespectGuardsOrder()
  183. {
  184. $this->broadcaster->channel('somechannel', function () {
  185. //
  186. }, ['guards' => ['myguard1', 'myguard2']]);
  187. $this->broadcaster->channel('someotherchannel', function () {
  188. //
  189. }, ['guards' => ['myguard2', 'myguard1']]);
  190. $request = m::mock(Request::class);
  191. $request->shouldReceive('user')
  192. ->once()
  193. ->with('myguard1')
  194. ->andReturn(null);
  195. $request->shouldReceive('user')
  196. ->twice()
  197. ->with('myguard2')
  198. ->andReturn(new DummyUser)
  199. ->ordered('user');
  200. $this->assertInstanceOf(
  201. DummyUser::class,
  202. $this->broadcaster->retrieveUser($request, 'somechannel')
  203. );
  204. $this->assertInstanceOf(
  205. DummyUser::class,
  206. $this->broadcaster->retrieveUser($request, 'someotherchannel')
  207. );
  208. }
  209. public function testRetrieveUserDontUseDefaultGuardWhenOneGuardSpecified()
  210. {
  211. $this->broadcaster->channel('somechannel', function () {
  212. //
  213. }, ['guards' => 'myguard']);
  214. $request = m::mock(Request::class);
  215. $request->shouldReceive('user')
  216. ->once()
  217. ->with('myguard')
  218. ->andReturn(null);
  219. $request->shouldNotReceive('user')
  220. ->withNoArgs();
  221. $this->broadcaster->retrieveUser($request, 'somechannel');
  222. }
  223. public function testRetrieveUserDontUseDefaultGuardWhenMultipleGuardsSpecified()
  224. {
  225. $this->broadcaster->channel('somechannel', function () {
  226. //
  227. }, ['guards' => ['myguard1', 'myguard2']]);
  228. $request = m::mock(Request::class);
  229. $request->shouldReceive('user')
  230. ->once()
  231. ->with('myguard1')
  232. ->andReturn(null);
  233. $request->shouldReceive('user')
  234. ->once()
  235. ->with('myguard2')
  236. ->andReturn(null);
  237. $request->shouldNotReceive('user')
  238. ->withNoArgs();
  239. $this->broadcaster->retrieveUser($request, 'somechannel');
  240. }
  241. /**
  242. * @dataProvider channelNameMatchPatternProvider
  243. */
  244. public function testChannelNameMatchPattern($channel, $pattern, $shouldMatch)
  245. {
  246. $this->assertEquals($shouldMatch, $this->broadcaster->channelNameMatchesPattern($channel, $pattern));
  247. }
  248. public function channelNameMatchPatternProvider()
  249. {
  250. return [
  251. ['something', 'something', true],
  252. ['something.23', 'something.{id}', true],
  253. ['something.23.test', 'something.{id}.test', true],
  254. ['something.23.test.42', 'something.{id}.test.{id2}', true],
  255. ['something-23:test-42', 'something-{id}:test-{id2}', true],
  256. ['something..test.42', 'something.{id}.test.{id2}', true],
  257. ['23:string:test', '{id}:string:{text}', true],
  258. ['something.23', 'something', false],
  259. ['something.23.test.42', 'something.test.{id}', false],
  260. ['something-23-test-42', 'something-{id}-test', false],
  261. ['23:test', '{id}:test:abcd', false],
  262. ];
  263. }
  264. }
  265. class FakeBroadcaster extends Broadcaster
  266. {
  267. public function auth($request)
  268. {
  269. //
  270. }
  271. public function validAuthenticationResponse($request, $result)
  272. {
  273. //
  274. }
  275. public function broadcast(array $channels, $event, array $payload = [])
  276. {
  277. //
  278. }
  279. public function extractAuthParameters($pattern, $channel, $callback)
  280. {
  281. return parent::extractAuthParameters($pattern, $channel, $callback);
  282. }
  283. public function retrieveChannelOptions($channel)
  284. {
  285. return parent::retrieveChannelOptions($channel);
  286. }
  287. public function retrieveUser($request, $channel)
  288. {
  289. return parent::retrieveUser($request, $channel);
  290. }
  291. public function channelNameMatchesPattern($channel, $pattern)
  292. {
  293. return parent::channelNameMatchesPattern($channel, $pattern);
  294. }
  295. }
  296. class BroadcasterTestEloquentModelStub extends Model
  297. {
  298. public function getRouteKeyName()
  299. {
  300. return 'id';
  301. }
  302. public function where($key, $value)
  303. {
  304. $this->value = $value;
  305. return $this;
  306. }
  307. public function first()
  308. {
  309. return "model.{$this->value}.instance";
  310. }
  311. }
  312. class BroadcasterTestEloquentModelNotFoundStub extends Model
  313. {
  314. public function getRouteKeyName()
  315. {
  316. return 'id';
  317. }
  318. public function where($key, $value)
  319. {
  320. $this->value = $value;
  321. return $this;
  322. }
  323. public function first()
  324. {
  325. //
  326. }
  327. }
  328. class DummyBroadcastingChannel
  329. {
  330. public function join($user, BroadcasterTestEloquentModelStub $model, $nonModel)
  331. {
  332. //
  333. }
  334. }
  335. class DummyUser
  336. {
  337. //
  338. }