From e6ae846b3f978499ba4f73e0de3a5c7893fb2bda Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 May 2026 21:54:41 +0000 Subject: [PATCH 1/2] refactor: propagate ctx into the four public constructors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finishes the work started in #144. The four public consumer constructors now take ctx as the first argument: - polyscript.New[E](ctx, src, opts...) - extism.FromExtismLoader(ctx, ldr, opts...) - risor.FromRisorLoader(ctx, ldr, opts...) - starlark.FromStarlarkLoader(ctx, ldr, opts...) The three inline context.Background() injections in engines/*/new.go go away; cancellation now actually reaches the loader's I/O and the compiler's parse path. deprecated.go keeps its twelve legacy FromXxx* signatures unchanged and injects context.Background() inline at each downstream call. They are slated for removal in #104; churning their signatures during the deprecation window adds caller pain without value. New cancellation tests: - engines/risor/new_test.go: TestFromRisorLoader_CompileCancelled — pre-cancelled ctx aborts the Risor parser - engines/extism/new_test.go: TestFromExtismLoader_LoaderCancelled — pre-cancelled ctx aborts the loader's GetReader (the wazero compile of the small embedded test module completes too fast to demonstrate parser-level cancellation) - polyscript_test.go: TestNew_CompileCancelled (top-level Risor cancel) and TestNew_HTTPLoaderCancelled (end-to-end: slow httptest server, ctx deadline, verify the fetch aborts near the deadline rather than waiting for the full server stall) Closes #145. https://claude.ai/code/session_01C61VEAmjxSnX5Xhbab8NvL --- CHANGELOG.md | 4 + deprecated.go | 25 +++--- engines/extism/new.go | 10 +-- engines/extism/new_test.go | 50 ++++++++--- engines/integration_test.go | 26 +++--- engines/risor/new.go | 11 +-- engines/risor/new_test.go | 41 ++++++--- engines/starlark/new.go | 10 +-- engines/starlark/new_test.go | 26 +++--- examples/data-prep/extism/main.go | 1 + examples/data-prep/risor/main.go | 1 + examples/data-prep/starlark/main.go | 1 + .../multiple-instantiation/extism/main.go | 1 + examples/multiple-instantiation/risor/main.go | 1 + .../multiple-instantiation/starlark/main.go | 1 + examples/simple/extism/main.go | 1 + examples/simple/risor/main.go | 1 + examples/simple/starlark/main.go | 1 + polyscript.go | 28 +++--- polyscript_extism_test.go | 9 ++ polyscript_options_test.go | 33 ++++--- polyscript_test.go | 89 +++++++++++++++---- readme_test.go | 7 +- 23 files changed, 258 insertions(+), 120 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2edec06..f71b839 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `context.Context` as the first argument. `NewExecutableUnit` gains a leading `ctx` too. The Extism compiler's `WithContext` option and the FromHTTP loader's `GetReaderWithContext` helper are removed (now redundant). +- **BREAKING**: `polyscript.New[E]`, `extism.FromExtismLoader`, + `risor.FromRisorLoader`, and `starlark.FromStarlarkLoader` now take a + `context.Context` as the first argument. Cancelling that ctx reaches the + loader's I/O and (where the parser supports it) the compile path. ### Deprecated - The twelve legacy top-level constructors (`FromRisorFile`, diff --git a/deprecated.go b/deprecated.go index 9cb7ffb..8838db6 100644 --- a/deprecated.go +++ b/deprecated.go @@ -7,6 +7,7 @@ package polyscript import ( + "context" "log/slog" extismMachine "github.com/robbyt/go-polyscript/engines/extism" @@ -35,7 +36,7 @@ func FromExtismFile( return nil, err } - return extismMachine.FromExtismLoader(l, + return extismMachine.FromExtismLoader(context.Background(), l, extismMachine.WithLogHandler(logHandler), extismMachine.WithEntryPoint(entryPoint), ) @@ -57,7 +58,7 @@ func FromExtismFileWithData( return nil, err } - return extismMachine.FromExtismLoader(l, + return extismMachine.FromExtismLoader(context.Background(), l, extismMachine.WithLogHandler(logHandler), extismMachine.WithStaticData(staticData), extismMachine.WithEntryPoint(entryPoint), @@ -77,7 +78,7 @@ func FromExtismBytes( return nil, err } - return extismMachine.FromExtismLoader(l, + return extismMachine.FromExtismLoader(context.Background(), l, extismMachine.WithLogHandler(logHandler), extismMachine.WithEntryPoint(entryPoint), ) @@ -99,7 +100,7 @@ func FromExtismBytesWithData( return nil, err } - return extismMachine.FromExtismLoader(l, + return extismMachine.FromExtismLoader(context.Background(), l, extismMachine.WithLogHandler(logHandler), extismMachine.WithStaticData(staticData), extismMachine.WithEntryPoint(entryPoint), @@ -118,7 +119,7 @@ func FromRisorFile( return nil, err } - return risorMachine.FromRisorLoader(l, risorMachine.WithLogHandler(logHandler)) + return risorMachine.FromRisorLoader(context.Background(), l, risorMachine.WithLogHandler(logHandler)) } // FromRisorFileWithData creates a Risor evaluator with both static and dynamic @@ -135,7 +136,7 @@ func FromRisorFileWithData( return nil, err } - return risorMachine.FromRisorLoader(l, + return risorMachine.FromRisorLoader(context.Background(), l, risorMachine.WithLogHandler(logHandler), risorMachine.WithStaticData(staticData), ) @@ -153,7 +154,7 @@ func FromRisorString( return nil, err } - return risorMachine.FromRisorLoader(l, risorMachine.WithLogHandler(logHandler)) + return risorMachine.FromRisorLoader(context.Background(), l, risorMachine.WithLogHandler(logHandler)) } // FromRisorStringWithData creates a Risor evaluator with both static and @@ -170,7 +171,7 @@ func FromRisorStringWithData( return nil, err } - return risorMachine.FromRisorLoader(l, + return risorMachine.FromRisorLoader(context.Background(), l, risorMachine.WithLogHandler(logHandler), risorMachine.WithStaticData(staticData), ) @@ -188,7 +189,7 @@ func FromStarlarkFile( return nil, err } - return starlarkMachine.FromStarlarkLoader(l, starlarkMachine.WithLogHandler(logHandler)) + return starlarkMachine.FromStarlarkLoader(context.Background(), l, starlarkMachine.WithLogHandler(logHandler)) } // FromStarlarkFileWithData creates a Starlark evaluator with both static and @@ -205,7 +206,7 @@ func FromStarlarkFileWithData( return nil, err } - return starlarkMachine.FromStarlarkLoader(l, + return starlarkMachine.FromStarlarkLoader(context.Background(), l, starlarkMachine.WithLogHandler(logHandler), starlarkMachine.WithStaticData(staticData), ) @@ -223,7 +224,7 @@ func FromStarlarkString( return nil, err } - return starlarkMachine.FromStarlarkLoader(l, starlarkMachine.WithLogHandler(logHandler)) + return starlarkMachine.FromStarlarkLoader(context.Background(), l, starlarkMachine.WithLogHandler(logHandler)) } // FromStarlarkStringWithData creates a Starlark evaluator with both static and @@ -240,7 +241,7 @@ func FromStarlarkStringWithData( return nil, err } - return starlarkMachine.FromStarlarkLoader(l, + return starlarkMachine.FromStarlarkLoader(context.Background(), l, starlarkMachine.WithLogHandler(logHandler), starlarkMachine.WithStaticData(staticData), ) diff --git a/engines/extism/new.go b/engines/extism/new.go index 2ddbc13..2be15d3 100644 --- a/engines/extism/new.go +++ b/engines/extism/new.go @@ -69,7 +69,10 @@ var ErrEntryPointRequired = errors.New("extism: entry point is required (use Wit // If both [WithStaticData] and [WithDataProvider] are supplied, // [WithDataProvider] takes precedence — pass exactly one of them per // call to keep intent unambiguous. -func FromExtismLoader(ldr loader.Loader, opts ...Option) (*evaluator.Evaluator, error) { +// +// The supplied ctx flows into the loader's I/O and the Extism SDK's +// WASM compile + entry-point probe; cancelling it halts both. +func FromExtismLoader(ctx context.Context, ldr loader.Loader, opts ...Option) (*evaluator.Evaluator, error) { cfg := &config{} for _, opt := range opts { if opt != nil { @@ -96,10 +99,7 @@ func FromExtismLoader(ldr loader.Loader, opts ...Option) (*evaluator.Evaluator, execUnitID = u.String() } - // FromExtismLoader is a one-shot startup constructor; compile uses a - // fresh Background context. Callers needing cancellable compile - // should drive script.NewExecutableUnit directly. - execUnit, err := script.NewExecutableUnit(context.Background(), cfg.handler, execUnitID, ldr, comp, provider) + execUnit, err := script.NewExecutableUnit(ctx, cfg.handler, execUnitID, ldr, comp, provider) if err != nil { return nil, err } diff --git a/engines/extism/new_test.go b/engines/extism/new_test.go index eec26e3..f9b1150 100644 --- a/engines/extism/new_test.go +++ b/engines/extism/new_test.go @@ -2,6 +2,7 @@ package extism import ( "bytes" + "context" "errors" "fmt" "io" @@ -42,7 +43,7 @@ func newErrorLoader(t *testing.T, msg string) *loaderMock { func TestFromExtismLoader_RequiresEntryPoint(t *testing.T) { mockLoader := new(loaderMock) - eval, err := FromExtismLoader(mockLoader) + eval, err := FromExtismLoader(t.Context(), mockLoader) require.Error(t, err) require.Nil(t, eval) require.ErrorIs(t, err, ErrEntryPointRequired) @@ -53,7 +54,7 @@ func TestFromExtismLoader_RequiresEntryPoint(t *testing.T) { func TestFromExtismLoader_EmptyEntryPointStillRejected(t *testing.T) { mockLoader := new(loaderMock) - eval, err := FromExtismLoader(mockLoader, WithEntryPoint("")) + eval, err := FromExtismLoader(t.Context(), mockLoader, WithEntryPoint("")) require.Error(t, err) require.Nil(t, eval) require.ErrorIs(t, err, ErrEntryPointRequired) @@ -61,7 +62,7 @@ func TestFromExtismLoader_EmptyEntryPointStillRejected(t *testing.T) { func TestFromExtismLoader_NoOptionsBeyondEntryPoint(t *testing.T) { mockLoader := newWASMLoader(t) - eval, err := FromExtismLoader(mockLoader, WithEntryPoint(wasmdata.EntrypointGreet)) + eval, err := FromExtismLoader(t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet)) require.NoError(t, err) require.NotNil(t, eval) assert.Equal(t, "extism.Evaluator", eval.String()) @@ -71,7 +72,7 @@ func TestFromExtismLoader_NoOptionsBeyondEntryPoint(t *testing.T) { func TestFromExtismLoader_WithLogHandler(t *testing.T) { mockLoader := newWASMLoader(t) handler := slog.NewTextHandler(os.Stdout, nil) - eval, err := FromExtismLoader(mockLoader, + eval, err := FromExtismLoader(t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithLogHandler(handler), ) @@ -82,7 +83,7 @@ func TestFromExtismLoader_WithLogHandler(t *testing.T) { func TestFromExtismLoader_NilLogHandler(t *testing.T) { mockLoader := newWASMLoader(t) - eval, err := FromExtismLoader(mockLoader, + eval, err := FromExtismLoader(t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithLogHandler(nil), ) @@ -93,7 +94,7 @@ func TestFromExtismLoader_NilLogHandler(t *testing.T) { func TestFromExtismLoader_WithStaticData(t *testing.T) { mockLoader := newWASMLoader(t) - eval, err := FromExtismLoader(mockLoader, + eval, err := FromExtismLoader(t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithStaticData(map[string]any{"input": "World"}), ) @@ -105,7 +106,7 @@ func TestFromExtismLoader_WithStaticData(t *testing.T) { func TestFromExtismLoader_WithDataProvider(t *testing.T) { mockLoader := newWASMLoader(t) provider := data.NewContextProvider("test_key") - eval, err := FromExtismLoader(mockLoader, + eval, err := FromExtismLoader(t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithDataProvider(provider), ) @@ -117,7 +118,7 @@ func TestFromExtismLoader_WithDataProvider(t *testing.T) { func TestFromExtismLoader_DataProviderBeatsStaticData(t *testing.T) { mockLoader := newWASMLoader(t) provider := data.NewContextProvider("sentinel") - eval, err := FromExtismLoader(mockLoader, + eval, err := FromExtismLoader(t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithStaticData(map[string]any{"ignored": true}), WithDataProvider(provider), @@ -130,7 +131,7 @@ func TestFromExtismLoader_DataProviderBeatsStaticData(t *testing.T) { func TestFromExtismLoader_NilOption(t *testing.T) { mockLoader := newWASMLoader(t) var nilOpt Option - eval, err := FromExtismLoader(mockLoader, + eval, err := FromExtismLoader(t.Context(), mockLoader, nilOpt, WithEntryPoint(wasmdata.EntrypointGreet), ) @@ -140,7 +141,7 @@ func TestFromExtismLoader_NilOption(t *testing.T) { func TestFromExtismLoader_LoaderError(t *testing.T) { mockLoader := newErrorLoader(t, "failed to load WASM") - eval, err := FromExtismLoader(mockLoader, WithEntryPoint(wasmdata.EntrypointGreet)) + eval, err := FromExtismLoader(t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet)) require.Error(t, err) require.Nil(t, eval) mockLoader.AssertExpectations(t) @@ -151,7 +152,7 @@ func TestFromExtismLoader_NilSourceURL(t *testing.T) { mockLoader.On("GetSourceURL").Return(nil) mockLoader.On("GetReader", mock.Anything).Return(io.NopCloser(bytes.NewReader(wasmdata.TestModule)), nil) - eval, err := FromExtismLoader(mockLoader, WithEntryPoint(wasmdata.EntrypointGreet)) + eval, err := FromExtismLoader(t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet)) require.NoError(t, err) require.NotNil(t, eval) mockLoader.AssertExpectations(t) @@ -166,7 +167,7 @@ func TestFromExtismLoader_DiskLoader(t *testing.T) { require.NoError(t, err) require.NotNil(t, diskLoader) - eval, err := FromExtismLoader(diskLoader, WithEntryPoint(wasmdata.EntrypointGreet)) + eval, err := FromExtismLoader(t.Context(), diskLoader, WithEntryPoint(wasmdata.EntrypointGreet)) require.NoError(t, err) require.NotNil(t, eval) assert.Equal(t, "extism.Evaluator", eval.String()) @@ -176,7 +177,7 @@ func TestFromExtismLoader_RunsEndToEnd(t *testing.T) { scriptLoader, err := loader.NewFromBytes(wasmdata.TestModule) require.NoError(t, err) - eval, err := FromExtismLoader( + eval, err := FromExtismLoader(t.Context(), scriptLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithStaticData(map[string]any{"input": "World"}), @@ -203,7 +204,7 @@ func TestFromExtismLoader_DefaultsToSlogDefault(t *testing.T) { scriptLoader, err := loader.NewFromBytes(wasmdata.TestModule) require.NoError(t, err) - eval, err := FromExtismLoader(scriptLoader, WithEntryPoint(wasmdata.EntrypointGreet)) + eval, err := FromExtismLoader(t.Context(), scriptLoader, WithEntryPoint(wasmdata.EntrypointGreet)) require.NoError(t, err) require.NotNil(t, eval) } @@ -224,3 +225,24 @@ func TestNewCompiler(t *testing.T) { require.NotNil(t, comp) }) } + +// TestFromExtismLoader_LoaderCancelled verifies that ctx cancellation +// during the loader IO phase aborts the constructor. This is the +// headline behavior of #145 for engines whose WASM compile is too fast +// to reliably observe a pre-cancelled ctx (the wazero compile of the +// small embedded test module completes in microseconds). +func TestFromExtismLoader_LoaderCancelled(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(t.Context()) + cancel() // pre-cancel; the loader's GetReader call observes it. + + mockLoader := new(loaderMock) + mockLoader.On("GetSourceURL").Return((*url.URL)(nil)) + mockLoader.On("GetReader", mock.Anything).Return(nil, ctx.Err()) + + eval, err := FromExtismLoader(ctx, mockLoader, WithEntryPoint(wasmdata.EntrypointGreet)) + require.Error(t, err) + require.Nil(t, eval) + require.ErrorIs(t, err, context.Canceled) +} diff --git a/engines/integration_test.go b/engines/integration_test.go index e04ecb1..528c0ab 100644 --- a/engines/integration_test.go +++ b/engines/integration_test.go @@ -452,7 +452,7 @@ let user_data = ctx["user_data"] ctxProvider := data.NewContextProvider(constants.EvalData) scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - evaluator, err := risorEngine.FromRisorLoader(scriptLoader, + evaluator, err := risorEngine.FromRisorLoader(t.Context(), scriptLoader, risorEngine.WithLogHandler(handler), risorEngine.WithDataProvider(ctxProvider)) require.NoError(t, err) @@ -510,7 +510,7 @@ _ = result ctxProvider := data.NewContextProvider(constants.EvalData) scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - evaluator, err := starlarkEngine.FromStarlarkLoader(scriptLoader, + evaluator, err := starlarkEngine.FromStarlarkLoader(t.Context(), scriptLoader, starlarkEngine.WithLogHandler(handler), starlarkEngine.WithDataProvider(ctxProvider)) require.NoError(t, err) @@ -554,7 +554,7 @@ _ = result ctxProvider := data.NewContextProvider(constants.EvalData) scriptLoader, err := loader.NewFromBytes(wasmdata.TestModule) require.NoError(t, err) - evaluator, err := extismEngine.FromExtismLoader(scriptLoader, + evaluator, err := extismEngine.FromExtismLoader(t.Context(), scriptLoader, extismEngine.WithLogHandler(handler), extismEngine.WithDataProvider(ctxProvider), extismEngine.WithEntryPoint(wasmdata.EntrypointGreet), @@ -614,7 +614,7 @@ let max_retries = ctx["constants"]["max_retries"] staticProvider := data.NewStaticProvider(staticData) scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - evaluator, err := risorEngine.FromRisorLoader(scriptLoader, + evaluator, err := risorEngine.FromRisorLoader(t.Context(), scriptLoader, risorEngine.WithLogHandler(handler), risorEngine.WithDataProvider(staticProvider)) require.NoError(t, err) @@ -651,7 +651,7 @@ _ = result staticProvider := data.NewStaticProvider(staticData) scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - evaluator, err := starlarkEngine.FromStarlarkLoader(scriptLoader, + evaluator, err := starlarkEngine.FromStarlarkLoader(t.Context(), scriptLoader, starlarkEngine.WithLogHandler(handler), starlarkEngine.WithDataProvider(staticProvider)) require.NoError(t, err) @@ -680,7 +680,7 @@ _ = result }) scriptLoader, err := loader.NewFromBytes(wasmdata.TestModule) require.NoError(t, err) - evaluator, err := extismEngine.FromExtismLoader(scriptLoader, + evaluator, err := extismEngine.FromExtismLoader(t.Context(), scriptLoader, extismEngine.WithLogHandler(handler), extismEngine.WithDataProvider(staticProvider), extismEngine.WithEntryPoint(wasmdata.EntrypointGreet), @@ -734,7 +734,7 @@ let request_id = ctx["request_id"] scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - evaluator, err := risorEngine.FromRisorLoader(scriptLoader, + evaluator, err := risorEngine.FromRisorLoader(t.Context(), scriptLoader, risorEngine.WithLogHandler(handler), risorEngine.WithDataProvider(compositeProvider)) require.NoError(t, err) @@ -781,7 +781,7 @@ _ = result scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - evaluator, err := starlarkEngine.FromStarlarkLoader(scriptLoader, + evaluator, err := starlarkEngine.FromStarlarkLoader(t.Context(), scriptLoader, starlarkEngine.WithLogHandler(handler), starlarkEngine.WithDataProvider(compositeProvider)) require.NoError(t, err) @@ -826,7 +826,7 @@ _ = result scriptLoader, err := loader.NewFromBytes(wasmdata.TestModule) require.NoError(t, err) - evaluator, err := extismEngine.FromExtismLoader(scriptLoader, + evaluator, err := extismEngine.FromExtismLoader(t.Context(), scriptLoader, extismEngine.WithLogHandler(handler), extismEngine.WithDataProvider(compositeProvider), extismEngine.WithEntryPoint(wasmdata.EntrypointGreet), @@ -897,7 +897,7 @@ let content_type = ctx["request"]["Headers"]["Content-Type"][0] ctxProvider := data.NewContextProvider(constants.EvalData) scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - evaluator, err := risorEngine.FromRisorLoader(scriptLoader, + evaluator, err := risorEngine.FromRisorLoader(t.Context(), scriptLoader, risorEngine.WithLogHandler(handler), risorEngine.WithDataProvider(ctxProvider)) require.NoError(t, err) @@ -946,7 +946,7 @@ _ = result ctxProvider := data.NewContextProvider(constants.EvalData) scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - evaluator, err := starlarkEngine.FromStarlarkLoader(scriptLoader, + evaluator, err := starlarkEngine.FromStarlarkLoader(t.Context(), scriptLoader, starlarkEngine.WithLogHandler(handler), starlarkEngine.WithDataProvider(ctxProvider)) require.NoError(t, err) @@ -978,7 +978,7 @@ _ = result ctxProvider := data.NewContextProvider(constants.EvalData) scriptLoader, err := loader.NewFromBytes(wasmdata.TestModule) require.NoError(t, err) - evaluator, err := extismEngine.FromExtismLoader(scriptLoader, + evaluator, err := extismEngine.FromExtismLoader(t.Context(), scriptLoader, extismEngine.WithLogHandler(handler), extismEngine.WithDataProvider(ctxProvider), extismEngine.WithEntryPoint(wasmdata.EntrypointGreet), @@ -1029,7 +1029,7 @@ let config_data = ctx["config"] ctxProvider := data.NewContextProvider(constants.EvalData) scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - evaluator, err := risorEngine.FromRisorLoader(scriptLoader, + evaluator, err := risorEngine.FromRisorLoader(t.Context(), scriptLoader, risorEngine.WithLogHandler(handler), risorEngine.WithDataProvider(ctxProvider)) require.NoError(t, err) diff --git a/engines/risor/new.go b/engines/risor/new.go index d1a4a42..3e8c5c1 100644 --- a/engines/risor/new.go +++ b/engines/risor/new.go @@ -47,11 +47,15 @@ import ( // evaluator from this package. Configure it with the With* options: // // eval, err := risor.FromRisorLoader( +// ctx, // ldr, // risor.WithLogHandler(slog.Default().Handler()), // risor.WithStaticData(map[string]any{"version": "1.0.0"}), // ) // +// The supplied ctx flows into the loader's I/O and the Risor parser; +// cancelling it halts compilation in flight. +// // All options are optional. With no options the evaluator inherits the // default slog handler (via [helpers.SetupLogger]) and uses a // [data.ContextProvider] for runtime data. @@ -59,7 +63,7 @@ import ( // If both [WithStaticData] and [WithDataProvider] are supplied, // [WithDataProvider] takes precedence — pass exactly one of them per // call to keep intent unambiguous. -func FromRisorLoader(ldr loader.Loader, opts ...Option) (*evaluator.Evaluator, error) { +func FromRisorLoader(ctx context.Context, ldr loader.Loader, opts ...Option) (*evaluator.Evaluator, error) { cfg := &config{} for _, opt := range opts { if opt != nil { @@ -83,10 +87,7 @@ func FromRisorLoader(ldr loader.Loader, opts ...Option) (*evaluator.Evaluator, e execUnitID = u.String() } - // FromRisorLoader is a one-shot startup constructor; compile uses a - // fresh Background context. Callers needing cancellable compile - // should drive script.NewExecutableUnit directly. - execUnit, err := script.NewExecutableUnit(context.Background(), cfg.handler, execUnitID, ldr, comp, provider) + execUnit, err := script.NewExecutableUnit(ctx, cfg.handler, execUnitID, ldr, comp, provider) if err != nil { return nil, err } diff --git a/engines/risor/new_test.go b/engines/risor/new_test.go index 9a9829e..873d807 100644 --- a/engines/risor/new_test.go +++ b/engines/risor/new_test.go @@ -2,6 +2,7 @@ package risor import ( "bytes" + "context" "errors" "fmt" "io" @@ -47,7 +48,7 @@ func newErrorLoader(t *testing.T, msg string) *loaderMock { func TestFromRisorLoader_NoOptions(t *testing.T) { // Without WithLogHandler the evaluator inherits slog.Default(); no error. - eval, err := FromRisorLoader(newTestLoader(t)) + eval, err := FromRisorLoader(t.Context(), newTestLoader(t)) require.NoError(t, err) require.NotNil(t, eval) assert.Equal(t, "risor.Evaluator", eval.String()) @@ -55,14 +56,14 @@ func TestFromRisorLoader_NoOptions(t *testing.T) { func TestFromRisorLoader_WithLogHandler(t *testing.T) { handler := slog.NewTextHandler(os.Stdout, nil) - eval, err := FromRisorLoader(newTestLoader(t), WithLogHandler(handler)) + eval, err := FromRisorLoader(t.Context(), newTestLoader(t), WithLogHandler(handler)) require.NoError(t, err) require.NotNil(t, eval) } func TestFromRisorLoader_NilLogHandler(t *testing.T) { // Explicitly passing nil is treated the same as omitting the option. - eval, err := FromRisorLoader(newTestLoader(t), WithLogHandler(nil)) + eval, err := FromRisorLoader(t.Context(), newTestLoader(t), WithLogHandler(nil)) require.NoError(t, err) require.NotNil(t, eval) } @@ -72,28 +73,28 @@ func TestFromRisorLoader_WithStaticData(t *testing.T) { "version": "1.0.0", "config": map[string]any{"timeout": 30, "retry": true}, } - eval, err := FromRisorLoader(newTestLoader(t), WithStaticData(staticData)) + eval, err := FromRisorLoader(t.Context(), newTestLoader(t), WithStaticData(staticData)) require.NoError(t, err) require.NotNil(t, eval) } func TestFromRisorLoader_EmptyStaticData(t *testing.T) { // Empty (but non-nil) map still composes a CompositeProvider; should succeed. - eval, err := FromRisorLoader(newTestLoader(t), WithStaticData(map[string]any{})) + eval, err := FromRisorLoader(t.Context(), newTestLoader(t), WithStaticData(map[string]any{})) require.NoError(t, err) require.NotNil(t, eval) } func TestFromRisorLoader_WithDataProvider(t *testing.T) { provider := data.NewContextProvider("test_key") - eval, err := FromRisorLoader(newTestLoader(t), WithDataProvider(provider)) + eval, err := FromRisorLoader(t.Context(), newTestLoader(t), WithDataProvider(provider)) require.NoError(t, err) require.NotNil(t, eval) } func TestFromRisorLoader_DataProviderBeatsStaticData(t *testing.T) { provider := data.NewContextProvider("sentinel") - eval, err := FromRisorLoader( + eval, err := FromRisorLoader(t.Context(), newTestLoader(t), WithStaticData(map[string]any{"ignored": true}), WithDataProvider(provider), @@ -105,14 +106,14 @@ func TestFromRisorLoader_DataProviderBeatsStaticData(t *testing.T) { func TestFromRisorLoader_NilOption(t *testing.T) { // nil options should be ignored, not panic. var nilOpt Option - eval, err := FromRisorLoader(newTestLoader(t), nilOpt, WithStaticData(map[string]any{"k": "v"})) + eval, err := FromRisorLoader(t.Context(), newTestLoader(t), nilOpt, WithStaticData(map[string]any{"k": "v"})) require.NoError(t, err) require.NotNil(t, eval) } func TestFromRisorLoader_LoaderError(t *testing.T) { mockLoader := newErrorLoader(t, "failed to load script") - eval, err := FromRisorLoader(mockLoader) + eval, err := FromRisorLoader(t.Context(), mockLoader) require.Error(t, err) require.Nil(t, eval) assert.Contains(t, err.Error(), "failed to load script") @@ -128,7 +129,7 @@ func TestFromRisorLoader_DiskLoader(t *testing.T) { require.NoError(t, err) require.NotNil(t, diskLoader) - eval, err := FromRisorLoader(diskLoader) + eval, err := FromRisorLoader(t.Context(), diskLoader) require.NoError(t, err) require.NotNil(t, eval) assert.Equal(t, "risor.Evaluator", eval.String()) @@ -147,7 +148,7 @@ func TestFromRisorLoader_RunsEndToEnd(t *testing.T) { scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - eval, err := FromRisorLoader( + eval, err := FromRisorLoader(t.Context(), scriptLoader, WithStaticData(map[string]any{"name": "World"}), ) @@ -171,7 +172,7 @@ func TestFromRisorLoader_DefaultsToSlogDefault(t *testing.T) { scriptLoader, err := loader.NewFromString(`"hi"`) require.NoError(t, err) - eval, err := FromRisorLoader(scriptLoader) + eval, err := FromRisorLoader(t.Context(), scriptLoader) require.NoError(t, err) require.NotNil(t, eval) } @@ -192,3 +193,19 @@ func TestNewCompiler(t *testing.T) { require.NotNil(t, comp) }) } + +// TestFromRisorLoader_CompileCancelled verifies that a pre-cancelled ctx +// causes the Risor parser to abort during compile. This is the headline +// behavior of #145: a cancellable ctx now actually reaches the parser +// instead of being swallowed by an inline context.Background(). +func TestFromRisorLoader_CompileCancelled(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(t.Context()) + cancel() // pre-cancel before construction + + eval, err := FromRisorLoader(ctx, newTestLoader(t)) + require.Error(t, err, "expected an error when ctx is pre-cancelled") + require.Nil(t, eval) + require.ErrorIs(t, err, context.Canceled) +} diff --git a/engines/starlark/new.go b/engines/starlark/new.go index f68dd8b..d4c3a10 100644 --- a/engines/starlark/new.go +++ b/engines/starlark/new.go @@ -59,7 +59,10 @@ import ( // If both [WithStaticData] and [WithDataProvider] are supplied, // [WithDataProvider] takes precedence — pass exactly one of them per // call to keep intent unambiguous. -func FromStarlarkLoader(ldr loader.Loader, opts ...Option) (*evaluator.Evaluator, error) { +// +// The supplied ctx flows into the loader's I/O; Starlark's parser is +// synchronous and does not observe ctx during compile. +func FromStarlarkLoader(ctx context.Context, ldr loader.Loader, opts ...Option) (*evaluator.Evaluator, error) { cfg := &config{} for _, opt := range opts { if opt != nil { @@ -83,10 +86,7 @@ func FromStarlarkLoader(ldr loader.Loader, opts ...Option) (*evaluator.Evaluator execUnitID = u.String() } - // FromStarlarkLoader is a one-shot startup constructor; compile uses a - // fresh Background context. Callers needing cancellable compile - // should drive script.NewExecutableUnit directly. - execUnit, err := script.NewExecutableUnit(context.Background(), cfg.handler, execUnitID, ldr, comp, provider) + execUnit, err := script.NewExecutableUnit(ctx, cfg.handler, execUnitID, ldr, comp, provider) if err != nil { return nil, err } diff --git a/engines/starlark/new_test.go b/engines/starlark/new_test.go index 93af763..f2a1dc9 100644 --- a/engines/starlark/new_test.go +++ b/engines/starlark/new_test.go @@ -46,7 +46,7 @@ func newErrorLoader(t *testing.T, msg string) *loaderMock { } func TestFromStarlarkLoader_NoOptions(t *testing.T) { - eval, err := FromStarlarkLoader(newTestLoader(t)) + eval, err := FromStarlarkLoader(t.Context(), newTestLoader(t)) require.NoError(t, err) require.NotNil(t, eval) assert.Equal(t, "starlark.Evaluator", eval.String()) @@ -54,13 +54,13 @@ func TestFromStarlarkLoader_NoOptions(t *testing.T) { func TestFromStarlarkLoader_WithLogHandler(t *testing.T) { handler := slog.NewTextHandler(os.Stdout, nil) - eval, err := FromStarlarkLoader(newTestLoader(t), WithLogHandler(handler)) + eval, err := FromStarlarkLoader(t.Context(), newTestLoader(t), WithLogHandler(handler)) require.NoError(t, err) require.NotNil(t, eval) } func TestFromStarlarkLoader_NilLogHandler(t *testing.T) { - eval, err := FromStarlarkLoader(newTestLoader(t), WithLogHandler(nil)) + eval, err := FromStarlarkLoader(t.Context(), newTestLoader(t), WithLogHandler(nil)) require.NoError(t, err) require.NotNil(t, eval) } @@ -70,27 +70,27 @@ func TestFromStarlarkLoader_WithStaticData(t *testing.T) { "version": "1.0.0", "config": map[string]any{"timeout": 30, "retry": true}, } - eval, err := FromStarlarkLoader(newTestLoader(t), WithStaticData(staticData)) + eval, err := FromStarlarkLoader(t.Context(), newTestLoader(t), WithStaticData(staticData)) require.NoError(t, err) require.NotNil(t, eval) } func TestFromStarlarkLoader_EmptyStaticData(t *testing.T) { - eval, err := FromStarlarkLoader(newTestLoader(t), WithStaticData(map[string]any{})) + eval, err := FromStarlarkLoader(t.Context(), newTestLoader(t), WithStaticData(map[string]any{})) require.NoError(t, err) require.NotNil(t, eval) } func TestFromStarlarkLoader_WithDataProvider(t *testing.T) { provider := data.NewContextProvider("test_key") - eval, err := FromStarlarkLoader(newTestLoader(t), WithDataProvider(provider)) + eval, err := FromStarlarkLoader(t.Context(), newTestLoader(t), WithDataProvider(provider)) require.NoError(t, err) require.NotNil(t, eval) } func TestFromStarlarkLoader_DataProviderBeatsStaticData(t *testing.T) { provider := data.NewContextProvider("sentinel") - eval, err := FromStarlarkLoader( + eval, err := FromStarlarkLoader(t.Context(), newTestLoader(t), WithStaticData(map[string]any{"ignored": true}), WithDataProvider(provider), @@ -101,14 +101,14 @@ func TestFromStarlarkLoader_DataProviderBeatsStaticData(t *testing.T) { func TestFromStarlarkLoader_NilOption(t *testing.T) { var nilOpt Option - eval, err := FromStarlarkLoader(newTestLoader(t), nilOpt, WithStaticData(map[string]any{"k": "v"})) + eval, err := FromStarlarkLoader(t.Context(), newTestLoader(t), nilOpt, WithStaticData(map[string]any{"k": "v"})) require.NoError(t, err) require.NotNil(t, eval) } func TestFromStarlarkLoader_LoaderError(t *testing.T) { mockLoader := newErrorLoader(t, "failed to load script") - eval, err := FromStarlarkLoader(mockLoader) + eval, err := FromStarlarkLoader(t.Context(), mockLoader) require.Error(t, err) require.Nil(t, eval) assert.Contains(t, err.Error(), "failed to load script") @@ -118,7 +118,7 @@ func TestFromStarlarkLoader_LoaderError(t *testing.T) { func TestFromStarlarkLoader_InvalidScript(t *testing.T) { invalidLoader, err := loader.NewFromString(`this is { not valid } Starlark syntax`) require.NoError(t, err) - eval, err := FromStarlarkLoader(invalidLoader) + eval, err := FromStarlarkLoader(t.Context(), invalidLoader) require.Error(t, err) require.Nil(t, eval) assert.ErrorIs(t, err, compiler.ErrValidationFailed) @@ -133,7 +133,7 @@ func TestFromStarlarkLoader_DiskLoader(t *testing.T) { require.NoError(t, err) require.NotNil(t, diskLoader) - eval, err := FromStarlarkLoader(diskLoader) + eval, err := FromStarlarkLoader(t.Context(), diskLoader) require.NoError(t, err) require.NotNil(t, eval) assert.Equal(t, "starlark.Evaluator", eval.String()) @@ -154,7 +154,7 @@ _ = result scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - eval, err := FromStarlarkLoader( + eval, err := FromStarlarkLoader(t.Context(), scriptLoader, WithStaticData(map[string]any{"name": "World"}), ) @@ -178,7 +178,7 @@ func TestFromStarlarkLoader_DefaultsToSlogDefault(t *testing.T) { scriptLoader, err := loader.NewFromString(`_ = "ok"`) require.NoError(t, err) - eval, err := FromStarlarkLoader(scriptLoader) + eval, err := FromStarlarkLoader(t.Context(), scriptLoader) require.NoError(t, err) require.NotNil(t, eval) } diff --git a/examples/data-prep/extism/main.go b/examples/data-prep/extism/main.go index ff3746f..2341025 100644 --- a/examples/data-prep/extism/main.go +++ b/examples/data-prep/extism/main.go @@ -110,6 +110,7 @@ func run() error { } evaluator, err := polyscript.New[polyscript.Extism]( + context.Background(), polyscript.FromBytes(wasmdata.TestModule), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](staticData), diff --git a/examples/data-prep/risor/main.go b/examples/data-prep/risor/main.go index 9d97c61..c6fcf22 100644 --- a/examples/data-prep/risor/main.go +++ b/examples/data-prep/risor/main.go @@ -26,6 +26,7 @@ func createRisorEvaluator( staticData map[string]any, ) (RisorEvaluator, error) { return polyscript.New[polyscript.Risor]( + context.Background(), polyscript.FromString(scriptContent), polyscript.WithStaticData[polyscript.Risor](staticData), polyscript.WithLogHandler[polyscript.Risor](logger.Handler()), diff --git a/examples/data-prep/starlark/main.go b/examples/data-prep/starlark/main.go index 8e929af..b810a05 100644 --- a/examples/data-prep/starlark/main.go +++ b/examples/data-prep/starlark/main.go @@ -29,6 +29,7 @@ func createStarlarkEvaluator( staticData map[string]any, ) (StarlarkEvaluator, error) { return polyscript.New[polyscript.Starlark]( + context.Background(), polyscript.FromString(scriptContent), polyscript.WithStaticData[polyscript.Starlark](staticData), polyscript.WithLogHandler[polyscript.Starlark](logger.Handler()), diff --git a/examples/multiple-instantiation/extism/main.go b/examples/multiple-instantiation/extism/main.go index f50cc96..0869601 100644 --- a/examples/multiple-instantiation/extism/main.go +++ b/examples/multiple-instantiation/extism/main.go @@ -23,6 +23,7 @@ func createEvaluator(logger *slog.Logger) (ExtismEvaluator, error) { } evaluator, err := polyscript.New[polyscript.Extism]( + context.Background(), polyscript.FromBytes(wasmdata.TestModule), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithLogHandler[polyscript.Extism](logger.Handler()), diff --git a/examples/multiple-instantiation/risor/main.go b/examples/multiple-instantiation/risor/main.go index 8cb63c2..2b1729e 100644 --- a/examples/multiple-instantiation/risor/main.go +++ b/examples/multiple-instantiation/risor/main.go @@ -25,6 +25,7 @@ func createEvaluator(logger *slog.Logger) (RisorEvaluator, error) { } evaluator, err := polyscript.New[polyscript.Risor]( + context.Background(), polyscript.FromString(risorScript), polyscript.WithLogHandler[polyscript.Risor](logger.Handler()), ) diff --git a/examples/multiple-instantiation/starlark/main.go b/examples/multiple-instantiation/starlark/main.go index 141feed..cd9aca2 100644 --- a/examples/multiple-instantiation/starlark/main.go +++ b/examples/multiple-instantiation/starlark/main.go @@ -25,6 +25,7 @@ func createEvaluator(logger *slog.Logger) (StarlarkEvaluator, error) { } evaluator, err := polyscript.New[polyscript.Starlark]( + context.Background(), polyscript.FromString(starlarkScript), polyscript.WithLogHandler[polyscript.Starlark](logger.Handler()), ) diff --git a/examples/simple/extism/main.go b/examples/simple/extism/main.go index badcd35..2cfba3c 100644 --- a/examples/simple/extism/main.go +++ b/examples/simple/extism/main.go @@ -23,6 +23,7 @@ func runExtismExample(logger *slog.Logger) (map[string]any, error) { } evaluator, err := polyscript.New[polyscript.Extism]( + context.Background(), polyscript.FromBytes(wasmdata.TestModule), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](inputData), diff --git a/examples/simple/risor/main.go b/examples/simple/risor/main.go index a8b25c3..a179a25 100644 --- a/examples/simple/risor/main.go +++ b/examples/simple/risor/main.go @@ -25,6 +25,7 @@ func runRisorExample(logger *slog.Logger) (map[string]any, error) { } evaluator, err := polyscript.New[polyscript.Risor]( + context.Background(), polyscript.FromString(risorScript), polyscript.WithStaticData[polyscript.Risor](input), polyscript.WithLogHandler[polyscript.Risor](logger.Handler()), diff --git a/examples/simple/starlark/main.go b/examples/simple/starlark/main.go index cddc39b..53bc92c 100644 --- a/examples/simple/starlark/main.go +++ b/examples/simple/starlark/main.go @@ -25,6 +25,7 @@ func runStarlarkExample(logger *slog.Logger) (map[string]any, error) { } evaluator, err := polyscript.New[polyscript.Starlark]( + context.Background(), polyscript.FromString(starlarkScript), polyscript.WithStaticData[polyscript.Starlark](input), polyscript.WithLogHandler[polyscript.Starlark](logger.Handler()), diff --git a/polyscript.go b/polyscript.go index 62a8fea..ec32ad0 100644 --- a/polyscript.go +++ b/polyscript.go @@ -48,6 +48,7 @@ package polyscript import ( + "context" "errors" "fmt" "log/slog" @@ -222,19 +223,22 @@ func WithExitOutputMaxBytes(n int) Option[Extism] { // New constructs an evaluator for the engine selected via the type parameter: // -// eval, err := polyscript.New[polyscript.Risor](polyscript.FromString(script)) +// eval, err := polyscript.New[polyscript.Risor](ctx, polyscript.FromString(script)) // -// eval, err := polyscript.New[polyscript.Extism]( +// eval, err := polyscript.New[polyscript.Extism](ctx, // polyscript.FromBytes(wasmBytes), // polyscript.WithEntryPoint("greet"), // polyscript.WithStaticData[polyscript.Extism](map[string]any{"input": "World"}), // ) // +// The supplied ctx flows into the loader's I/O (e.g. HTTP fetch) and the +// compiler's parser (Risor, Extism); cancelling it halts those operations. +// // Shared options ([WithStaticData], [WithLogHandler]) work for any engine but // usually need an explicit type argument; see "A note on type arguments" in // the package doc. [WithEntryPoint] is bound to [Extism]; passing it to // [Risor] or [Starlark] is a compile error. -func New[E Engine](src Source, opts ...Option[E]) (platform.Evaluator, error) { +func New[E Engine](ctx context.Context, src Source, opts ...Option[E]) (platform.Evaluator, error) { cfg := &config{} for _, o := range opts { if o != nil { @@ -253,33 +257,33 @@ func New[E Engine](src Source, opts ...Option[E]) (platform.Evaluator, error) { switch any(e).(type) { case Risor: - return newRisor(ldr, cfg) + return newRisor(ctx, ldr, cfg) case Starlark: - return newStarlark(ldr, cfg) + return newStarlark(ctx, ldr, cfg) case Extism: - return newExtism(ldr, cfg) + return newExtism(ctx, ldr, cfg) default: return nil, fmt.Errorf("polyscript.New: unsupported engine type %T", e) } } -func newRisor(ldr loader.Loader, cfg *config) (platform.Evaluator, error) { +func newRisor(ctx context.Context, ldr loader.Loader, cfg *config) (platform.Evaluator, error) { opts := []risorMachine.Option{risorMachine.WithLogHandler(cfg.handler)} if cfg.staticData != nil { opts = append(opts, risorMachine.WithStaticData(cfg.staticData)) } - return risorMachine.FromRisorLoader(ldr, opts...) + return risorMachine.FromRisorLoader(ctx, ldr, opts...) } -func newStarlark(ldr loader.Loader, cfg *config) (platform.Evaluator, error) { +func newStarlark(ctx context.Context, ldr loader.Loader, cfg *config) (platform.Evaluator, error) { opts := []starlarkMachine.Option{starlarkMachine.WithLogHandler(cfg.handler)} if cfg.staticData != nil { opts = append(opts, starlarkMachine.WithStaticData(cfg.staticData)) } - return starlarkMachine.FromStarlarkLoader(ldr, opts...) + return starlarkMachine.FromStarlarkLoader(ctx, ldr, opts...) } -func newExtism(ldr loader.Loader, cfg *config) (platform.Evaluator, error) { +func newExtism(ctx context.Context, ldr loader.Loader, cfg *config) (platform.Evaluator, error) { opts := []extismMachine.Option{ extismMachine.WithEntryPoint(cfg.entryPoint), extismMachine.WithLogHandler(cfg.handler), @@ -288,5 +292,5 @@ func newExtism(ldr loader.Loader, cfg *config) (platform.Evaluator, error) { if cfg.staticData != nil { opts = append(opts, extismMachine.WithStaticData(cfg.staticData)) } - return extismMachine.FromExtismLoader(ldr, opts...) + return extismMachine.FromExtismLoader(ctx, ldr, opts...) } diff --git a/polyscript_extism_test.go b/polyscript_extism_test.go index e2e65cf..e7b1f20 100644 --- a/polyscript_extism_test.go +++ b/polyscript_extism_test.go @@ -20,6 +20,7 @@ func TestExtismFromBytes(t *testing.T) { t.Run("success with embedded wasmdata", func(t *testing.T) { eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromBytes(wasmdata.TestModule), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), ) @@ -29,6 +30,7 @@ func TestExtismFromBytes(t *testing.T) { t.Run("empty bytes", func(t *testing.T) { eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromBytes([]byte{}), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), ) @@ -50,6 +52,7 @@ func TestExtismFromBytesWithData(t *testing.T) { } eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromBytes(wasmdata.TestModule), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](staticData), @@ -76,6 +79,7 @@ func TestExtismFromFile(t *testing.T) { t.Run("valid", func(t *testing.T) { eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromFile(wasmPath), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), ) @@ -93,6 +97,7 @@ func TestExtismFromFile(t *testing.T) { t.Run("invalid path", func(t *testing.T) { _, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromFile("non-existent-file.wasm"), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), ) @@ -101,6 +106,7 @@ func TestExtismFromFile(t *testing.T) { t.Run("with static data", func(t *testing.T) { eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromFile(wasmPath), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](map[string]any{"input": "Test User"}), @@ -119,6 +125,7 @@ func TestExtismDataIntegrationScenarios(t *testing.T) { t.Run("ExtismWithData", func(t *testing.T) { eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromFile(wasmPath), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](map[string]any{"input": "Test User"}), @@ -136,6 +143,7 @@ func TestExtismDataIntegrationScenarios(t *testing.T) { assert.Equal(t, "Hello, Test User!", resultMap["greeting"]) evalNoInput, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromFile(wasmPath), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](map[string]any{ @@ -160,6 +168,7 @@ func TestExtismEvalEndToEnd(t *testing.T) { require.NoError(t, os.WriteFile(wasmPath, wasmData, 0o644)) eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromFile(wasmPath), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](map[string]any{"input": "Test User"}), diff --git a/polyscript_options_test.go b/polyscript_options_test.go index 32f4d92..47838e8 100644 --- a/polyscript_options_test.go +++ b/polyscript_options_test.go @@ -16,7 +16,7 @@ func TestNewRisor(t *testing.T) { t.Parallel() t.Run("FromString minimal", func(t *testing.T) { - eval, err := polyscript.New[polyscript.Risor](polyscript.FromString(`"hello"`)) + eval, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromString(`"hello"`)) require.NoError(t, err) require.NotNil(t, eval) }) @@ -24,6 +24,7 @@ func TestNewRisor(t *testing.T) { t.Run("FromString with static data", func(t *testing.T) { script := `{"name": ctx["name"], "length": len(ctx["name"])}` eval, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(script), polyscript.WithStaticData[polyscript.Risor](map[string]any{"name": "World"}), ) @@ -38,7 +39,7 @@ func TestNewRisor(t *testing.T) { t.Run("FromString with dynamic runtime data", func(t *testing.T) { script := `{"name": ctx["name"]}` - eval, err := polyscript.New[polyscript.Risor](polyscript.FromString(script)) + eval, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromString(script)) require.NoError(t, err) ctx, err := eval.AddDataToContext(t.Context(), map[string]any{"name": "Robert"}) @@ -55,18 +56,18 @@ func TestNewRisor(t *testing.T) { path := filepath.Join(dir, "script.risor") require.NoError(t, os.WriteFile(path, []byte(`{"ok": true}`), 0o600)) - eval, err := polyscript.New[polyscript.Risor](polyscript.FromFile(path)) + eval, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromFile(path)) require.NoError(t, err) require.NotNil(t, eval) }) t.Run("empty string surfaces error from source", func(t *testing.T) { - _, err := polyscript.New[polyscript.Risor](polyscript.FromString("")) + _, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromString("")) require.Error(t, err) }) t.Run("zero-value Source errors", func(t *testing.T) { - _, err := polyscript.New[polyscript.Risor](polyscript.Source{}) + _, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.Source{}) require.Error(t, err) assert.Contains(t, err.Error(), "zero-value Source") }) @@ -76,7 +77,7 @@ func TestNewStarlark(t *testing.T) { t.Parallel() t.Run("FromString minimal", func(t *testing.T) { - eval, err := polyscript.New[polyscript.Starlark](polyscript.FromString(`_ = "hello"`)) + eval, err := polyscript.New[polyscript.Starlark](t.Context(), polyscript.FromString(`_ = "hello"`)) require.NoError(t, err) require.NotNil(t, eval) }) @@ -85,6 +86,7 @@ func TestNewStarlark(t *testing.T) { script := `result = {"name": ctx["name"]} _ = result` eval, err := polyscript.New[polyscript.Starlark]( + t.Context(), polyscript.FromString(script), polyscript.WithStaticData[polyscript.Starlark](map[string]any{"name": "World"}), ) @@ -103,6 +105,7 @@ func TestNewExtism(t *testing.T) { t.Run("FromBytes with entry point", func(t *testing.T) { eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromBytes(wasmdata.TestModule), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](map[string]any{"input": "World"}), @@ -117,7 +120,7 @@ func TestNewExtism(t *testing.T) { }) t.Run("missing entry point errors", func(t *testing.T) { - _, err := polyscript.New[polyscript.Extism](polyscript.FromBytes(wasmdata.TestModule)) + _, err := polyscript.New[polyscript.Extism](t.Context(), polyscript.FromBytes(wasmdata.TestModule)) require.Error(t, err) assert.Contains(t, err.Error(), "entry point is required") }) @@ -125,7 +128,7 @@ func TestNewExtism(t *testing.T) { t.Run("missing entry point errors before resolving source", func(t *testing.T) { // The missing-entrypoint check must short-circuit before we try to // read empty bytes, so the user gets the more informative error. - _, err := polyscript.New[polyscript.Extism](polyscript.FromBytes(nil)) + _, err := polyscript.New[polyscript.Extism](t.Context(), polyscript.FromBytes(nil)) require.Error(t, err) assert.Contains(t, err.Error(), "entry point is required") }) @@ -136,6 +139,7 @@ func TestNewExtism(t *testing.T) { require.NoError(t, os.WriteFile(path, wasmdata.TestModule, 0o600)) eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromFile(path), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](map[string]any{"input": "Test"}), @@ -157,6 +161,7 @@ func TestNewExtism(t *testing.T) { // engines/extism/evaluator/evaluator_test.go (issue #122). for _, n := range []int{-1, 0, 42, 8 * 1024} { eval, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromBytes(wasmdata.TestModule), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithExitOutputMaxBytes(n), @@ -180,13 +185,13 @@ func TestFromLoader(t *testing.T) { ldr, err := loader.NewFromString(`"ok"`) require.NoError(t, err) - eval, err := polyscript.New[polyscript.Risor](polyscript.FromLoader(ldr)) + eval, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromLoader(ldr)) require.NoError(t, err) require.NotNil(t, eval) }) t.Run("nil loader errors", func(t *testing.T) { - _, err := polyscript.New[polyscript.Risor](polyscript.FromLoader(nil)) + _, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromLoader(nil)) require.Error(t, err) assert.Contains(t, err.Error(), "nil loader") }) @@ -199,7 +204,7 @@ func TestFromLoader(t *testing.T) { var typedNil *loader.FromString var l loader.Loader = typedNil - _, err := polyscript.New[polyscript.Risor](polyscript.FromLoader(l)) + _, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromLoader(l)) require.Error(t, err) assert.Contains(t, err.Error(), "nil loader") }) @@ -210,6 +215,7 @@ func TestOptionsCompose(t *testing.T) { t.Run("nil Option is ignored", func(t *testing.T) { eval, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(`"ok"`), nil, polyscript.WithStaticData[polyscript.Risor](map[string]any{"k": "v"}), @@ -220,6 +226,7 @@ func TestOptionsCompose(t *testing.T) { t.Run("WithLogHandler accepts nil", func(t *testing.T) { eval, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(`"ok"`), polyscript.WithLogHandler[polyscript.Risor](nil), ) @@ -229,6 +236,7 @@ func TestOptionsCompose(t *testing.T) { t.Run("later option wins for static data", func(t *testing.T) { eval, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(`{"k": ctx["k"]}`), polyscript.WithStaticData[polyscript.Risor](map[string]any{"k": "first"}), polyscript.WithStaticData[polyscript.Risor](map[string]any{"k": "second"}), @@ -260,6 +268,7 @@ func TestFromBytesIsolatesCallerSlice(t *testing.T) { } eval, err := polyscript.New[polyscript.Extism]( + t.Context(), src, polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](map[string]any{"input": "World"}), @@ -289,7 +298,7 @@ func TestSourceErrorPropagation(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - _, err := polyscript.New[polyscript.Risor](tc.source) + _, err := polyscript.New[polyscript.Risor](t.Context(), tc.source) require.Error(t, err) assert.Contains(t, err.Error(), tc.want) }) diff --git a/polyscript_test.go b/polyscript_test.go index a20f93a..a570f4e 100644 --- a/polyscript_test.go +++ b/polyscript_test.go @@ -6,11 +6,14 @@ import ( "fmt" "io" "log/slog" + "net/http" + "net/http/httptest" "net/url" "os" "path/filepath" "strings" "testing" + "time" "github.com/robbyt/go-polyscript" "github.com/robbyt/go-polyscript/platform" @@ -84,14 +87,14 @@ func prepareAndEval( // stringEngineFn is a small adapter so a table-driven test can mix engines that // share the (script-content, log-handler) shape. -type stringEngineFn func(content string, h slog.Handler) (platform.Evaluator, error) +type stringEngineFn func(ctx context.Context, content string, h slog.Handler) (platform.Evaluator, error) -func risorString(content string, h slog.Handler) (platform.Evaluator, error) { - return polyscript.New[polyscript.Risor](polyscript.FromString(content), polyscript.WithLogHandler[polyscript.Risor](h)) +func risorString(ctx context.Context, content string, h slog.Handler) (platform.Evaluator, error) { + return polyscript.New[polyscript.Risor](ctx, polyscript.FromString(content), polyscript.WithLogHandler[polyscript.Risor](h)) } -func starlarkString(content string, h slog.Handler) (platform.Evaluator, error) { - return polyscript.New[polyscript.Starlark](polyscript.FromString(content), polyscript.WithLogHandler[polyscript.Starlark](h)) +func starlarkString(ctx context.Context, content string, h slog.Handler) (platform.Evaluator, error) { + return polyscript.New[polyscript.Starlark](ctx, polyscript.FromString(content), polyscript.WithLogHandler[polyscript.Starlark](h)) } func TestEngineEvaluatorsFromString(t *testing.T) { @@ -108,7 +111,7 @@ func TestEngineEvaluatorsFromString(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - eval, err := tc.creator(tc.content, nil) + eval, err := tc.creator(t.Context(), tc.content, nil) require.NoError(t, err) require.NotNil(t, eval) }) @@ -132,7 +135,7 @@ func TestFromStringValidation(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - eval, err := tc.creator(tc.content, nil) + eval, err := tc.creator(t.Context(), tc.content, nil) if tc.expectError { require.Error(t, err) return @@ -158,7 +161,7 @@ _ = result` require.NoError(t, os.WriteFile(starlarkPath, []byte(starlarkContent), 0o644)) t.Run("Risor file - valid", func(t *testing.T) { - eval, err := polyscript.New[polyscript.Risor](polyscript.FromFile(risorPath)) + eval, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromFile(risorPath)) require.NoError(t, err) require.NotNil(t, eval) @@ -168,12 +171,13 @@ _ = result` }) t.Run("Risor file - invalid path", func(t *testing.T) { - _, err := polyscript.New[polyscript.Risor](polyscript.FromFile("non-existent-file.risor")) + _, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromFile("non-existent-file.risor")) require.Error(t, err) }) t.Run("Risor file - with static data", func(t *testing.T) { eval, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromFile(risorPath), polyscript.WithStaticData[polyscript.Risor](map[string]any{"test_key": "test_value"}), ) @@ -182,7 +186,7 @@ _ = result` }) t.Run("Starlark file - valid", func(t *testing.T) { - eval, err := polyscript.New[polyscript.Starlark](polyscript.FromFile(starlarkPath)) + eval, err := polyscript.New[polyscript.Starlark](t.Context(), polyscript.FromFile(starlarkPath)) require.NoError(t, err) require.NotNil(t, eval) @@ -192,12 +196,13 @@ _ = result` }) t.Run("Starlark file - invalid path", func(t *testing.T) { - _, err := polyscript.New[polyscript.Starlark](polyscript.FromFile("non-existent-file.star")) + _, err := polyscript.New[polyscript.Starlark](t.Context(), polyscript.FromFile("non-existent-file.star")) require.Error(t, err) }) t.Run("Starlark file - with static data", func(t *testing.T) { eval, err := polyscript.New[polyscript.Starlark]( + t.Context(), polyscript.FromFile(starlarkPath), polyscript.WithStaticData[polyscript.Starlark](map[string]any{"test_key": "test_value"}), ) @@ -213,6 +218,7 @@ func TestDataProviders(t *testing.T) { script := `print(ctx["static_key"], ", ", ctx["dynamic_key"])` eval, err := polyscript.New[polyscript.Starlark]( + t.Context(), polyscript.FromString(script), polyscript.WithStaticData[polyscript.Starlark](map[string]any{"static_key": "static_value"}), ) @@ -242,6 +248,7 @@ func TestEvalHelpers(t *testing.T) { ` eval, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(script), polyscript.WithStaticData[polyscript.Risor](map[string]any{}), ) @@ -330,6 +337,7 @@ func TestEvalHelpers(t *testing.T) { ` eval, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(script), polyscript.WithStaticData[polyscript.Risor](map[string]any{}), ) @@ -351,13 +359,13 @@ func TestEvalHelpers(t *testing.T) { t.Errorf("length is unexpected type %T", v) } - nilEval, err := polyscript.New[polyscript.Risor](polyscript.FromString(`nil`)) + nilEval, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromString(`nil`)) require.NoError(t, err) nilResult, err := evalAndExtractMap(t, t.Context(), nilEval) require.NoError(t, err) assert.Equal(t, map[string]any{}, nilResult) - numEval, err := polyscript.New[polyscript.Risor](polyscript.FromString(`42`)) + numEval, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromString(`42`)) require.NoError(t, err) _, err = evalAndExtractMap(t, t.Context(), numEval) require.Error(t, err) @@ -412,6 +420,7 @@ _ = result` ` eval, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(risorScript), polyscript.WithStaticData[polyscript.Risor](staticData), ) @@ -443,6 +452,7 @@ _ = result` t.Run("StarlarkWithData", func(t *testing.T) { eval, err := polyscript.New[polyscript.Starlark]( + t.Context(), polyscript.FromFile(starlarkPath), polyscript.WithStaticData[polyscript.Starlark](staticData), ) @@ -470,19 +480,19 @@ func TestCreateEvaluatorEdgeCases(t *testing.T) { t.Parallel() t.Run("Empty Script Content Error", func(t *testing.T) { - _, err := polyscript.New[polyscript.Risor](polyscript.FromString("")) + _, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromString("")) require.Error(t, err) assert.Contains(t, err.Error(), "empty") }) t.Run("Invalid Path Error", func(t *testing.T) { - _, err := polyscript.New[polyscript.Risor](polyscript.FromFile("/path/does/not/exist.risor")) + _, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromFile("/path/does/not/exist.risor")) require.Error(t, err) assert.Contains(t, err.Error(), "no such file or directory") }) t.Run("InvalidScriptTest", func(t *testing.T) { - _, err := polyscript.New[polyscript.Risor](polyscript.FromString("this is not valid risor code }{")) + _, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromString("this is not valid risor code }{")) require.Error(t, err) assert.Contains(t, err.Error(), "compile") }) @@ -514,3 +524,50 @@ func TestFromStringLoader(t *testing.T) { require.NotNil(t, l.GetSourceURL()) }) } + +// TestNew_CompileCancelled proves the headline behavior of #145: a +// cancellable ctx supplied to polyscript.New[E] reaches the engine +// compile path. Risor's parser checks ctx; Extism's WASM compile of the +// small embedded test module completes too fast to observe a +// pre-cancelled ctx so it's covered via the HTTP-loader test below. +func TestNew_CompileCancelled(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(t.Context()) + cancel() + + _, err := polyscript.New[polyscript.Risor](ctx, polyscript.FromString(`"hi"`)) + require.Error(t, err) + require.ErrorIs(t, err, context.Canceled) +} + +// TestNew_HTTPLoaderCancelled proves the cancellable ctx actually +// reaches the HTTP loader's fetch — the precise scenario #145 set out +// to fix. The server delays the response past the ctx deadline; the +// fetch must abort with context.DeadlineExceeded rather than hanging. +func TestNew_HTTPLoaderCancelled(t *testing.T) { + t.Parallel() + + // Stall the server long enough for the ctx timeout to fire. + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + select { + case <-r.Context().Done(): + case <-time.After(2 * time.Second): + } + })) + t.Cleanup(server.Close) + + ldr, err := loader.NewFromHTTP(server.URL) + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(t.Context(), 50*time.Millisecond) + t.Cleanup(cancel) + + start := time.Now() + _, err = polyscript.New[polyscript.Risor](ctx, polyscript.FromLoader(ldr)) + require.Error(t, err) + require.ErrorIs(t, err, context.DeadlineExceeded) + // Sanity check: the abort happened near the deadline, not after the + // full 2s server stall. + assert.Less(t, time.Since(start), time.Second, "fetch should abort near the ctx deadline, not wait for the server") +} diff --git a/readme_test.go b/readme_test.go index 8b4c5d7..6391462 100644 --- a/readme_test.go +++ b/readme_test.go @@ -31,6 +31,7 @@ func TestReadmeQuickStart(t *testing.T) { ` evaluator, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(script), polyscript.WithStaticData[polyscript.Risor](map[string]any{"name": "World"}), ) @@ -66,6 +67,7 @@ func TestReadmeStaticProvider(t *testing.T) { ` evaluator, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(script), polyscript.WithStaticData[polyscript.Risor](map[string]any{"name": "cats", "excited": true}), ) @@ -92,7 +94,7 @@ func TestReadmeContextProvider(t *testing.T) { } ` - evaluator, err := polyscript.New[polyscript.Risor](polyscript.FromString(script)) + evaluator, err := polyscript.New[polyscript.Risor](t.Context(), polyscript.FromString(script)) require.NoError(t, err, "Should create evaluator successfully") runtimeData := map[string]any{"name": "Billie Jean", "relationship": false} @@ -134,6 +136,7 @@ func TestReadmeCombiningStaticAndDynamic(t *testing.T) { } evaluator, err := polyscript.New[polyscript.Risor]( + t.Context(), polyscript.FromString(script), polyscript.WithStaticData[polyscript.Risor](staticData), ) @@ -167,6 +170,7 @@ _ = result ` evaluator, err := polyscript.New[polyscript.Starlark]( + t.Context(), polyscript.FromString(scriptContent), polyscript.WithStaticData[polyscript.Starlark](map[string]any{"name": "World"}), ) @@ -185,6 +189,7 @@ func TestReadmeExtism(t *testing.T) { t.Parallel() evaluator, err := polyscript.New[polyscript.Extism]( + t.Context(), polyscript.FromBytes(wasmdata.TestModule), polyscript.WithEntryPoint(wasmdata.EntrypointGreet), polyscript.WithStaticData[polyscript.Extism](map[string]any{"input": "World"}), From 2f73466bd414bf65bc146b7dd2fd74acb87b98fd Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 23 May 2026 21:56:23 +0000 Subject: [PATCH 2/2] fix(test): gofumpt-format the three engine new_test.go files CI gci lint flagged formatting on the new ctx-injected From*Loader calls. gofumpt prefers each arg on its own line when wrapping; the mechanical sed I used to inject t.Context() left some calls with two args on the wrap line. https://claude.ai/code/session_01C61VEAmjxSnX5Xhbab8NvL --- engines/extism/new_test.go | 21 ++++++++++++++------- engines/risor/new_test.go | 6 ++++-- engines/starlark/new_test.go | 6 ++++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/engines/extism/new_test.go b/engines/extism/new_test.go index f9b1150..7468784 100644 --- a/engines/extism/new_test.go +++ b/engines/extism/new_test.go @@ -72,7 +72,8 @@ func TestFromExtismLoader_NoOptionsBeyondEntryPoint(t *testing.T) { func TestFromExtismLoader_WithLogHandler(t *testing.T) { mockLoader := newWASMLoader(t) handler := slog.NewTextHandler(os.Stdout, nil) - eval, err := FromExtismLoader(t.Context(), mockLoader, + eval, err := FromExtismLoader( + t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithLogHandler(handler), ) @@ -83,7 +84,8 @@ func TestFromExtismLoader_WithLogHandler(t *testing.T) { func TestFromExtismLoader_NilLogHandler(t *testing.T) { mockLoader := newWASMLoader(t) - eval, err := FromExtismLoader(t.Context(), mockLoader, + eval, err := FromExtismLoader( + t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithLogHandler(nil), ) @@ -94,7 +96,8 @@ func TestFromExtismLoader_NilLogHandler(t *testing.T) { func TestFromExtismLoader_WithStaticData(t *testing.T) { mockLoader := newWASMLoader(t) - eval, err := FromExtismLoader(t.Context(), mockLoader, + eval, err := FromExtismLoader( + t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithStaticData(map[string]any{"input": "World"}), ) @@ -106,7 +109,8 @@ func TestFromExtismLoader_WithStaticData(t *testing.T) { func TestFromExtismLoader_WithDataProvider(t *testing.T) { mockLoader := newWASMLoader(t) provider := data.NewContextProvider("test_key") - eval, err := FromExtismLoader(t.Context(), mockLoader, + eval, err := FromExtismLoader( + t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithDataProvider(provider), ) @@ -118,7 +122,8 @@ func TestFromExtismLoader_WithDataProvider(t *testing.T) { func TestFromExtismLoader_DataProviderBeatsStaticData(t *testing.T) { mockLoader := newWASMLoader(t) provider := data.NewContextProvider("sentinel") - eval, err := FromExtismLoader(t.Context(), mockLoader, + eval, err := FromExtismLoader( + t.Context(), mockLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithStaticData(map[string]any{"ignored": true}), WithDataProvider(provider), @@ -131,7 +136,8 @@ func TestFromExtismLoader_DataProviderBeatsStaticData(t *testing.T) { func TestFromExtismLoader_NilOption(t *testing.T) { mockLoader := newWASMLoader(t) var nilOpt Option - eval, err := FromExtismLoader(t.Context(), mockLoader, + eval, err := FromExtismLoader( + t.Context(), mockLoader, nilOpt, WithEntryPoint(wasmdata.EntrypointGreet), ) @@ -177,7 +183,8 @@ func TestFromExtismLoader_RunsEndToEnd(t *testing.T) { scriptLoader, err := loader.NewFromBytes(wasmdata.TestModule) require.NoError(t, err) - eval, err := FromExtismLoader(t.Context(), + eval, err := FromExtismLoader( + t.Context(), scriptLoader, WithEntryPoint(wasmdata.EntrypointGreet), WithStaticData(map[string]any{"input": "World"}), diff --git a/engines/risor/new_test.go b/engines/risor/new_test.go index 873d807..6c975ad 100644 --- a/engines/risor/new_test.go +++ b/engines/risor/new_test.go @@ -94,7 +94,8 @@ func TestFromRisorLoader_WithDataProvider(t *testing.T) { func TestFromRisorLoader_DataProviderBeatsStaticData(t *testing.T) { provider := data.NewContextProvider("sentinel") - eval, err := FromRisorLoader(t.Context(), + eval, err := FromRisorLoader( + t.Context(), newTestLoader(t), WithStaticData(map[string]any{"ignored": true}), WithDataProvider(provider), @@ -148,7 +149,8 @@ func TestFromRisorLoader_RunsEndToEnd(t *testing.T) { scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - eval, err := FromRisorLoader(t.Context(), + eval, err := FromRisorLoader( + t.Context(), scriptLoader, WithStaticData(map[string]any{"name": "World"}), ) diff --git a/engines/starlark/new_test.go b/engines/starlark/new_test.go index f2a1dc9..c55ddea 100644 --- a/engines/starlark/new_test.go +++ b/engines/starlark/new_test.go @@ -90,7 +90,8 @@ func TestFromStarlarkLoader_WithDataProvider(t *testing.T) { func TestFromStarlarkLoader_DataProviderBeatsStaticData(t *testing.T) { provider := data.NewContextProvider("sentinel") - eval, err := FromStarlarkLoader(t.Context(), + eval, err := FromStarlarkLoader( + t.Context(), newTestLoader(t), WithStaticData(map[string]any{"ignored": true}), WithDataProvider(provider), @@ -154,7 +155,8 @@ _ = result scriptLoader, err := loader.NewFromString(script) require.NoError(t, err) - eval, err := FromStarlarkLoader(t.Context(), + eval, err := FromStarlarkLoader( + t.Context(), scriptLoader, WithStaticData(map[string]any{"name": "World"}), )