View source
<?php
namespace Drupal\Tests\graphql\Kernel\Framework;
use Drupal\Core\Cache\Context\CacheContextsManager;
use Drupal\Core\Cache\Context\ContextCacheKeys;
use Drupal\graphql\GraphQL\Cache\CacheableValue;
use Drupal\graphql\GraphQL\QueryProvider\QueryProviderInterface;
use Drupal\Tests\graphql\Kernel\GraphQLTestBase;
use Prophecy\Argument;
class ResultCacheTest extends GraphQLTestBase {
public function testCacheableResult() {
$this
->mockField('root', [
'id' => 'root',
'name' => 'root',
'type' => 'String',
], NULL, function ($field) {
$field
->expects(static::once())
->method('resolveValues')
->willReturnCallback(function () {
(yield 'test');
});
});
$this
->query('{ root }');
$this
->query('{ root }');
}
public function testUncacheableResult() {
$this
->mockField('root', [
'id' => 'root',
'name' => 'root',
'type' => 'String',
], NULL, function ($field) {
$callback = function () {
(yield (new CacheableValue('test'))
->mergeCacheMaxAge(0));
};
$field
->expects(static::exactly(2))
->method('resolveValues')
->will($this
->toBoundPromise($callback, $field));
});
$this
->query('{ root }');
$this
->query('{ root }');
}
public function testUncacheableResultAnnotation() {
$this
->mockField('root', [
'id' => 'root',
'name' => 'root',
'type' => 'String',
'response_cache_max_age' => 0,
], NULL, function ($field) {
$field
->expects(static::exactly(2))
->method('resolveValues')
->willReturnCallback(function () {
(yield 'test');
});
});
$this
->query('{ root }');
$this
->query('{ root }');
}
public function testVariables() {
$this
->mockField('root', [
'id' => 'root',
'name' => 'root',
'type' => 'String',
], NULL, function ($field) {
$field
->expects(static::exactly(2))
->method('resolveValues')
->willReturnCallback(function () {
(yield 'test');
});
});
$this
->query('{ root }', [
'value' => 'a',
]);
$this
->query('{ root }', [
'value' => 'b',
]);
$this
->query('{ root }', [
'value' => 'a',
]);
}
public function testContext() {
$contextManager = $this
->prophesize(CacheContextsManager::class);
$this->container
->set('cache_contexts_manager', $contextManager
->reveal());
$contextManager
->assertValidTokens(Argument::any())
->willReturn(TRUE);
$hasContext = Argument::containing('context');
$hasNotContext = Argument::that(function ($arg) {
return !in_array('context', $arg);
});
$contextManager
->convertTokensToKeys($hasNotContext)
->willReturn(new ContextCacheKeys([]));
$contextKeys = $contextManager
->convertTokensToKeys($hasContext);
$this
->mockField('root', [
'id' => 'root',
'name' => 'root',
'type' => 'String',
'response_cache_contexts' => [
'context',
],
], NULL, function ($field) {
$field
->expects(static::exactly(2))
->method('resolveValues')
->willReturnCallback(function () {
(yield 'test');
});
});
$contextKeys
->willReturn(new ContextCacheKeys([
'a',
]));
$this
->query('{ root }');
$contextKeys
->willReturn(new ContextCacheKeys([
'b',
]));
$this
->query('{ root }');
$contextKeys
->willReturn(new ContextCacheKeys([
'a',
]));
$this
->query('{ root }');
}
public function testTags() {
$this
->mockField('root', [
'id' => 'root',
'name' => 'root',
'type' => 'String',
'response_cache_tags' => [
'a',
'b',
],
], NULL, function ($field) {
$field
->expects(static::exactly(2))
->method('resolveValues')
->willReturnCallback(function () {
(yield 'test');
});
});
$this
->query('{ root }');
$this->container
->get('cache_tags.invalidator')
->invalidateTags([
'a',
]);
$this
->query('{ root }');
$this->container
->get('cache_tags.invalidator')
->invalidateTags([
'c',
]);
$this
->query('{ root }');
}
public function testPersistedQuery() {
$queryProvider = $this
->prophesize(QueryProviderInterface::class);
$this->container
->set('graphql.query_provider', $queryProvider
->reveal());
$queryProvider
->getQuery('query:a', Argument::any())
->willReturn('{ a }');
$queryProvider
->getQuery('query:b', Argument::any())
->willReturn('{ b }');
$this
->mockField('a', [
'id' => 'a',
'name' => 'a',
'type' => 'String',
], NULL, function ($field) {
$field
->expects(static::exactly(1))
->method('resolveValues')
->willReturnCallback(function () {
(yield 'a');
});
});
$this
->mockField('b', [
'id' => 'b',
'name' => 'b',
'type' => 'String',
], NULL, function ($field) {
$field
->expects(static::exactly(2))
->method('resolveValues')
->willReturnCallback(function () {
(yield 'b');
});
});
$this
->persistedQuery('query:a');
$this
->persistedQuery('query:b');
$this
->persistedQuery('query:a');
$this
->persistedQuery('query:b', [
'value' => 'test',
]);
}
public function testLeakingCacheMetadata() {
$renderer = $this->container
->get('renderer');
$this
->mockField('leakA', [
'id' => 'leakA',
'name' => 'leakA',
'type' => 'String',
], function () use ($renderer) {
$el = [
'#plain_text' => 'Leak A',
'#cache' => [
'tags' => [
'a',
],
],
];
(yield $renderer
->render($el)
->__toString());
});
$this
->mockField('leakB', [
'id' => 'leakB',
'name' => 'leakB',
'type' => 'String',
], function () use ($renderer) {
$el = [
'#plain_text' => 'Leak B',
'#cache' => [
'tags' => [
'b',
],
],
];
$value = $renderer
->render($el)
->__toString();
return function () use ($value) {
(yield $value);
};
});
$query = <<<GQL
query {
leakA
leakB
}
GQL;
$metadata = $this
->defaultCacheMetaData()
->addCacheTags([
'a',
'b',
]);
$this
->assertResults($query, [], [
'leakA' => 'Leak A',
'leakB' => 'Leak B',
], $metadata);
$result = $this
->query($query);
$this
->assertEquals(200, $result
->getStatusCode());
}
}