From 3cd4c7a7e42bc29c85219fdd0abaae22865b6916 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sat, 18 Jul 2026 08:34:37 -0400 Subject: [PATCH] Consolidate WordPress runtime inputs --- .../cli/src/commands/wordpress-runtime.ts | 10 +++++- packages/cli/src/recipe-dry-run.ts | 4 ++- packages/cli/src/recipe-validation.ts | 4 +++ .../runtime-core/src/agent-task-recipe.ts | 3 -- packages/runtime-core/src/command-registry.ts | 14 -------- .../wordpress-runtime-discovery-contracts.ts | 3 +- .../src/wordpress-workload-primitives.ts | 3 ++ .../class-wp-codebox-fuzz-suite-runner.php | 4 +-- scripts/php-fuzz-suite-runner-smoke.php | 2 +- tests/agent-task-contracts.test.ts | 3 +- ...layground-cli-runner-bootstrap-ini.test.ts | 9 +++--- tests/public-fuzz-workload-cli.test.ts | 32 +++++++++++++++++-- ...dpress-runtime-discovery-contracts.test.ts | 5 ++- 13 files changed, 61 insertions(+), 35 deletions(-) diff --git a/packages/cli/src/commands/wordpress-runtime.ts b/packages/cli/src/commands/wordpress-runtime.ts index bc9664dd..4631c8cc 100644 --- a/packages/cli/src/commands/wordpress-runtime.ts +++ b/packages/cli/src/commands/wordpress-runtime.ts @@ -183,7 +183,7 @@ function runtimeBackedFuzzSuitePolicy(suite: FuzzSuiteContract): RuntimePolicy { function fuzzSuiteRequiresRecipeRuntime(input: Record): boolean { const requirements = fuzzSuiteRuntimeRequirements(input) - return arrayOption(requirements?.extra_plugins).length > 0 || arrayOption(requirements?.component_contracts).length > 0 + return arrayOption(requirements?.extra_plugins).length > 0 || arrayOption(requirements?.component_contracts).length > 0 || Boolean(runtimeRequirementWordPressDirectory(requirements)) } function runtimeBackedFuzzSuiteCommands(suite: FuzzSuiteContract): string[] { @@ -310,6 +310,9 @@ function applyFuzzSuiteRuntimeRequirements(recipe: WorkspaceRecipe, requirements } recipe.runtime = { ...runtime, + assets: runtimeRequirementWordPressDirectory(requirements) + ? { ...(runtime.assets ?? {}), wordpressDirectory: runtimeRequirementWordPressDirectory(requirements) } + : runtime.assets, stack: arrayOption(requirements.runtime_mounts).length > 0 ? { ...(runtime.stack ?? {}), mounts: arrayOption(requirements.runtime_mounts) as WorkspaceRecipeMount[] } : runtime.stack, } recipe.metadata = { @@ -318,6 +321,10 @@ function applyFuzzSuiteRuntimeRequirements(recipe: WorkspaceRecipe, requirements } } +function runtimeRequirementWordPressDirectory(requirements: Record | undefined): string | undefined { + return requirements ? stringValue(requirements.wordpress_directory) : undefined +} + function fuzzSuiteRuntimeRequirements(suiteInput: Record): Record | undefined { return objectOption(objectOption(suiteInput.metadata)?.runtime_requirements) } @@ -428,6 +435,7 @@ function workloadRecipeOptions(input: Record, runtimeRequiremen const steps = workloadRecipeSteps(input, runtimeRequirements) return { wordpressVersion: stringValue(input.wordpressVersion ?? input.wordpress_version ?? input.wp), + wordpressDirectory: stringValue(input.wordpress_directory), blueprint: input.blueprint, preview: objectOption(input.preview) as WordPressWorkloadRunRecipeOptions["preview"], mounts: arrayOption(input.mounts) as WordPressWorkloadRunRecipeOptions["mounts"], diff --git a/packages/cli/src/recipe-dry-run.ts b/packages/cli/src/recipe-dry-run.ts index 97f86b86..c00171a8 100644 --- a/packages/cli/src/recipe-dry-run.ts +++ b/packages/cli/src/recipe-dry-run.ts @@ -1,5 +1,5 @@ import { basename, dirname, resolve } from "node:path" -import { fixtureImportDeterministicIdPlan, normalizeRuntimeBackendKind, validateRuntimePolicy, type FixtureImportDeterministicIdPlan, type MountSpec, type RuntimePolicy, type RuntimeWordPressInstallMode, type SandboxWorkspaceMode, type WorkspaceRecipe, type WorkspaceRecipeDeclaredArtifact, type WorkspaceRecipeDistribution, type WorkspaceRecipeDistributionStartupProbe, type WorkspaceRecipeFixtureDatabase, type WorkspaceRecipePluginRuntime, type WorkspaceRecipePluginRuntimeHealthProbe, type WorkspaceRecipeSiteSeed, type WorkspaceRecipeSiteSeedBootstrap, type WorkspaceRecipeWorkspace } from "@automattic/wp-codebox-core" +import { fixtureImportDeterministicIdPlan, normalizeRuntimeBackendKind, validateRuntimePolicy, type FixtureImportDeterministicIdPlan, type MountSpec, type RuntimeAssetSpec, type RuntimePolicy, type RuntimeWordPressInstallMode, type SandboxWorkspaceMode, type WorkspaceRecipe, type WorkspaceRecipeDeclaredArtifact, type WorkspaceRecipeDistribution, type WorkspaceRecipeDistributionStartupProbe, type WorkspaceRecipeFixtureDatabase, type WorkspaceRecipePluginRuntime, type WorkspaceRecipePluginRuntimeHealthProbe, type WorkspaceRecipeSiteSeed, type WorkspaceRecipeSiteSeedBootstrap, type WorkspaceRecipeWorkspace } from "@automattic/wp-codebox-core" import { SANDBOX_WORKSPACE_ROOT, stripUndefined } from "@automattic/wp-codebox-core/internals" import { serializeError } from "./output.js" import { RecipeArtifactsMountConflictError, recipeArtifactsMountConflict } from "./commands/recipe-run-artifacts-mount-guard.js" @@ -55,6 +55,7 @@ export interface RecipePlan { wp: string phpVersion?: string wordpressInstallMode?: RuntimeWordPressInstallMode + assets?: RuntimeAssetSpec blueprint: unknown extensions?: Array<{ manifest: string }> } @@ -441,6 +442,7 @@ export async function planWorkspaceRecipe(recipe: WorkspaceRecipe, recipeDirecto wp: recipe.runtime?.wp ?? context.defaultWordPressVersion, ...(recipe.runtime?.phpVersion ? { phpVersion: recipe.runtime.phpVersion } : {}), ...(recipe.runtime?.wordpressInstallMode ? { wordpressInstallMode: recipe.runtime.wordpressInstallMode } : {}), + ...(recipe.runtime?.assets ? { assets: recipe.runtime.assets } : {}), ...(recipe.runtime?.extensions ? { extensions: recipe.runtime.extensions } : {}), blueprint: recipeBlueprintWithBootActivePlugins(recipe.runtime?.blueprint, extraPlugins), }, diff --git a/packages/cli/src/recipe-validation.ts b/packages/cli/src/recipe-validation.ts index 1b38f9a7..6e2f2ae0 100644 --- a/packages/cli/src/recipe-validation.ts +++ b/packages/cli/src/recipe-validation.ts @@ -337,6 +337,10 @@ function validateRecipeRuntimeAssets(assets: RuntimeAssetSpec | undefined, recip if (assets.wordpressZip !== undefined && typeof assets.wordpressZip !== "string") { throw new Error(`Recipe runtime assets wordpressZip must be a string: ${recipePath}`) } + + if (assets.wordpressDirectory !== undefined && typeof assets.wordpressDirectory !== "string") { + throw new Error(`Recipe runtime assets wordpressDirectory must be a string: ${recipePath}`) + } } function validateRecipeRuntimeExtensions(extensions: NonNullable["extensions"] | undefined, recipePath: string): void { diff --git a/packages/runtime-core/src/agent-task-recipe.ts b/packages/runtime-core/src/agent-task-recipe.ts index 365b1a7d..d7914074 100644 --- a/packages/runtime-core/src/agent-task-recipe.ts +++ b/packages/runtime-core/src/agent-task-recipe.ts @@ -39,7 +39,6 @@ export interface AgentTaskRunInput { workspaces?: NonNullable["workspaces"] dependency_overlays?: NonNullable["dependency_overlays"] extra_plugins?: WorkspaceRecipeExtraPlugin[] - extraPlugins?: WorkspaceRecipeExtraPlugin[] runtime_stack_mounts?: WorkspaceRecipeMount[] runtime_overlays?: Array> agent_bundles?: Array> @@ -677,9 +676,7 @@ function agentTaskExtraPlugins(input: AgentTaskRunInput): WorkspaceRecipeExtraPl ...normalizeAgentTaskExtraPlugins(runtimeProfileRecord(input).plugins), ...normalizeAgentTaskExtraPlugins(runtimeProfileRecord(input).mu_plugins), ...normalizeAgentTaskExtraPlugins(input.extra_plugins), - ...normalizeAgentTaskExtraPlugins(input.extraPlugins), ...normalizeAgentTaskExtraPlugins(runtimeRequirements?.extra_plugins), - ...normalizeAgentTaskExtraPlugins(runtimeRequirements?.extraPlugins), ] } diff --git a/packages/runtime-core/src/command-registry.ts b/packages/runtime-core/src/command-registry.ts index b29abe07..e763a94e 100644 --- a/packages/runtime-core/src/command-registry.ts +++ b/packages/runtime-core/src/command-registry.ts @@ -627,20 +627,6 @@ export const commandRegistry = [ recipe: true, handler: { kind: "playground", method: "runRestRouteInventory" }, }, - { - id: "wordpress.inventory-rest-routes", - description: "Inventory registered WordPress REST routes for fuzzing seed discovery using rest_get_server()->get_routes().", - acceptedArgs: [], - outputShape: "wp-codebox/wordpress-rest-route-inventory/v1 JSON with route, namespace, methods, bounded endpoint permission descriptors, bounded arg/schema descriptors, status, and diagnostics.", - outputSchema: objectEnvelopeSchema(WORDPRESS_REST_ROUTE_INVENTORY_SCHEMA, { - routes: { type: "array" }, - namespaces: { type: "array" }, - diagnostics: { type: "array" }, - }), - policyRequirement: "Runtime policy commands must include wordpress.inventory-rest-routes.", - recipe: true, - handler: { kind: "playground", method: "runRestRouteInventory" }, - }, { id: "wordpress.admin-page-inventory", description: "Inventory already-loaded WordPress admin menu pages for fuzzing target discovery without crawling the browser UI.", diff --git a/packages/runtime-core/src/wordpress-runtime-discovery-contracts.ts b/packages/runtime-core/src/wordpress-runtime-discovery-contracts.ts index 1c06ff95..b0a30a5b 100644 --- a/packages/runtime-core/src/wordpress-runtime-discovery-contracts.ts +++ b/packages/runtime-core/src/wordpress-runtime-discovery-contracts.ts @@ -9,7 +9,6 @@ export const WORDPRESS_EXECUTION_ACTION_RESULT_SCHEMA = "wp-codebox/wordpress-ex export type WordPressRuntimeInventoryCommand = | "wordpress.rest-route-inventory" - | "wordpress.inventory-rest-routes" | "wordpress.admin-page-inventory" | "wordpress.admin-action-inventory" | "wordpress.inventory-database" @@ -48,7 +47,7 @@ export interface WordPressRestRouteDiscovery { export interface WordPressRestRouteInventory { schema: typeof WORDPRESS_REST_ROUTE_INVENTORY_SCHEMA - command: "wordpress.rest-route-inventory" | "wordpress.inventory-rest-routes" + command: "wordpress.rest-route-inventory" status: "ok" | "unsupported" routes: WordPressRestRouteDescriptor[] namespaces: string[] diff --git a/packages/runtime-core/src/wordpress-workload-primitives.ts b/packages/runtime-core/src/wordpress-workload-primitives.ts index 87dc9a39..6b05bbd3 100644 --- a/packages/runtime-core/src/wordpress-workload-primitives.ts +++ b/packages/runtime-core/src/wordpress-workload-primitives.ts @@ -20,6 +20,8 @@ export interface WordPressAbilityStepOptions { export interface WordPressWorkloadRunRecipeOptions { wordpressVersion?: string + /** Local WordPress core source mounted at /wordpress before Playground starts. */ + wordpressDirectory?: string blueprint?: unknown preview?: RuntimePreviewSpec mounts?: WorkspaceRecipeMount[] @@ -80,6 +82,7 @@ export function wordpressWorkloadRunRecipe(options: WordPressWorkloadRunRecipeOp runtime: stripUndefined({ backend: "wordpress-playground", wp: options.wordpressVersion ?? DEFAULT_WORDPRESS_VERSION, + assets: options.wordpressDirectory ? { wordpressDirectory: options.wordpressDirectory } : undefined, blueprint: options.blueprint ?? { steps: [] }, preview: options.preview, stack: Array.isArray(options.runtimeStackMounts) && options.runtimeStackMounts.length > 0 ? { mounts: options.runtimeStackMounts } : undefined, diff --git a/packages/wordpress-plugin/src/class-wp-codebox-fuzz-suite-runner.php b/packages/wordpress-plugin/src/class-wp-codebox-fuzz-suite-runner.php index 83f74bd3..7c50e538 100644 --- a/packages/wordpress-plugin/src/class-wp-codebox-fuzz-suite-runner.php +++ b/packages/wordpress-plugin/src/class-wp-codebox-fuzz-suite-runner.php @@ -442,7 +442,7 @@ private static function execute_fuzz_suite_step( array $step, array $case, array return match ( $command ) { 'wordpress.ensure-plugin-active' => self::execute_fuzz_suite_plugin_activation( $args, $observation, $case_id ), 'wordpress.ensure-external-http-guardrail' => self::execute_fuzz_suite_external_http_guardrail( $args, $observation ), - 'wordpress.inventory-rest-routes', 'wordpress.rest-route-inventory' => self::execute_fuzz_suite_rest_route_inventory( $args, $observation, $case_id ), + 'wordpress.rest-route-inventory' => self::execute_fuzz_suite_rest_route_inventory( $args, $observation, $case_id ), 'wordpress.inventory-database' => self::execute_fuzz_suite_database_inventory( $args, $observation, $case_id ), 'wordpress.admin-page-inventory' => self::execute_fuzz_suite_admin_page_inventory( $args, $observation ), 'wordpress.fuzz-admin-pages' => self::execute_fuzz_suite_admin_page_fuzz( $args, $observation ), @@ -517,7 +517,7 @@ private static function execute_fuzz_suite_rest_route_inventory( array $args, ar $observation['namespaces'] = array_values( array_unique( $namespaces ) ); $observation['payload'] = array( 'schema' => 'wp-codebox/wordpress-rest-route-inventory/v1', - 'command' => 'wordpress.inventory-rest-routes', + 'command' => 'wordpress.rest-route-inventory', 'routes' => $items, 'namespaces' => $observation['namespaces'], ); diff --git a/scripts/php-fuzz-suite-runner-smoke.php b/scripts/php-fuzz-suite-runner-smoke.php index 050598c7..6555268b 100644 --- a/scripts/php-fuzz-suite-runner-smoke.php +++ b/scripts/php-fuzz-suite-runner-smoke.php @@ -397,7 +397,7 @@ class WP_Codebox_Fuzz_Suite_Runner_Smoke { 'id' => 'rest-route-inventory', 'phases' => array( 'action' => array( - array( 'command' => 'wordpress.inventory-rest-routes', 'args' => array( 'namespaces=sample/v1', 'artifact=route_inventory' ) ), + array( 'command' => 'wordpress.rest-route-inventory', 'args' => array( 'namespaces=sample/v1', 'artifact=route_inventory' ) ), ), ), ), diff --git a/tests/agent-task-contracts.test.ts b/tests/agent-task-contracts.test.ts index 657885c7..cb7a7f0f 100644 --- a/tests/agent-task-contracts.test.ts +++ b/tests/agent-task-contracts.test.ts @@ -630,8 +630,7 @@ try { activate: false, pluginFile: "agent-runtime-tool-bridge/agent-runtime-tool-bridge.php", metadata: { source: "agent-task-input" }, - }], - extraPlugins: [{ + }, { source: providerSource, slug: "test-provider", loadAs: "plugin", diff --git a/tests/playground-cli-runner-bootstrap-ini.test.ts b/tests/playground-cli-runner-bootstrap-ini.test.ts index 0f5257bb..1728c165 100644 --- a/tests/playground-cli-runner-bootstrap-ini.test.ts +++ b/tests/playground-cli-runner-bootstrap-ini.test.ts @@ -6,7 +6,7 @@ import { tmpdir } from "node:os" import { startPlaygroundCliServer, type PlaygroundCliModule } from "../packages/runtime-playground/src/playground-cli-runner.js" import type { RuntimeCreateSpec } from "../packages/runtime-core/src/index.js" -const wordpressDirectory = await mkdtemp(join(tmpdir(), "wp-codebox-wordpress-source-")) +const wordpressDevelopDirectory = await mkdtemp(join(tmpdir(), "wp-codebox-wordpress-develop-")) const artifactsDirectory = await mkdtemp(join(tmpdir(), "wp-codebox-artifacts-")) const calls: Parameters[0][] = [] @@ -32,7 +32,7 @@ try { version: "mounted-wordpress-source", phpVersion: "8.4", wordpressInstallMode: "do-not-attempt-installing", - assets: { wordpressDirectory }, + assets: { wordpressDirectory: wordpressDevelopDirectory }, extensions: [{ manifest: "/tmp/sodium/manifest.json" }], blueprint: {}, }, @@ -64,7 +64,8 @@ try { assert.equal(calls.length, 1) assert.equal(calls[0]["mount-before-install"]?.length, 2) assert.equal(calls[0]["mount-before-install"]?.[0]?.vfsPath, "/internal/shared") - assert.deepEqual(calls[0]["mount-before-install"]?.[1], { hostPath: wordpressDirectory, vfsPath: "/wordpress" }) + // A wordpress-develop checkout is the runtime root, not an ordinary post-startup mount. + assert.deepEqual(calls[0]["mount-before-install"]?.[1], { hostPath: wordpressDevelopDirectory, vfsPath: "/wordpress" }) assert.deepEqual(calls[0].mount, []) assert.equal(calls[0].workers, 6) assert.equal(calls[0].wordpressInstallMode, "do-not-attempt-installing") @@ -121,7 +122,7 @@ try { assert.match(distributionAutoPrepend, /define\("WPCOM_IS_BRANCH_PREVIEW", true\)/) assert.match(distributionAutoPrepend, /define\("WPCOM_BRANCH_ID", 123\)/) } finally { - await rm(wordpressDirectory, { recursive: true, force: true }) + await rm(wordpressDevelopDirectory, { recursive: true, force: true }) await rm(artifactsDirectory, { recursive: true, force: true }) } diff --git a/tests/public-fuzz-workload-cli.test.ts b/tests/public-fuzz-workload-cli.test.ts index 67fd78bd..2278411b 100644 --- a/tests/public-fuzz-workload-cli.test.ts +++ b/tests/public-fuzz-workload-cli.test.ts @@ -40,6 +40,8 @@ return static function ( array $input, array $args ): array { ); }; `, "utf8") + const wordpressDevelopSource = join(directory, "wordpress-develop") + await mkdir(wordpressDevelopSource) const descriptorOutput = await captureStdout(async () => { assert.equal(await runCli(["fuzz", "descriptor", "--format=json"]), 0) @@ -149,7 +151,7 @@ return static function ( array $input, array $args ): array { id: "public-cli-runtime-command-suite", cases: [{ id: "rest-route-inventory", - target: { kind: "runtime", id: "wordpress.inventory-rest-routes", entrypoint: "wordpress.inventory-rest-routes" }, + target: { kind: "runtime", id: "wordpress.rest-route-inventory", entrypoint: "wordpress.rest-route-inventory" }, input: { args: [] }, }], }), "utf8") @@ -163,7 +165,33 @@ return static function ( array $input, array $args ): array { assert.notEqual(runtimeCommandFuzzJson.cases[0].skipReason, "fuzz_suite_executor_unavailable") assert.equal(runtimeCommandFuzzJson.cases[0].metadata.adapter.adapterKind, "runtime") assert.equal(runtimeCommandFuzzJson.cases[0].metadata.execution.result.json.schema, "wp-codebox/recipe-run-dry-run/v1") - assert.equal(runtimeCommandFuzzJson.cases[0].metadata.execution.result.json.plan.workflow.steps[0].command, "wordpress.inventory-rest-routes") + assert.equal(runtimeCommandFuzzJson.cases[0].metadata.execution.result.json.plan.workflow.steps[0].command, "wordpress.rest-route-inventory") + + const coreRuntimeFuzzInput = join(directory, "core-runtime-fuzz.json") + await writeFile(coreRuntimeFuzzInput, JSON.stringify({ + schema: "wp-codebox/fuzz-suite/v1", + id: "public-cli-core-runtime-suite", + metadata: { + runtime_requirements: { + wordpress_directory: wordpressDevelopSource, + extra_plugins: [{ slug: "sample-plugin", source: samplePluginSource, loadAs: "plugin", activate: true }], + }, + }, + cases: [{ + id: "core-phpunit", + target: { kind: "runtime", id: "wordpress.core-phpunit", entrypoint: "wordpress.core-phpunit" }, + input: { args: ["test-root=/wordpress/tests/phpunit"] }, + }], + }), "utf8") + const coreRuntimeFuzzOutput = await captureStdout(async () => { + assert.equal(await runCli(["run-fuzz-suite", "--input-file", coreRuntimeFuzzInput, "--format=json", "--dry-run"]), 0) + }) + const coreRuntimeFuzzJson = JSON.parse(coreRuntimeFuzzOutput) + const coreRuntimePlan = coreRuntimeFuzzJson.cases[0].metadata.execution.result.json.plan + assert.equal(coreRuntimePlan.runtime.assets.wordpressDirectory, wordpressDevelopSource) + assert.equal(coreRuntimePlan.runtime.stack, undefined) + assert.deepEqual(coreRuntimePlan.extra_plugins.map((plugin: { slug: string }) => plugin.slug), ["sample-plugin"]) + assert.equal(coreRuntimePlan.workflow.steps[0].command, "wordpress.core-phpunit") const phpRuntimeFuzzInput = join(directory, "runtime-php-workload-fuzz.json") await writeFile(phpRuntimeFuzzInput, JSON.stringify({ diff --git a/tests/wordpress-runtime-discovery-contracts.test.ts b/tests/wordpress-runtime-discovery-contracts.test.ts index c137b4d5..b3a927af 100644 --- a/tests/wordpress-runtime-discovery-contracts.test.ts +++ b/tests/wordpress-runtime-discovery-contracts.test.ts @@ -146,7 +146,6 @@ assert.equal(executionSurfaces.surfaces[0]?.invocation.resultSchema, WORDPRESS_E const inventoryDefinitions = [ ["wordpress.rest-route-inventory", "runRestRouteInventory", WORDPRESS_REST_ROUTE_INVENTORY_SCHEMA], - ["wordpress.inventory-rest-routes", "runRestRouteInventory", WORDPRESS_REST_ROUTE_INVENTORY_SCHEMA], ["wordpress.admin-page-inventory", "runAdminPageInventory", WORDPRESS_ADMIN_PAGE_INVENTORY_SCHEMA], ["wordpress.admin-action-inventory", "runAdminActionInventory", WORDPRESS_ADMIN_ACTION_INVENTORY_SCHEMA], ["wordpress.inventory-database", "runDatabaseInventory", WORDPRESS_DATABASE_INVENTORY_SCHEMA], @@ -234,7 +233,7 @@ assert.deepEqual(route?.endpoints?.[0]?.args[1]?.enum, ["view", "edit"]) assert.equal(route?.endpoints?.[0]?.args[1]?.defaultPresent, true) assert.deepEqual(route?.schema?.properties, ["id", "name"]) -const inventoryPhp = runtimeInventoryPhpCode("rest", "wordpress.inventory-rest-routes", WORDPRESS_REST_ROUTE_INVENTORY_SCHEMA).replace(/^<\?php\n/, "") +const inventoryPhp = runtimeInventoryPhpCode("rest", "wordpress.rest-route-inventory", WORDPRESS_REST_ROUTE_INVENTORY_SCHEMA).replace(/^<\?php\n/, "") const inventoryDiscovered = await runPhpJson(` function wp_strip_all_tags( $text ) { return strip_tags( $text ); } function wp_json_encode( $data, $flags = 0 ) { return json_encode( $data, $flags ); } @@ -256,7 +255,7 @@ function rest_get_server() { ${inventoryPhp} `) -assert.equal(inventoryDiscovered.command, "wordpress.inventory-rest-routes") +assert.equal(inventoryDiscovered.command, "wordpress.rest-route-inventory") assert.equal(inventoryDiscovered.schema, WORDPRESS_REST_ROUTE_INVENTORY_SCHEMA) assert.equal(inventoryDiscovered.routes[0]?.route, "/demo/v1/items")