Skip to content

Commit 8f6d632

Browse files
committed
fix: compose managed multisite PHPUnit runtime
1 parent 3f9ca38 commit 8f6d632

6 files changed

Lines changed: 234 additions & 58 deletions

packages/cli/src/commands/recipe-runtime-setup.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,20 @@ export async function applyRecipeRuntimeSetup(args: {
127127
interruption?.throwIfInterrupted()
128128
}
129129

130+
const extraPluginMounts: MountSpec[] = extraPlugins.map((plugin) => ({
131+
type: "directory",
132+
source: plugin.source,
133+
target: plugin.target,
134+
mode: "readonly",
135+
metadata: {
136+
kind: "extra-plugin",
137+
slug: plugin.slug,
138+
source: plugin.provenance,
139+
},
140+
}))
130141
await phaseTracker.run("mount_plugins", phasePluginMountData(extraPlugins), async () => {
131-
for (const plugin of extraPlugins) {
132-
await awaitRecipe(`extra-plugin.mount:${plugin.slug}`, runtime.mount({
133-
type: "directory",
134-
source: plugin.source,
135-
target: plugin.target,
136-
mode: "readonly",
137-
metadata: {
138-
kind: "extra-plugin",
139-
slug: plugin.slug,
140-
source: plugin.provenance,
141-
},
142-
}))
142+
for (const [index, plugin] of extraPlugins.entries()) {
143+
await awaitRecipe(`extra-plugin.mount:${plugin.slug}`, runtime.mount(extraPluginMounts[index]))
143144
interruption?.throwIfInterrupted()
144145
}
145146
})
@@ -190,6 +191,7 @@ export async function applyRecipeRuntimeSetup(args: {
190191
}
191192

192193
const materializableMounts: MountSpec[] = [
194+
...extraPluginMounts,
193195
...inputMounts,
194196
...stagedFiles.map((stagedFile) => ({
195197
type: stagedFile.type,

packages/runtime-playground/src/phpunit-command-handlers.ts

Lines changed: 100 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export interface PhpunitRunCodeOptions {
3030
resultFile?: string
3131
}
3232

33+
export type PhpunitMultisitePreinstallCodeOptions = Pick<PhpunitRunCodeOptions, "testsDir" | "env" | "wpConfigDefines" | "databaseType" | "resultFile">
34+
3335
export interface CorePhpunitRunCodeOptions {
3436
coreRoot: string
3537
testsDir: string
@@ -334,6 +336,101 @@ function ${functionName}(array $argv) {
334336
}`
335337
}
336338

339+
function managedPhpunitConfigWriterPhp(): string {
340+
return `function pg_write_managed_test_config(array $extra_defines, string $table_prefix, string $database_type): string {
341+
$config_path = '/tmp/wp-tests-config.php';
342+
$config = "<?php\n";
343+
pg_append_wp_config_defines($config, $extra_defines);
344+
$config .= '$table_prefix = ' . var_export($table_prefix, true) . ";\n";
345+
if ($database_type === 'mysql') {
346+
$db_host = getenv('DB_HOST');
347+
if (!is_string($db_host) || $db_host === '') {
348+
throw new RuntimeException('Managed PHPUnit requires DB_HOST when database-type=mysql; declare a MySQL runtime service with canonical DB_* outputs.');
349+
}
350+
$db_port = getenv('DB_PORT');
351+
if (is_string($db_port) && $db_port !== '') {
352+
$db_host .= ':' . $db_port;
353+
}
354+
foreach (array(
355+
'DB_NAME' => getenv('DB_NAME') ?: 'runtime',
356+
'DB_USER' => getenv('DB_USER') ?: 'runtime',
357+
'DB_PASSWORD' => getenv('DB_PASSWORD') ?: '',
358+
'DB_HOST' => $db_host,
359+
) as $name => $value) {
360+
$config .= 'define(' . var_export($name, true) . ', ' . var_export($value, true) . ");\n";
361+
}
362+
} else {
363+
$config .= <<<'CONFIG'
364+
define('DB_NAME', ':memory:');
365+
define('DB_USER', 'root');
366+
define('DB_PASSWORD', '');
367+
define('DB_HOST', 'localhost');
368+
CONFIG;
369+
}
370+
$config .= <<<'CONFIG'
371+
define('DB_CHARSET', 'utf8');
372+
define('WP_TESTS_DOMAIN', 'example.org');
373+
define('WP_TESTS_EMAIL', 'admin@example.org');
374+
define('WP_TESTS_TITLE', 'Test Blog');
375+
define('WP_PHP_BINARY', 'php');
376+
define('ABSPATH', '/wordpress/');
377+
define('FS_CHMOD_FILE', 0644);
378+
define('FS_CHMOD_DIR', 0755);
379+
define('FS_METHOD', 'direct');
380+
CONFIG;
381+
file_put_contents($config_path, $config);
382+
return $config_path;
383+
}`
384+
}
385+
386+
export function phpunitMultisitePreinstallCode(options: PhpunitMultisitePreinstallCodeOptions): string {
387+
const wpConfigDefines = { ...options.wpConfigDefines }
388+
for (const name of ["MULTISITE", "SUBDOMAIN_INSTALL", "DOMAIN_CURRENT_SITE", "PATH_CURRENT_SITE", "SITE_ID_CURRENT_SITE", "BLOG_ID_CURRENT_SITE"]) {
389+
delete wpConfigDefines[name]
390+
}
391+
return `error_reporting(E_ALL);
392+
ini_set('display_errors', '1');
393+
ini_set('display_startup_errors', '1');
394+
395+
$tests_dir = ${JSON.stringify(options.testsDir)};
396+
$bench_env = json_decode(${JSON.stringify(JSON.stringify(options.env))}, true);
397+
$wp_config_defines = json_decode(${JSON.stringify(JSON.stringify(wpConfigDefines))}, true);
398+
$database_type = ${JSON.stringify(options.databaseType)};
399+
$result_file = ${JSON.stringify(options.resultFile ?? PLUGIN_PHPUNIT_RESULT_FILE)};
400+
$preinstall_complete = false;
401+
402+
@file_put_contents($result_file, '');
403+
register_shutdown_function(static function () use (&$preinstall_complete, $result_file): void {
404+
if ($preinstall_complete) {
405+
return;
406+
}
407+
$failure = error_get_last();
408+
if (is_array($failure) && in_array($failure['type'] ?? 0, array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR), true)) {
409+
@file_put_contents($result_file, 'STAGE_FATAL:preinstall:' . (string) ($failure['message'] ?? '') . ' at ' . (string) ($failure['file'] ?? '') . ':' . (int) ($failure['line'] ?? 0) . "\n", FILE_APPEND);
410+
}
411+
});
412+
413+
${phpEnvAssignmentFunction("pg_apply_env", "json_encode", "error_log('Skipping invalid environment key: ' . var_export($name, true));")}
414+
${phpWpConfigDefineAppenderFunction("pg_append_wp_config_defines", "error_log('Skipping invalid wp_config_defines key: ' . var_export($name, true));")}
415+
${managedPhpunitConfigWriterPhp()}
416+
417+
pg_apply_env($bench_env);
418+
$config_path = pg_write_managed_test_config($wp_config_defines, 'wptests_', $database_type);
419+
if (!defined('WP_TESTS_MULTISITE')) {
420+
define('WP_TESTS_MULTISITE', true);
421+
}
422+
$argv = array('install.php', $config_path, 'run_ms_tests', 'no_core_tests');
423+
$_SERVER['argv'] = $argv;
424+
$_SERVER['argc'] = count($argv);
425+
try {
426+
require $tests_dir . '/includes/install.php';
427+
$preinstall_complete = true;
428+
} catch (Throwable $error) {
429+
@file_put_contents($result_file, 'STAGE_FAIL:preinstall:' . get_class($error) . ': ' . $error->getMessage() . ' at ' . $error->getFile() . ':' . $error->getLine() . "\n", FILE_APPEND);
430+
throw $error;
431+
}`
432+
}
433+
337434
export function phpunitRunCode(options: PhpunitRunCodeOptions): string {
338435
return `error_reporting(E_ALL);
339436
ini_set('display_errors', '1');
@@ -415,6 +512,8 @@ ${phpEnvAssignmentFunction("pg_apply_env", "json_encode", "pg_log('NOTICE: skipp
415512
416513
${phpWpConfigDefineAppenderFunction("pg_append_wp_config_defines", "pg_log('NOTICE: skipping invalid wp_config_defines key: ' . var_export($name, true));")}
417514
515+
${managedPhpunitConfigWriterPhp()}
516+
418517
function pg_diagnostic_context(): string {
419518
global $current_stage;
420519
$hook = function_exists('current_filter') ? current_filter() : null;
@@ -661,48 +760,8 @@ function pg_run_boot_stage(array $cfg = []): ?string {
661760
$autoload_required = !empty($cfg['autoload_required']);
662761
$extra_defines = $cfg['extra_defines'] ?? array();
663762
$table_prefix = isset($cfg['table_prefix']) && is_string($cfg['table_prefix']) && $cfg['table_prefix'] !== '' ? $cfg['table_prefix'] : 'wptests_';
664-
$config_path = '/tmp/wp-tests-config.php';
665-
$config = "<?php\n";
666-
pg_append_wp_config_defines($config, $extra_defines);
667-
$config .= '$table_prefix = ' . var_export($table_prefix, true) . ";\n";
668763
$database_type = (string) ($cfg['database_type'] ?? 'sqlite');
669-
if ($database_type === 'mysql') {
670-
$db_host = getenv('DB_HOST');
671-
if (!is_string($db_host) || $db_host === '') {
672-
throw new RuntimeException('Managed PHPUnit requires DB_HOST when database-type=mysql; declare a MySQL runtime service with canonical DB_* outputs.');
673-
}
674-
$db_port = getenv('DB_PORT');
675-
if (is_string($db_port) && $db_port !== '') {
676-
$db_host .= ':' . $db_port;
677-
}
678-
foreach (array(
679-
'DB_NAME' => getenv('DB_NAME') ?: 'runtime',
680-
'DB_USER' => getenv('DB_USER') ?: 'runtime',
681-
'DB_PASSWORD' => getenv('DB_PASSWORD') ?: '',
682-
'DB_HOST' => $db_host,
683-
) as $name => $value) {
684-
$config .= 'define(' . var_export($name, true) . ', ' . var_export($value, true) . ");\n";
685-
}
686-
} else {
687-
$config .= <<<'CONFIG'
688-
define('DB_NAME', ':memory:');
689-
define('DB_USER', 'root');
690-
define('DB_PASSWORD', '');
691-
define('DB_HOST', 'localhost');
692-
CONFIG;
693-
}
694-
$config .= <<<'CONFIG'
695-
define('DB_CHARSET', 'utf8');
696-
define('WP_TESTS_DOMAIN', 'example.org');
697-
define('WP_TESTS_EMAIL', 'admin@example.org');
698-
define('WP_TESTS_TITLE', 'Test Blog');
699-
define('WP_PHP_BINARY', 'php');
700-
define('ABSPATH', '/wordpress/');
701-
define('FS_CHMOD_FILE', 0644);
702-
define('FS_CHMOD_DIR', 0755);
703-
define('FS_METHOD', 'direct');
704-
CONFIG;
705-
file_put_contents($config_path, $config);
764+
$config_path = pg_write_managed_test_config($extra_defines, $table_prefix, $database_type);
706765
if ($harness_autoload !== '' && is_readable($harness_autoload)) {
707766
pg_preload_wp_cli_namespaced_functions($harness_autoload);
708767
require_once $harness_autoload;

packages/runtime-playground/src/wordpress-command-runners.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
restPerformanceObservationInputFromArgs,
3131
restPerformanceObservationPhpCode,
3232
phpunitRunCode,
33+
phpunitMultisitePreinstallCode,
3334
pluginStateInputFromArgs,
3435
pluginStatePhpCode,
3536
PLUGIN_PHPUNIT_RESULT_FILE,
@@ -928,6 +929,7 @@ export async function runPhpunitCommand({
928929
const autoloadFile = argValue(args, "autoload-file")?.trim() || (bootstrapMode === "project" ? "" : "/wp-codebox-vendor/autoload.php")
929930
const autoloadFileRole = argValue(args, "autoload-file-role")?.trim() === "harness" ? "harness" : undefined
930931
const processIdentity = boundedProcessIdentity(spec.processIdentity)
932+
const multisite = booleanArg(args, "multisite")
931933
const resultFile = processIdentity ? `/tmp/wp-codebox-phpunit-result-${processIdentity}.txt` : PLUGIN_PHPUNIT_RESULT_FILE
932934
const diagnosticHostFile = `/wordpress/wp-content/plugins/${pluginSlug}/.pg-test-result${processIdentity ? `-${processIdentity}` : ""}.txt`
933935
const code = explicitCode ? await phpCodeFromArgs(args, "wordpress.phpunit", false) : phpunitRunCode({
@@ -950,7 +952,7 @@ export async function runPhpunitCommand({
950952
preloadFiles: jsonArrayArg(args, "preload-files-json").filter((value): value is string => typeof value === "string"),
951953
bootstrapMode,
952954
projectBootstrap: argValue(args, "project-bootstrap")?.trim() || "",
953-
multisite: booleanArg(args, "multisite"),
955+
multisite,
954956
databaseType,
955957
resultFile,
956958
})
@@ -960,6 +962,17 @@ export async function runPhpunitCommand({
960962
let response: PlaygroundRunResponse
961963
try {
962964
const bootstrapArgs = explicitCode ? args : [...args, "bootstrap=runtime-only"]
965+
if (!explicitCode && bootstrapMode === "managed" && databaseType === "mysql" && multisite) {
966+
const preinstallCode = phpunitMultisitePreinstallCode({
967+
testsDir: argValue(args, "tests-dir")?.trim() || "/wp-codebox-vendor/wp-phpunit/wp-phpunit",
968+
env: jsonObjectArg(args, "env-json"),
969+
wpConfigDefines: jsonObjectArg(args, "wp-config-defines-json"),
970+
databaseType,
971+
resultFile,
972+
})
973+
const preinstallResponse = await runPlaygroundCommand("wordpress.phpunit", server, { code: bootstrapPhpCode(runtimeSpec, preinstallCode, bootstrapArgs) })
974+
assertPlaygroundResponseOk("wordpress.phpunit multisite preinstall", preinstallResponse)
975+
}
963976
response = await runPlaygroundCommand("wordpress.phpunit", server, { code: bootstrapPhpCode(runtimeSpec, code, bootstrapArgs, undefined, resultFile) })
964977
} catch (error) {
965978
await persistPluginPhpunitResult(server, resultFile, artifactRoot, processIdentity)

tests/disposable-mysql-mysqli.integration.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,12 @@ require_once ABSPATH . 'wp-settings.php';
149149
assert.deepEqual(aggregate.entries.map((entry: { inputIndex: number }) => entry.inputIndex), [0, 1])
150150

151151
const multisitePlugin = join(directory, "managed-multisite-fixture")
152+
const multisiteDependency = join(directory, "managed-multisite-dependency")
152153
const multisiteRecipePath = join(directory, "managed-multisite-recipe.json")
153154
const multisiteArtifacts = join(directory, "managed-multisite-artifacts")
154155
await mkdir(join(multisitePlugin, "tests"), { recursive: true })
156+
await mkdir(multisiteDependency, { recursive: true })
157+
await writeFile(join(multisiteDependency, "managed-multisite-dependency.php"), "<?php\n/** Plugin Name: Managed Multisite Dependency */\n")
155158
await writeFile(join(multisitePlugin, "managed-multisite-fixture.php"), `<?php
156159
/** Plugin Name: Managed Multisite Fixture */
157160
add_action('init', static function (): void {
@@ -189,6 +192,13 @@ final class ManagedMultisiteTest extends WP_UnitTestCase {
189192
$this->assertInstanceOf(mysqli::class, $wpdb->dbh);
190193
$this->assertSame('1', (string) $wpdb->get_var("SELECT JSON_VALID('{\\"valid\\":true}')"));
191194
}
195+
196+
public function test_network_schema_seed_and_dependency_are_materialized(): void {
197+
global $wpdb;
198+
$this->assertGreaterThanOrEqual(1, (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->blogs} WHERE blog_id = 1"));
199+
$this->assertGreaterThan(0, (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->sitemeta} WHERE site_id = 1"));
200+
$this->assertFileExists('/wordpress/wp-content/plugins/managed-multisite-dependency/managed-multisite-dependency.php');
201+
}
192202
}
193203
`)
194204
const multisiteRecipe = buildWordPressPhpunitRecipe({
@@ -197,7 +207,8 @@ final class ManagedMultisiteTest extends WP_UnitTestCase {
197207
databaseType: "mysql",
198208
multisite: true,
199209
pluginSource: multisitePlugin,
200-
dependencyMounts: ["/wordpress/wp-content/plugins/managed-multisite-fixture"],
210+
extra_plugins: [{ source: multisiteDependency, slug: "managed-multisite-dependency", activate: false }],
211+
dependencyMounts: ["/wordpress/wp-content/plugins/managed-multisite-fixture", "/wordpress/wp-content/plugins/managed-multisite-dependency"],
201212
mounts: [{ source: join(harness, "vendor"), target: "/wp-codebox-vendor", mode: "readonly" }],
202213
})
203214
await writeFile(multisiteRecipePath, `${JSON.stringify(multisiteRecipe)}\n`)

tests/phpunit-project-autoload.test.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import { tmpdir } from "node:os"
55
import { join } from "node:path"
66

77
import { buildWordPressPhpunitRecipe } from "../packages/runtime-core/src/recipe-builders.js"
8-
import { corePhpunitRunCode, phpunitRunCode } from "../packages/runtime-playground/src/phpunit-command-handlers.js"
8+
import { corePhpunitRunCode, phpunitMultisitePreinstallCode, phpunitRunCode } from "../packages/runtime-playground/src/phpunit-command-handlers.js"
99
import { runPhpunitCommand } from "../packages/runtime-playground/src/wordpress-command-runners.js"
1010
import { recipePolicy } from "../packages/cli/src/recipe-validation.js"
1111
import { recipeExtraPluginSourceSubpath } from "../packages/cli/src/recipe-sources.js"
1212
import { recipeInputMountPathMap, rewriteInputMountPathArgs } from "../packages/cli/src/commands/recipe-runtime-setup.js"
1313

1414
const woocommerceAutoload = "/wordpress/wp-content/plugins/woocommerce/vendor/autoload_packages.php"
1515
const phpunitRuntimeSpec = {
16+
environment: { kind: "wordpress", name: "test", version: "latest" },
1617
runtimeEnv: { TC_MYSQL_PORT: "3306" },
1718
} as never
1819

@@ -666,6 +667,73 @@ const decodedMysqlCode = decodedBootstrapWrapper(capturedMysqlCode)
666667
assert.ok(decodedMysqlCode.includes('$database_type = "mysql";'))
667668
assert.ok(decodedMysqlCode.includes("'DB_HOST' => $db_host"), "managed PHPUnit writes the provisioned MySQL host into wp-tests-config.php")
668669
assert.ok(decodedMysqlCode.includes("getenv('DB_PASSWORD')"), "managed PHPUnit consumes the provisioned MySQL credentials")
670+
671+
const preinstallCode = phpunitMultisitePreinstallCode({
672+
testsDir: "/wp-codebox-vendor/wp-phpunit/wp-phpunit",
673+
env: {},
674+
wpConfigDefines: { MULTISITE: true, DOMAIN_CURRENT_SITE: "example.org" },
675+
databaseType: "mysql",
676+
resultFile: "/tmp/preinstall-result.txt",
677+
})
678+
assert.ok(preinstallCode.includes("'run_ms_tests'"), "multisite preinstall selects wp-phpunit's network installer")
679+
assert.ok(preinstallCode.includes("define('WP_TESTS_MULTISITE', true)"), "multisite preinstall enables wp-phpunit network installation")
680+
assert.ok(preinstallCode.includes("require $tests_dir . '/includes/install.php'"), "multisite preinstall uses wp-phpunit's installer")
681+
assert.ok(preinstallCode.includes("pg_write_managed_test_config($wp_config_defines, 'wptests_', $database_type)"), "multisite preinstall uses the managed runner's database config and prefix")
682+
assert.equal(preinstallCode.includes("define('MULTISITE'"), false, "multisite preinstall must not bootstrap WordPress as an installed network")
683+
assert.equal(preinstallCode.includes("DOMAIN_CURRENT_SITE"), false, "multisite preinstall must not define current-network constants")
684+
assert.ok(preinstallCode.includes("@file_put_contents($result_file, '');"), "multisite preinstall clears stale diagnostics")
685+
assert.ok(preinstallCode.includes("STAGE_FAIL:preinstall:"), "multisite preinstall records throwable diagnostics")
686+
assert.ok(preinstallCode.includes("STAGE_FATAL:preinstall:"), "multisite preinstall records fatal diagnostics")
687+
688+
const mysqlMultisiteInvocations: string[] = []
689+
await runPhpunitCommand({
690+
artifactRoot: mkdtempSync(join(tmpdir(), "wp-codebox-phpunit-mysql-multisite-")),
691+
mounts: [],
692+
runPlaygroundCommand: async (_command, _server, input) => {
693+
mysqlMultisiteInvocations.push(decodedBootstrapWrapper(input.code))
694+
return { text: "ok", exitCode: 0 }
695+
},
696+
runtimeSpec: {
697+
environment: { kind: "wordpress", name: "test", version: "latest", databaseSetup: "external" },
698+
runtimeEnv: { DB_HOST: "127.0.0.1", DB_PORT: "3307", DB_NAME: "runtime", DB_USER: "runtime", DB_PASSWORD: "secret" },
699+
policy: { commands: ["wordpress.phpunit"] },
700+
} as never,
701+
server: { playground: {} } as never,
702+
spec: { command: "wordpress.phpunit", args: ["plugin-slug=demo-plugin", "database-type=mysql", "multisite=1"] },
703+
})
704+
assert.equal(mysqlMultisiteInvocations.length, 2, "managed MySQL multisite runs a separate preinstall before PHPUnit")
705+
assert.ok(mysqlMultisiteInvocations[0].includes("'run_ms_tests'"))
706+
assert.ok(mysqlMultisiteInvocations[1].includes("$phpunit_argv = pg_build_phpunit_argv"), "normal managed PHPUnit behavior follows preinstall")
707+
708+
const failedPreinstallInvocations: string[] = []
709+
await assert.rejects(
710+
runPhpunitCommand({
711+
artifactRoot: mkdtempSync(join(tmpdir(), "wp-codebox-phpunit-mysql-multisite-failure-")),
712+
mounts: [],
713+
runPlaygroundCommand: async (_command, _server, input) => {
714+
failedPreinstallInvocations.push(decodedBootstrapWrapper(input.code))
715+
return { text: "preinstall failed", exitCode: 1 }
716+
},
717+
runtimeSpec: {
718+
environment: { kind: "wordpress", name: "test", version: "latest", databaseSetup: "external" },
719+
runtimeEnv: { DB_HOST: "127.0.0.1", DB_PORT: "3307", DB_NAME: "runtime", DB_USER: "runtime", DB_PASSWORD: "secret" },
720+
policy: { commands: ["wordpress.phpunit"] },
721+
} as never,
722+
server: {
723+
playground: {
724+
readFileAsText: async () => "STAGE_FAIL:preinstall:RuntimeException: network install failed",
725+
},
726+
} as never,
727+
spec: { command: "wordpress.phpunit", args: ["plugin-slug=demo-plugin", "database-type=mysql", "multisite=1"] },
728+
}),
729+
(error: Error) => {
730+
assert.match(error.message, /wordpress\.phpunit multisite preinstall failed with exit code 1/)
731+
assert.match(error.message, /wordpress\.phpunit structured diagnostics/)
732+
assert.match(error.message, /network install failed/)
733+
return true
734+
},
735+
)
736+
assert.equal(failedPreinstallInvocations.length, 1, "failed multisite preinstall prevents the main PHPUnit invocation")
669737
await assert.rejects(runPhpunitCommand({
670738
artifactRoot: mkdtempSync(join(tmpdir(), "wp-codebox-phpunit-mysql-reject-")),
671739
mounts: [],

0 commit comments

Comments
 (0)