Skip to content

[tests] extism Compile() error paths need adapter-level mocking for full coverage #108

@robbyt

Description

@robbyt

Summary

engines/extism/compiler/compiler.go:54-56 carries a stale TODO:

// Compile implements script.Compiler
// TODO: Some error paths are difficult to test with the current design
// Consider adding integration tests for hard-to-reach error cases.

The TODO is being removed under #99, but the underlying gap deserves a real issue. Coverage for the package is currently ~93%; the remaining branches in Compile() aren't testable today because real Extism SDK objects are constructed inside compile.CompileBytes() and exposed only through concrete types.

Specific untested branches in Compile()

File:line Branch Why it's hard to test today
compiler.go:63-66 io.ReadAll(scriptReader) returning a non-EOF error Easy fix — a custom io.ReadCloser whose Read() returns an error will exercise it.
compiler.go:68-71 scriptReader.Close() returning an error Same approach as above.
compiler.go:91-94 plugin.Instance(c.ctx, PluginInstanceConfig{}) failing Hard. plugin is a real *extismSDK.CompiledPlugin returned by compile.CompileBytes; there's no adapter boundary to substitute a failing instance constructor.
compiler.go:96-98 instance.Close(ctx) returning an error Logged-only, doesn't propagate, so coverage value is low.
compiler.go:103-108 instance.FunctionExists(funcName) returning false Partially covered by TestCompile_MissingFunction but only via a real WASM module that lacks the function — the negative case isn't exercised at the unit level.
compiler.go:113-115 NewExecutable(...) returning nil Currently unreachable: the constructor never returns nil. Either the guard is dead code or NewExecutable is intended to grow nil-returning paths.

Proposal

Extend the existing engines/extism/adapters interface set so compile.CompileBytes returns an adapters.CompiledPlugin rather than a concrete *extismSDK.CompiledPlugin. The Compiler would then accept a plugin-factory option (defaulting to the real SDK call) so unit tests could inject a mock plugin whose Instance() errors, whose FunctionExists() returns false, etc.

Concretely:

  1. Make compile.CompileBytes return adapters.CompiledPlugin (the interface already exists in engines/extism/adapters/interfaces.go).
  2. Add an internal seam on Compiler (e.g. compileFn func(ctx, []byte, *Settings) (adapters.CompiledPlugin, error)) so tests can override.
  3. Add a mock CompiledPlugin in engines/extism/compiler/mocks_test.go and write the missing branch tests.

Out of scope

  • Production behavior — this is a coverage / testability change.
  • The exit-code-vs-output question is tracked separately under [bug] Extism drops WASM output on non-zero exit code #94.
  • Doesn't replace the legitimate end-to-end wasmdata.TestModule-based tests; this just pulls the unit-level branches under test.

Files

  • engines/extism/compiler/compiler.go — function under test
  • engines/extism/compiler/internal/compile/compile.go — return type adjustment
  • engines/extism/adapters/interfaces.go — confirm CompiledPlugin covers the surface needed
  • engines/extism/compiler/compiler_test.go — new branch tests
  • New: engines/extism/compiler/mocks_test.go (or extend existing test scaffolding)

This was previously tracked inline as a TODO at compiler.go:54-56. Filed in response to issue #99's request to either resolve TODOs or convert them to issues.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions