Skip to content

feat(cli): gonext plugin test — in-process fake host + conformance suite#461

Merged
tayebmokni merged 3 commits into
mainfrom
feat/plugin-test-cli
May 25, 2026
Merged

feat(cli): gonext plugin test — in-process fake host + conformance suite#461
tayebmokni merged 3 commits into
mainfrom
feat/plugin-test-cli

Conversation

@tayebmokni
Copy link
Copy Markdown
Contributor

Closes #247.

Adds an offline conformance harness so plugin authors can test their
plugins against an in-memory fake host without standing up Postgres /
Redis / the wazero runtime.

What ships

  • packages/go/plugins/fakehost/ — in-memory implementations of every
    host ABI (gn_db_, gn_kv_, gn_cache_invalidate, gn_http_fetch,
    gn_media_read, gn_users_read, gn_secrets_get, gn_audit_emit,
    gn_cron_register, gn_log, gn_time_ms, gn_i18n_translate,
    gn_metric_observe, gn_event_emit, gn_span_event, gn_panic,
    http.serve). Every call is recorded as a typed Event with a
    deterministic clock the test can advance. Capability denial, KV
    quotas, scripted HTTP responses, and a secrets bag that never echoes
    values into the trace are all wired.

  • packages/go/plugins/conformance/ — scenario runner driving the
    fake host. Built-in scenarios assert: declared capabilities are in
    the v1 vocabulary (plus legacy hooks.subscribe/jobs.enqueue),
    capabilities cover the manifest's hook registrations
    (save_post/publish_post need posts.write), hook names are
    non-empty, jobs are namespaced under the plugin slug,
    init+teardown is idempotent, and a synthetic 1s budget is
    respected. Parses both the v1 manifest schema and the legacy
    apiVersion-form (so the existing examples/plugins/seo bundle
    passes without modification).

  • cli/gonext/cmd/plugin/test.go — extended with --suite=conformance
    and --record-fixtures=DIR. The default suite (no flag, or
    --suite=default) continues to run the existing plugintest
    contract checks unchanged — marketplace ingest stays on the stable
    shape.

  • tests/conformance/fixtures/ — README + a seo_init.json sample
    fixture demonstrating the JSON format. The runner's
    LoadFixtureScenarios helper picks *.json files up
    automatically; round-tripping --record-fixtures → load is
    covered by tests.

Tests

  • Unit tests for every fake-host method (KV round-trip, quota,
    capability gating, secret-trace hiding, scripted HTTP, concurrent
    safety).
  • Unit tests for every conformance scenario.
  • Integration: gonext plugin test --suite=conformance examples/plugins/seo runs end-to-end and reports
    conformance PASS — 6 scenarios (0 failed, 1 skipped). (The one
    skip is limits.budget, which reports reason wasm-not-wired — it
    upgrades to a real meter check when the runtime is wired to
    conformance in a follow-up.)
  • TestRunTest_Conformance_SEOExample smoke-tests the end-to-end
    path against examples/plugins/seo from the CLI test suite.

Out of scope

  • Wiring the conformance scenarios into the actual wazero runtime
    (driving a plugin's WASM init from the conformance runner). The
    conformance package is structured so this drops in as the body of
    driveSyntheticInit once the runtime integration lands.
  • YAML fixture format — JSON is what --record-fixtures emits and
    what authors paste back; YAML can be added in a follow-up if
    demand materialises.

Run locally:

gonext plugin test --suite=conformance examples/plugins/seo
gonext plugin test --suite=conformance --json examples/plugins/seo
gonext plugin test --suite=conformance --record-fixtures=tests/conformance/fixtures examples/plugins/seo

🤖 Generated with Claude Code

tib0o0o and others added 3 commits May 25, 2026 22:07
…ests

Adds a new packages/go/plugins/fakehost package that mirrors each host
ABI (gn_db_*, gn_kv_*, gn_cache_invalidate, gn_http_fetch,
gn_media_read, gn_users_read, gn_secrets_get, gn_audit_emit,
gn_cron_register, gn_log, gn_time_ms, gn_i18n_translate,
gn_metric_observe, gn_event_emit, gn_span_event, gn_panic, http.serve).

Every call is recorded as a typed Event so the upcoming
`gonext plugin test --suite=conformance` (issue #247) can assert what
the plugin actually did.  Includes deterministic clock, capability
denial gating, KV quotas, scripted HTTP responses, and a secrets bag
that deliberately never echoes values into the recorded trace.

Refs #247.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Claude (for tayebmokni) <tayeb.mokni@gmail.com>
Adds packages/go/plugins/conformance implementing the suite invoked by
`gonext plugin test --suite=conformance`.

Each Scenario receives a parsed manifest + a fresh fakehost.Host and
returns a ScenarioResult.  The built-ins assert: capabilities are in
the v1 vocabulary, declared capabilities cover the plugin's
registered hooks (statically — dynamic dispatch awaits the WASM
runner), hook names are non-empty, jobs are namespaced under the
plugin slug, init+teardown is idempotent, and the synthetic 1s job
respects its budget.

The runner accepts both directory bundles and .gnplugin/.zip
archives, parses v1 and legacy manifests, recovers panics, and
optionally records per-scenario JSON fixtures.  A
LoadFixtureScenarios helper round-trips user-supplied fixtures from
tests/conformance/fixtures.  Smoke-tested against
examples/plugins/seo's manifest.

Refs #247.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
Wires the conformance suite (issue #247) into the existing
`gonext plugin test` subcommand:

  gonext plugin test --suite=conformance [--json] [--record-fixtures=DIR] <bundle>

The default suite (no --suite or --suite=default) continues to run the
plugintest contract checks unchanged — marketplace ingest stays on the
stable shape.  --suite=conformance switches to
packages/go/plugins/conformance.NewSuite, which parses the manifest
(v1 OR legacy), runs the built-in scenarios against an in-memory
fakehost.Host, and optionally writes one JSON fixture per scenario via
--record-fixtures.

Includes a tests/conformance/fixtures/README + a seo_init.json
template showing the fixture format.  Smoke-tested end-to-end against
examples/plugins/seo (TestRunTest_Conformance_SEOExample passes
green).

Refs #247.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
@tayebmokni tayebmokni enabled auto-merge (squash) May 25, 2026 20:17
@tayebmokni tayebmokni merged commit b5d6206 into main May 25, 2026
11 of 15 checks passed
@tayebmokni tayebmokni deleted the feat/plugin-test-cli branch May 25, 2026 20:23
tayebmokni pushed a commit that referenced this pull request May 25, 2026
Closes #242.

packages/ts/sdk-plugin: TypeScript SDK for GoNext plugin authors via
Javy (JavaScript → WASM).

- pluginInit + registerAction / registerFilter
- Typed wrappers over every gn_* host ABI through globalThis
- JSON envelope codec
- Manifest builder mirroring the Go side
- bin/gonext-sdk-build.js wraps esbuild + javy compile
- cli/gonext/templates/typescript + 'gonext plugin init --template=typescript'
- examples/plugins/sdk-ts-hello working example
- 54 Vitest tests (codec + manifest + dispatch)

This PR was rebased onto main after #460/#461/#462 landed; the
runInit dispatch now switches on template name and embeds both
templates/go and templates/typescript via the single templatesFS.

Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
tayebmokni pushed a commit that referenced this pull request May 25, 2026
Closes #239.

packages/rust/gonext-sdk + packages/rust/gonext-sdk-macros:
Cargo crates for Rust plugin authors.

- plugin_init! proc-macro generates gn_alloc/gn_free/_start/gn_handle_hook exports
- Safe typed wrappers over every gn_* host ABI (env, env_net, gonext_data, env_platform)
- JSON envelope codec via serde_json
- Manifest builder mirroring the Go side
- cli/gonext/cmd/plugin/templates/rust + 'gonext plugin init --template=rust'
- examples/plugins/sdk-rust-hello: working example (action + filter + kv.set + audit.emit)

Rebased onto main after #460/#461/#462 landed; init.go now switches
on template name. TestRunInitUnknownTemplate updated to use a
clearly-not-real template name so it stays robust as new templates
land.

Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
tayebmokni pushed a commit that referenced this pull request May 25, 2026
Closes #242.

packages/ts/sdk-plugin: TypeScript SDK for GoNext plugin authors via
Javy (JavaScript → WASM).

- pluginInit + registerAction / registerFilter
- Typed wrappers over every gn_* host ABI through globalThis
- JSON envelope codec
- Manifest builder mirroring the Go side
- bin/gonext-sdk-build.js wraps esbuild + javy compile
- cli/gonext/templates/typescript + 'gonext plugin init --template=typescript'
- examples/plugins/sdk-ts-hello working example
- 54 Vitest tests (codec + manifest + dispatch)

This PR was rebased onto main after #460/#461/#462 landed; the
runInit dispatch now switches on template name and embeds both
templates/go and templates/typescript via the single templatesFS.

Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
tayebmokni pushed a commit that referenced this pull request May 26, 2026
Closes #242.

packages/ts/sdk-plugin: TypeScript SDK for GoNext plugin authors via
Javy (JavaScript → WASM).

- pluginInit + registerAction / registerFilter
- Typed wrappers over every gn_* host ABI through globalThis
- JSON envelope codec
- Manifest builder mirroring the Go side
- bin/gonext-sdk-build.js wraps esbuild + javy compile
- cli/gonext/templates/typescript + 'gonext plugin init --template=typescript'
- examples/plugins/sdk-ts-hello working example
- 54 Vitest tests (codec + manifest + dispatch)

This PR was rebased onto main after #460/#461/#462 landed; the
runInit dispatch now switches on template name and embeds both
templates/go and templates/typescript via the single templatesFS.

Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI: gonext plugin test (in-process fake host + contract conformance suite)

2 participants