View source
<?php
namespace Drupal\Tests\bakery\Unit;
use Drupal\bakery\Cookies\ChocolateChip;
use Drupal\bakery\Cookies\Gingerbread;
use Drupal\bakery\Cookies\Stroopwafel;
use Drupal\bakery\Exception\MissingKeyException;
use Drupal\bakery\Kitchen;
use Drupal\Component\Datetime\Time;
use Drupal\Component\DependencyInjection\Container;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\MemoryStorage;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Http\RequestStack;
use Drupal\Core\Logger\LoggerChannelFactory;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
use Drupal\Core\Render\Markup;
use Drupal\Core\Session\AccountProxy;
use Drupal\Core\Session\UserSession;
use Drupal\Tests\UnitTestCase;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request as GuzzleRequest;
use GuzzleHttp\Psr7\Response as GuzzleResponse;
use Psr\Log\Test\TestLogger;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
class KitchenTest extends UnitTestCase {
protected $request;
protected $mockHandler;
protected $currentUser;
protected $messageBag;
protected $requestHistory;
protected $testLogger;
private $kitchen;
private $time;
private $config;
private $cookieJar;
public function setUp() {
parent::setUp();
$this->request = new RequestStack();
$this->request
->push(new Request());
$this->time = new Time($this->request);
$eventDispatcher = new EventDispatcher();
$this->config = new Config('config.test', new MemoryStorage(), $eventDispatcher, $this
->prophesize(TypedConfigManagerInterface::class)
->reveal());
$this->config
->set('bakery_key', 'test_key');
$this->config
->set('bakery_cookie_extension', '_ext');
$this->config
->set('bakery_domain', '.example.com');
$this->config
->set('bakery_freshness', 3600);
$this->currentUser = new AccountProxy($eventDispatcher);
$this->cookieJar = new ParameterBag();
$cf = $this
->prophesize(ConfigFactoryInterface::class);
$cf
->get('bakery.settings')
->willReturn($this->config);
$this->kitchen = new Kitchen($this->time, $cf
->reveal(), $this->currentUser, $this->cookieJar);
$container = new Container();
$container
->set('bakery.kitchen', $this->kitchen);
\Drupal::setContainer($container);
}
protected function setupTestClient() {
$this->requestHistory = [];
$this->mockHandler = new MockHandler();
$container = \Drupal::getContainer();
$handlerStack = HandlerStack::create($this->mockHandler);
$handlerStack
->push(Middleware::history($this->requestHistory));
$client = new Client([
'handler' => $handlerStack,
]);
$container
->set('http_client', $client);
$this->messageBag = new FlashBag();
$container
->set('messenger', new Messenger($this->messageBag, new KillSwitch()));
$container
->set('string_translation', $this
->getStringTranslationStub());
$this->testLogger = new TestLogger();
$loggerFactory = new LoggerChannelFactory();
$loggerFactory
->addLogger($this->testLogger);
$container
->set('logger.factory', $loggerFactory);
}
public function testBake() {
$cookie_name = $this->kitchen
->cookieName(Kitchen::CHOCOLATE_CHIP);
$cookie = new ChocolateChip('name', 'mail', 'init', FALSE);
$this->kitchen
->bake($cookie);
$this
->assertTrue($this->cookieJar
->has($cookie_name));
$this
->assertEquals($this->kitchen
->bakeData($cookie), $this->cookieJar
->get($cookie_name));
$this
->assertEquals($cookie
->toData() + [
'type' => $cookie_name,
'timestamp' => $this->time
->getRequestTime(),
], $this->kitchen
->taste(Kitchen::CHOCOLATE_CHIP, $this->cookieJar));
}
public function testTaste() {
$this
->assertFalse($this->kitchen
->taste(Kitchen::CHOCOLATE_CHIP, $this->cookieJar), 'Missing cookie');
$cookie = new ChocolateChip('name', 'mail', 'init', FALSE);
$this->kitchen
->bake($cookie);
$this->config
->set('bakery_domain', '');
$this
->assertFalse($this->kitchen
->taste(Kitchen::CHOCOLATE_CHIP, $this->cookieJar), 'Missing cookie domain');
}
public function testEat() {
$cookie_name = $this->kitchen
->cookieName(Kitchen::CHOCOLATE_CHIP);
$cookie = new ChocolateChip('name', 'mail', 'init', FALSE);
$this->kitchen
->bake($cookie);
$this
->assertTrue($this->cookieJar
->has($cookie_name));
$this->kitchen
->eat(Kitchen::CHOCOLATE_CHIP);
$this
->assertTrue($this->cookieJar
->has($cookie_name));
$this
->assertEquals('', $this->cookieJar
->get($cookie_name));
}
public function testCookieName() {
$this->config
->set('bakery_cookie_extension', '');
$this
->assertEquals('CHOCOLATECHIPSSL', $this->kitchen
->cookieName('CHOCOLATECHIP'));
$this->config
->set('bakery_cookie_extension', 'test');
$this
->assertEquals('CHOCOLATECHIPSSLtest', $this->kitchen
->cookieName('CHOCOLATECHIP'));
}
public function testEncrypt() {
$this
->assertEquals('Atolz3jC24C+eJehiE71pW6Rolns8EOP6Z7bHYvKjX8=', base64_encode($this->kitchen
->encrypt(serialize('test message'))));
}
public function testDecrypt() {
$this
->assertEquals('test message', unserialize($this->kitchen
->decrypt(base64_decode('Atolz3jC24C+eJehiE71pW6Rolns8EOP6Z7bHYvKjX8='))));
}
public function testBakeDataMissingKey() {
$this->config
->set('bakery_key', '');
$this
->expectException(MissingKeyException::class);
$this->kitchen
->bakeData(new Stroopwafel(123, 'asdf'));
}
public function testBakeData() {
$encoded = 'Mzc0M2RiNTMwMzJkOTE0MDFhMDMxMmVmNjQ2NmYxZGRlODMwZmM1ZjQ3ZDhkNzY5YjAwYTI1YWI3YjFlNGI1ZDYKk5BY7/qPHxsfaqbNRmpYnv5Zt5PdEKDTAaoTps/fYUAfHLHpidALZ3OxG2Nm6tFDXNmMR/SwnZMjICrc0UJgbcqe+VK4sCkHY41UDYhzHKRBHqg3S1hwVQ2727jLoEKHe5bKRpDnXA3DbIAp0p+y+qT7MvxMKPThOWEiZlpc8U3QhthD17aIXyEWgR487C5o4obdDr/w8vYTM+qyQhNMynDGdBhBTIh1mN4gd3vr';
$cookie = new Gingerbread('tester', 0, 'child.example.com', 123);
$timestamp = 1634021402;
$this->request
->getCurrentRequest()->server
->set('REQUEST_TIME', $timestamp);
$this
->assertEquals($encoded, $this->kitchen
->bakeData($cookie));
$data = $this->kitchen
->tasteData($encoded, Gingerbread::getName());
$this
->assertEqualsCanonicalizing([
'name' => $cookie
->getAccountName(),
'or_email' => $cookie
->getOrEmail(),
'slave' => $cookie
->getChild(),
'uid' => $cookie
->getChildUid(),
'type' => Gingerbread::getName(),
'timestamp' => $timestamp,
], $data);
$this
->assertFalse($this->kitchen
->tasteData('a' . $encoded, Gingerbread::getName()));
$this
->assertFalse($this->kitchen
->tasteData($encoded, Stroopwafel::getName()));
$this->request
->getCurrentRequest()->server
->set('REQUEST_TIME', $timestamp + $this->config
->get('bakery_freshness') + 50);
$this
->assertFalse($this->kitchen
->tasteData($encoded, Gingerbread::getName()));
}
public function testTasteDataMissingKey() {
$this->config
->set('bakery_key', '');
$this
->expectException(MissingKeyException::class);
$this->kitchen
->tasteData('asdf', Stroopwafel::getName());
}
public function testShipMaster() {
$this
->setupTestClient();
$this->config
->set('bakery_is_master', 1);
$this->config
->set('bakery_slaves', [
'https://child0.example.com/',
'https://child1.example.com/',
'https://child2.example.com/',
]);
$this->currentUser
->setAccount(new UserSession([
'uid' => 1,
]));
$cookie = new Stroopwafel(123, 'asdf');
$this->mockHandler
->append(new GuzzleResponse(200, [], 'Response 1'));
$this->mockHandler
->append(new RequestException(401, new GuzzleRequest('POST', 'https://example.com'), new GuzzleResponse(401, [], 'Conflict')));
$this->mockHandler
->append(new BadResponseException(401, new GuzzleRequest('POST', 'https://example.com'), new GuzzleResponse(401, [], 'Conflict')));
$this
->assertTrue($this->kitchen
->ship($cookie), 'Ship succeeded.');
$this
->assertCount(3, $this->requestHistory);
foreach ($this->requestHistory as $i => $history) {
$request = $history['request'];
$this
->assertEquals('https://child' . $i . '.example.com/' . $cookie
->getPath(), (string) $request
->getUri());
$this
->assertEquals(http_build_query([
'stroopwafel' => $this->kitchen
->bakeData($cookie),
]), (string) $request
->getBody());
}
$this
->assertEquals([
'status' => [
'Response 1',
],
'error' => [
Markup::create('Error occurred talking to the site at <em class="placeholder">https://child1.example.com/</em>'),
Markup::create('Error occurred talking to the site at <em class="placeholder">https://child2.example.com/</em>'),
],
], $this->messageBag
->all());
$this
->assertTrue($this->testLogger
->hasRecordThatContains('Failed to fetch file due to error', RfcLogLevel::ERROR), 'Request Error');
$this
->assertTrue($this->testLogger
->hasRecordThatContains('Failed to fetch file due to HTTP error', RfcLogLevel::ERROR), 'HTTP Error');
}
public function testShipChild() {
$this
->setupTestClient();
$this->config
->set('bakery_is_master', 0);
$this->config
->set('bakery_master', 'https://www.example.com/');
$cookie = new Stroopwafel(123, 'asdf');
$response = new GuzzleResponse();
$this->mockHandler
->append($response);
$this
->assertEquals($response, $this->kitchen
->ship($cookie));
$this
->assertCount(1, $this->requestHistory);
$request = $this->requestHistory[0]['request'];
$this
->assertEquals('https://www.example.com/' . $cookie
->getPath(), (string) $request
->getUri());
$this
->assertEquals(http_build_query([
'stroopwafel' => $this->kitchen
->bakeData($cookie),
]), (string) $request
->getBody());
}
}