Skip to content

Commit a8652e3

Browse files
authored
Merge pull request #62 from Flowpack/tests/fix-behat-tests
TESTS: fix behat tests
2 parents e0d267f + c9cca4d commit a8652e3

8 files changed

Lines changed: 145 additions & 43 deletions

File tree

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -727,25 +727,37 @@ The orchestrator's exit codes: `1` release already completed, `2` empty enumerat
727727

728728
### Testing the Rendering
729729

730-
For executing behavioral tests, install the `neos/behat` package and run `./flow behat:setup`. Then:
730+
The behavioral tests need the `neos/behat` package (`composer require --dev neos/behat`), which brings Behat itself
731+
along. Behat is used from the main composer installation:
731732

732733
```bash
733734
cd Packages/Application/Flowpack.DecoupledContentStore/Tests/Behavior
734-
../../../../bin/behat -c behat.yml.dist
735+
../../../../../bin/behat -c behat.yml.dist
735736
```
736737

738+
(five levels up is the installation root - adjust the path if the package sits somewhere else, for example as a symlink
739+
into a `DistributionPackages` checkout)
740+
741+
The tests bootstrap the `Testing/Behat` context, so the database and the Redis instances they work on are the ones
742+
configured in `Configuration/Testing/Behat/`.
743+
744+
**Every feature file is tagged `@resetRedis`, and that hook calls `FLUSHALL`** on the primary content store - not just
745+
the configured database, but every database on that Redis server. Point the Behat context at a Redis instance whose
746+
contents you are willing to lose; if it is the same server a development content store uses, running the tests wipes it,
747+
including caches other applications keep there.
748+
737749
Behat also supports running single tests or single files - they need to be specified after the config file, e.g.
738750

739751
```bash
740752
741753
# run all scenarios in a given folder
742-
../../../../bin/behat -c behat.yml.dist Features/ContentStore/
754+
../../../../../bin/behat -c behat.yml.dist Features/ContentStore/
743755
744756
# run all scenarios in the single feature file
745-
../../../../bin/behat -c behat.yml.dist Features/ContentStore/Basics.feature
757+
../../../../../bin/behat -c behat.yml.dist Features/ContentStore/Basics.feature
746758
747759
# run the scenario starting at line 66
748-
../../../../bin/behat -c behat.yml.dist Features/ContentStore/Basics.feature:66
760+
../../../../../bin/behat -c behat.yml.dist Features/ContentStore/Basics.feature:66
749761
```
750762

751763
In case of exceptions, it might be helpful to run the tests with `--stop-on-failure`, which stops the test cases at the first

Tests/Behavior/Features/Bootstrap/FeatureContext.php

Lines changed: 96 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
use Behat\Behat\Context\Context;
46
use Behat\Gherkin\Node\PyStringNode;
7+
use Composer\InstalledVersions;
58
use Flowpack\DecoupledContentStore\Command\ContentReleaseValidationCommandController;
69
use Flowpack\DecoupledContentStore\ContentReleaseManager;
710
use Flowpack\DecoupledContentStore\Core\ConcurrentBuildLockService;
811
use Flowpack\DecoupledContentStore\Core\Domain\ValueObject\ContentReleaseIdentifier;
912
use Flowpack\DecoupledContentStore\Core\Domain\ValueObject\PrunnerJobId;
1013
use Flowpack\DecoupledContentStore\Core\Domain\ValueObject\RedisInstanceIdentifier;
11-
use Flowpack\DecoupledContentStore\Exception as DecoupledContentStoreException;
12-
use Flowpack\DecoupledContentStore\QuickPublish\Dto\NodeIdentifiers;
13-
use Flowpack\DecoupledContentStore\QuickPublish\Infrastructure\RedisReleaseCopyService;
14-
use Flowpack\DecoupledContentStore\QuickPublish\QuickPublishNodeEnumerator;
15-
use Flowpack\DecoupledContentStore\Core\RedisKeyService;
1614
use Flowpack\DecoupledContentStore\Core\Infrastructure\ContentReleaseLogger;
1715
use Flowpack\DecoupledContentStore\Core\Infrastructure\RedisClientManager;
16+
use Flowpack\DecoupledContentStore\Core\RedisKeyService;
17+
use Flowpack\DecoupledContentStore\Exception as DecoupledContentStoreException;
1818
use Flowpack\DecoupledContentStore\IncrementalContentReleaseHandler;
1919
use Flowpack\DecoupledContentStore\NodeEnumeration\Domain\Repository\RedisEnumerationRepository;
2020
use Flowpack\DecoupledContentStore\NodeEnumeration\Domain\Service\NodeContextCombinator;
@@ -32,15 +32,26 @@
3232
use Flowpack\DecoupledContentStore\NodeRendering\ProcessEvents\RenderingQueueFilledEvent;
3333
use Flowpack\DecoupledContentStore\NodeRendering\Render\CustomFusionView;
3434
use Flowpack\DecoupledContentStore\PrepareContentRelease\Infrastructure\RedisContentReleaseService;
35+
use Flowpack\DecoupledContentStore\QuickPublish\Dto\NodeIdentifiers;
36+
use Flowpack\DecoupledContentStore\QuickPublish\Infrastructure\RedisReleaseCopyService;
37+
use Flowpack\DecoupledContentStore\QuickPublish\QuickPublishNodeEnumerator;
3538
use Flowpack\DecoupledContentStore\Tests\Behavior\Fixtures\StubPrunnerApiService;
36-
use Neos\Behat\Tests\Behat\FlowContextTrait;
39+
use Neos\Behat\FlowBootstrapTrait;
40+
use Neos\Behat\FlowEntitiesTrait;
3741
use Neos\ContentRepository\Domain\Model\NodeInterface;
3842
use Neos\ContentRepository\Domain\Repository\WorkspaceRepository;
3943
use Neos\ContentRepository\Domain\Service\NodeTypeManager;
4044
use Neos\ContentRepository\Tests\Behavior\Features\Bootstrap\NodeOperationsTrait;
45+
use Neos\Flow\Mvc\ActionRequest;
4146
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
4247
use Neos\Flow\Persistence\PersistenceManagerInterface;
43-
use Neos\Flow\Tests\Behavior\Features\Bootstrap\SecurityOperationsTrait;
48+
use Neos\Flow\Security\Account;
49+
use Neos\Flow\Security\Authentication\AuthenticationProviderManager;
50+
use Neos\Flow\Security\Authentication\Provider\TestingProvider;
51+
use Neos\Flow\Security\Authentication\TokenAndProviderFactoryInterface;
52+
use Neos\Flow\Security\Authentication\TokenInterface;
53+
use Neos\Flow\Security\Context as SecurityContext;
54+
use Neos\Flow\Security\Policy\PolicyService;
4455
use Neos\Neos\Domain\Model\Domain;
4556
use Neos\Neos\Domain\Model\Site;
4657
use Neos\Neos\Domain\Repository\DomainRepository;
@@ -49,44 +60,50 @@
4960
use Neos\Utility\Arrays;
5061
use Neos\Utility\ObjectAccess;
5162
use PHPUnit\Framework\Assert;
63+
use Psr\Http\Message\ServerRequestFactoryInterface;
5264
use Symfony\Component\Console\Output\BufferedOutput;
5365
use Symfony\Component\Yaml\Yaml;
5466

55-
require_once __DIR__ . '/../../../../../../Packages/Application/Neos.Behat/Tests/Behat/FlowContextTrait.php';
56-
require_once
57-
__DIR__
58-
. '/../../../../../../Packages/Application/Neos.ContentRepository/Tests/Behavior/Features/Bootstrap/NodeOperationsTrait.php';
59-
require_once
60-
__DIR__
61-
. '/../../../../../../Packages/Framework/Neos.Flow/Tests/Behavior/Features/Bootstrap/SecurityOperationsTrait.php';
67+
// The content repository keeps its step definitions in a directory no autoloader knows about, so the file has to be
68+
// included by hand. Composer knows where the package was installed, which holds wherever this package itself sits -
69+
// inside a Neos installation as well as in a checkout of its own.
70+
require_once InstalledVersions::getInstallPath('neos/content-repository')
71+
. '/Tests/Behavior/Features/Bootstrap/NodeOperationsTrait.php';
6272

6373
/**
6474
* Features context
6575
*/
6676
class FeatureContext implements Context
6777
{
68-
use FlowContextTrait;
69-
use SecurityOperationsTrait;
78+
use FlowBootstrapTrait;
79+
use FlowEntitiesTrait;
7080
use NodeOperationsTrait;
7181

82+
/**
83+
* The step definitions of the content repository run their steps in a sub process where this is TRUE, which none
84+
* of the features here ask for.
85+
*/
7286
protected $isolated = false;
7387

7488
/**
7589
* @var ObjectManagerInterface
7690
*/
7791
protected $objectManager;
7892

93+
private SecurityContext $securityContext;
94+
private ActionRequest $securityActionRequest;
95+
private AuthenticationProviderManager $authenticationManager;
96+
private TestingProvider $testingProvider;
97+
private PolicyService $policyService;
98+
7999
private ?InterruptibleProcessRuntime $renderOrchestratorProcess;
80100
private ?InterruptibleProcessRuntimeEventInterface $renderOrchestratorProcessLastEvent;
81101
private StubPrunnerApiService $stubPrunnerApiService;
82102
private BufferedOutput $renderOrchestratorProcessBufferedOutput;
83103

84104
public function __construct()
85105
{
86-
if (self::$bootstrap === null) {
87-
self::$bootstrap = $this->initializeFlow();
88-
}
89-
$this->objectManager = self::$bootstrap->getObjectManager();
106+
$this->objectManager = self::bootstrapFlow()->getObjectManager();
90107
$this->setupSecurity();
91108

92109
// for testing, we use Private/EndToEndTestFusion as fusion folder to load.
@@ -116,6 +133,63 @@ public function getObjectManager(): ObjectManagerInterface
116133
return $this->objectManager;
117134
}
118135

136+
/**
137+
* Persist any changes - part of what the content repository's step definitions expect from their context.
138+
*/
139+
public function persistAll(): void
140+
{
141+
$persistenceManager = $this->getObject(PersistenceManagerInterface::class);
142+
$persistenceManager->persistAll();
143+
$persistenceManager->clearState();
144+
145+
// roles are read through the policy service, which holds on to them across a database reset
146+
$this->policyService->reset();
147+
}
148+
149+
/**
150+
* The authentication a scenario gets through "I am authenticated with role" needs a security context which has
151+
* a request, and a TestingProvider to hand the account to.
152+
*/
153+
private function setupSecurity(): void
154+
{
155+
$this->policyService = $this->getObject(PolicyService::class);
156+
$this->authenticationManager = $this->getObject(AuthenticationProviderManager::class);
157+
158+
// asking for the providers is what builds them, and with them the singleton TestingProvider
159+
$providers = $this->getObject(TokenAndProviderFactoryInterface::class)->getProviders();
160+
$this->testingProvider = $providers['TestingProvider'];
161+
162+
$httpRequest = $this->getObject(ServerRequestFactoryInterface::class)
163+
->createServerRequest('GET', 'http://localhost/');
164+
$this->securityActionRequest = ActionRequest::fromHttpRequest($httpRequest);
165+
166+
$this->securityContext = $this->getObject(SecurityContext::class);
167+
$this->securityContext->clearContext();
168+
$this->securityContext->setRequest($this->securityActionRequest);
169+
}
170+
171+
/**
172+
* @Given /^I am authenticated with role "([^"]*)"$/
173+
*/
174+
public function iAmAuthenticatedWithRole(string $roleIdentifier): void
175+
{
176+
$roles = [];
177+
foreach (Arrays::trimExplode(',', $roleIdentifier) as $roleName) {
178+
$roles[] = $this->policyService->getRole($roleName);
179+
}
180+
181+
$account = new Account();
182+
$account->setAccountIdentifier('TestAccount');
183+
$account->setRoles($roles);
184+
185+
$this->testingProvider->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL);
186+
$this->testingProvider->setAccount($account);
187+
188+
$this->securityContext->clearContext();
189+
$this->securityContext->setRequest($this->securityActionRequest);
190+
$this->authenticationManager->authenticate();
191+
}
192+
119193
/**
120194
* @BeforeScenario @resetRedis
121195
*/
@@ -331,7 +405,7 @@ public function theEnumerationContainsNode($contentReleaseIdentifier, $expectedC
331405
} )()
332406
);
333407

334-
Assert::assertCount($expectedCount, $enumerationAsArray);
408+
Assert::assertCount((int)$expectedCount, $enumerationAsArray);
335409
}
336410

337411
/**
@@ -488,7 +562,7 @@ public function duringRenderingOfContentReleaseSomeErrorsOccured($contentRelease
488562
$contentReleaseIdentifier = ContentReleaseIdentifier::fromString($contentReleaseIdentifier);
489563
$redisRenderingErrorManager = $this->getObjectManager()->get(RedisRenderingErrorManager::class);
490564
$renderingErrors = $redisRenderingErrorManager->getRenderingErrors($contentReleaseIdentifier);
491-
Assert::assertCount($expectedNumberOfErrors, $renderingErrors);
565+
Assert::assertCount((int)$expectedNumberOfErrors, $renderingErrors);
492566
}
493567

494568
private const DEFAULT_NODETYPES_CONFIG = <<<EOF

Tests/Behavior/Features/ContentStore/Basics.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@fixtures
2+
@flowEntities
23
@resetRedis
34
Feature: Basic Rendering
45

Tests/Behavior/Features/ContentStore/ErrorsWhileRendering.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@fixtures
2+
@flowEntities
23
@resetRedis
34
Feature: Errors while rendering
45

Tests/Behavior/Features/ContentStore/IncrementalRendering.feature

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@fixtures
2+
@flowEntities
23
@resetRedis
34
Feature: Incremental Rendering
45

@@ -77,11 +78,15 @@ Feature: Incremental Rendering
7778
BEFOREUnterseite2AFTER
7879
"""
7980
Then I expect the content release "6" to not contain anything for URI "http://test.de/de/nested"
80-
# TODO: right now, a rerendering of the homepage is still needed - would be nice to get rid of this sometime in the future.
81-
Then I expect the content release "6" to not contain anything for URI "http://test.de/de"
82-
# /sites/test/sub
83-
# /sites/test right now (TODO debatable whether this makes sense)
84-
And the rendering queue for content release "6" contains 2 documents
81+
# The homepage keeps its rendering: the cache entry of a content collection is tagged with the collection node
82+
# (Neos.Neos:ContentCollection sets @context.node to the nearest content collection), so a change inside a
83+
# document further down the tree flushes neither the homepage nor its collection.
84+
Then I expect the content release "6" to contain the following content for URI "http://test.de/de" at CSS selector "body .neos-contentcollection":
85+
"""
86+
BEFOREHallo - this is rendered.AFTER
87+
"""
88+
# only /sites/test/sub
89+
And the rendering queue for content release "6" contains 1 document
8590

8691
# however, when we re-run the rendering (in the next iteration), the rendering should converge and work out.
8792
And I run the renderer for content release "6" until the queue is empty

Tests/Behavior/Features/ContentStore/QuickRelease.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@fixtures
2+
@flowEntities
23
@resetRedis
34
Feature: Quick Release
45

Tests/Behavior/Features/ContentStore/Renamings.feature

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@fixtures
2+
@flowEntities
23
@resetRedis
34
Feature: Renamings before rendering, and during a rendering.
45

@@ -259,14 +260,15 @@ Feature: Renamings before rendering, and during a rendering.
259260
And I flush the content cache depending on the modified nodes
260261
And a next content release was triggered
261262

262-
# - here, /sites/test is not in the cache (DescendantOf_ modification), /sites/test/sub2 is not in the cache (DescendantOf_ modification)
263-
# and /sites/test/sub is IN THE CACHE.
263+
# - here, /sites/test/sub2 is not in the cache (DescendantOf_ modification of its content collection),
264+
# while /sites/test and /sites/test/sub ARE IN THE CACHE: a content collection is tagged with the collection
265+
# node, so a change below another document does not reach them.
264266

265267
# NOW, start a new enumeration and a new content release.
266268
When I create a content release "6"
267269
When I enumerate all nodes for content release "6"
268270
Then the enumeration for content release "6" contains 3 nodes
269-
# - Render Orchestrator would add /sites/test/sub to Content Release, and schedule /sites/test and /sites/test/sub2 for rendering
271+
# - Render Orchestrator would add /sites/test and /sites/test/sub to Content Release, and schedule /sites/test/sub2 for rendering
270272
When I run the render-orchestrator control loop once for content release "6"
271273

272274
# - Rename Uri Path Semgment to "foo" for the sub2 page
@@ -278,18 +280,19 @@ Feature: Renamings before rendering, and during a rendering.
278280
And I flush the content cache depending on the modified nodes
279281

280282
# - Cache is flushed for sub2
281-
# - Cache should also be flushed for /sites/site and /sites/site/sub because of FixedNodeLinkHandlingInContentCacheFlusherAspect
282-
# - however, /sites/site/sub has already a been added to the content release with the old URI -> BROKEN LINK which goes live.
283+
# - Cache is also flushed for /sites/test and /sites/test/sub, because both link to sub2 and therefore carry its
284+
# NodeDynamicTag_ (added by Neos.Neos:ConvertUris while rendering the link)
285+
# - however, both have already been added to the content release with the old URI -> BROKEN LINKS which go live.
283286
And I run the renderer for content release "6" until the queue is empty
284287
When I continue running the render-orchestrator control loop
285288
Then I expect the render-orchestrator control loop to exit with status code 0
286289

287290
Then during rendering of content release "6", 0 errors occured
288291
Then I expect the content release "6" to not contain anything for URI "http://test.de/de/nested2"
289-
# no broken link :-)
292+
# THIS IS THE BROKEN LINK
290293
Then I expect the content release "6" to contain the following HTML content for URI "http://test.de/de" at CSS selector "body .neos-contentcollection":
291294
"""
292-
BEFOREHallo - this is rendered. <a href="/de/foo">Link to /nested2</a>AFTER
295+
BEFOREHallo - this is rendered. <a href="/de/nested2">Link to /nested2</a>AFTER
293296
"""
294297
# THIS IS THE BROKEN LINK
295298
Then I expect the content release "6" to contain the following HTML content for URI "http://test.de/de/nested" at CSS selector "body .neos-contentcollection":
@@ -314,7 +317,7 @@ Feature: Renamings before rendering, and during a rendering.
314317
Then I expect the render-orchestrator control loop to exit with status code 0
315318
Then during rendering of content release "7", 0 errors occured
316319
Then I expect the content release "6" to not contain anything for URI "http://test.de/de/nested2"
317-
# no broken link :-)
320+
# BROKEN LINK -> fixed
318321
Then I expect the content release "7" to contain the following HTML content for URI "http://test.de/de" at CSS selector "body .neos-contentcollection":
319322
"""
320323
BEFOREHallo - this is rendered. <a href="/de/foo">Link to /nested2</a>AFTER

Tests/Behavior/Fixtures/StubPrunnerApiService.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,22 @@
77
use Flowpack\Prunner\PrunnerApiService;
88
use Flowpack\Prunner\ValueObject\JobId;
99
use Flowpack\Prunner\ValueObject\PipelineName;
10+
use Flowpack\Prunner\ValueObject\QueuePartitionName;
1011

1112
class StubPrunnerApiService extends PrunnerApiService
1213
{
1314
public array $calls = [];
1415

15-
public function schedulePipeline(PipelineName $pipeline, array $variables): JobId
16-
{
16+
public function schedulePipeline(
17+
PipelineName $pipeline,
18+
array $variables,
19+
?QueuePartitionName $queuePartition = null
20+
): JobId {
1721
$this->calls[] = [
1822
'method' => 'schedulePipeline',
1923
'pipeline' => $pipeline,
20-
'variables' => $variables
24+
'variables' => $variables,
25+
'queuePartition' => $queuePartition
2126
];
2227

2328
return JobId::create('STUB' . count($this->calls));

0 commit comments

Comments
 (0)