Repository navigation
Expand file tree
/
Copy pathCapabilityContractTest.php
More file actions
96 lines (79 loc) 路 3.68 KB
/
Copy pathCapabilityContractTest.php
File metadata and controls
96 lines (79 loc) 路 3.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?php
declare(strict_types=1);
namespace DrevOps\BehatSteps\Tests;
use DrevOps\BehatSteps\Backend\Entity\EntityStubInterface;
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\DataProvider;
/**
* Asserts the return contract of every capability create, update and delete.
*
* A teardown deletes whatever a scenario created without checking first, so
* a create returns a stub and a delete returns nothing. An update and a copy
* return a stub as a create does.
*
* CONTRIBUTING.md states the contract, including that a delete tolerates a
* missing target.
*/
#[CoversNothing]
class CapabilityContractTest extends UnitTestCase {
/**
* Assert that a create, an update or a copy returns an entity stub.
*
* @param class-string $interface
* The capability interface declaring the method.
* @param string $method
* The method name.
*/
#[DataProvider('dataProviderCreateReturnsStub')]
public function testCreateReturnsStub(string $interface, string $method): void {
$type = static::reflect($interface)->getMethod($method)->getReturnType();
$declared = $type instanceof \ReflectionType ? (string) $type : 'no return type';
$this->assertSame(EntityStubInterface::class, $declared, sprintf('A capability create, update or copy returns the stub: "%s(): %s", not "%s(): %s".', $method, EntityStubInterface::class, $method, $declared));
}
public static function dataProviderCreateReturnsStub(): \Iterator {
yield from static::collectCapabilityMethods('/^(?:create|place|update|duplicate)[A-Z]/');
}
/**
* Assert that a delete method returns nothing.
*
* @param class-string $interface
* The capability interface declaring the method.
* @param string $method
* The method name.
*/
#[DataProvider('dataProviderDeleteReturnsVoid')]
public function testDeleteReturnsVoid(string $interface, string $method): void {
$type = static::reflect($interface)->getMethod($method)->getReturnType();
$declared = $type instanceof \ReflectionType ? (string) $type : 'no return type';
$this->assertSame('void', $declared, sprintf('A capability delete returns nothing and tolerates a missing target: "%s(): void", not "%s(): %s".', $method, $method, $declared));
}
public static function dataProviderDeleteReturnsVoid(): \Iterator {
// An entity delete opens with its verb, while the config and state
// deletes keep their subsystem prefix: 'deleteNode()', 'configDelete()'.
yield from static::collectCapabilityMethods('/^delete[A-Z]|Delete$/');
}
/**
* Yield the capability methods whose name matches a pattern.
*
* @param string $pattern
* The regular expression a method name must match.
*
* @return \Iterator<string, array{class-string, string}>
* Interface and method name pairs, keyed by 'Interface::method'.
*/
protected static function collectCapabilityMethods(string $pattern): \Iterator {
$files = array_merge(glob(realpath(__DIR__ . '/../../../src/Backend/Capability') . '/*Interface.php') ?: [], glob(realpath(__DIR__ . '/../../../src/Backend/Drupal/Capability') . '/*Interface.php') ?: []);
foreach ($files as $file) {
$namespace = str_contains($file, '/Backend/Drupal/') ? 'DrevOps\\BehatSteps\\Backend\\Drupal\\Capability\\' : 'DrevOps\\BehatSteps\\Backend\\Capability\\';
$interface = $namespace . basename($file, '.php');
if (!interface_exists($interface)) {
continue;
}
foreach (static::reflect($interface)->getMethods() as $method) {
if (preg_match($pattern, $method->getName()) === 1) {
yield basename($file, '.php') . '::' . $method->getName() => [$interface, $method->getName()];
}
}
}
}
}