Found while validating the fork on lawnstarter/signup-api for PE-80049 (https://lawnstarter.atlassian.net/browse/PE-80049). Graded shape: Q1 — "Who calls X?".
Summary
Calls made inside the closure argument to Pest's it() / test() / describe() produce no node and no calls edge. The identical call written in a classic PHPUnit test class resolves normally. There is no warning, so "who calls X" silently under-reports — often to zero — for any repo that uses Pest's functional style.
Impact on signup-api
220 of 237 test files in that repo use the Pest functional style. Only 16 files repo-wide contribute any calls edge originating in tests/, and those are almost entirely the 12 class-style holdouts.
Concretely, graphify affected "CreateProspect" --relation calls returns exactly one caller — the production mutation — and misses all 12 call sites in tests/Integration/internal/app/Actions/Prospects/CreateProspectTest.php, each a plain CreateProspect::make()->handle(...) at closure top level. This was the one graded comprehension question that failed for the repo.
Minimal reproduction
Three files:
app/Greeter.php
<?php
namespace App;
class Greeter
{
public function greet(): string { return 'hi'; }
}
tests/GreeterPestTest.php
<?php
use App\Greeter;
it('greets from a pest closure', function () {
$g = new Greeter();
expect($g->greet())->toBe('hi');
});
tests/GreeterClassTest.php
<?php
namespace Tests;
use App\Greeter;
use PHPUnit\Framework\TestCase;
class GreeterClassTest extends TestCase
{
public function test_it_greets(): void
{
$g = new Greeter();
$this->assertSame('hi', $g->greet());
}
}
Then:
graphify update .
graphify affected "Greeter" --relation calls
Actual
Affected nodes for Greeter
Relations: calls
Depth: 2
- .test_it_greets() [calls] tests/GreeterClassTest.php:L12
Expected
Both call sites listed — the Pest closure at tests/GreeterPestTest.php:L6 alongside the class-based one. Failing that, the closure body should at least be attributed to some node (e.g. a synthetic node named for the it() description) rather than dropped without a warning.
Notes
This is the "silent incompleteness" limit from the successor spike (Finding 4) landing on a very large surface: a consumer cannot distinguish "this action has no test callers" from "its test callers were refused".
Related but distinct from the TypeScript closure issues #63 and #68 — this is the PHP extractor, and the trigger is the closure being an argument to a global function call rather than a module-scope statement.
Environment: graphify 0.9.40, fork origin/v8 @ 5e9d95c11837aeabbbb5d90cb45544f49dc58ca4, cold cache.
Found while validating the fork on
lawnstarter/signup-apifor PE-80049 (https://lawnstarter.atlassian.net/browse/PE-80049). Graded shape: Q1 — "Who calls X?".Summary
Calls made inside the closure argument to Pest's
it()/test()/describe()produce no node and nocallsedge. The identical call written in a classic PHPUnit test class resolves normally. There is no warning, so "who calls X" silently under-reports — often to zero — for any repo that uses Pest's functional style.Impact on signup-api
220 of 237 test files in that repo use the Pest functional style. Only 16 files repo-wide contribute any
callsedge originating intests/, and those are almost entirely the 12 class-style holdouts.Concretely,
graphify affected "CreateProspect" --relation callsreturns exactly one caller — the production mutation — and misses all 12 call sites intests/Integration/internal/app/Actions/Prospects/CreateProspectTest.php, each a plainCreateProspect::make()->handle(...)at closure top level. This was the one graded comprehension question that failed for the repo.Minimal reproduction
Three files:
app/Greeter.phptests/GreeterPestTest.phptests/GreeterClassTest.phpThen:
Actual
Expected
Both call sites listed — the Pest closure at
tests/GreeterPestTest.php:L6alongside the class-based one. Failing that, the closure body should at least be attributed to some node (e.g. a synthetic node named for theit()description) rather than dropped without a warning.Notes
This is the "silent incompleteness" limit from the successor spike (Finding 4) landing on a very large surface: a consumer cannot distinguish "this action has no test callers" from "its test callers were refused".
Related but distinct from the TypeScript closure issues #63 and #68 — this is the PHP extractor, and the trigger is the closure being an argument to a global function call rather than a module-scope statement.
Environment:
graphify 0.9.40, forkorigin/v8@5e9d95c11837aeabbbb5d90cb45544f49dc58ca4, cold cache.