Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`MachineType` equivalents). The conventional import alias is now
`engineTypes`. Closes [#90](https://github.com/robbyt/go-polyscript/issues/90).
([#137](https://github.com/robbyt/go-polyscript/pull/137))
- **Breaking:** Unexported the six fields on `platform/script.ExecutableUnit`
(`ID`, `CreatedAt`, `ScriptLoader`, `Compiler`, `Content`, `DataProvider`).
External callers must use the existing getters (`GetID()`, `GetCreatedAt()`,
`GetLoader()`, `GetCompiler()`, `GetContent()`, `GetDataProvider()`) for
reads and construct only via `NewExecutableUnit(...)`. Mid-flight mutation
of `DataProvider` (and the other fields) is no longer possible from outside
the package. Closes [#91](https://github.com/robbyt/go-polyscript/issues/91).

### Deprecated
- The twelve legacy top-level constructors (`FromRisorFile`,
Expand Down
98 changes: 18 additions & 80 deletions engines/extism/evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
content := createMockExecutable(mockPlugin, "main")

// Create a mock executable
exe := &script.ExecutableUnit{
ID: "test-json-success",
DataProvider: ctxProvider,
Content: content,
}
exe := newExe(t, "test-json-success", content, ctxProvider)

evaluator := New(handler, exe)

Expand Down Expand Up @@ -214,11 +210,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
mockPlugin.On("Close", mock.Anything).Return(nil)

content := createMockExecutable(mockPlugin, "main")
exe := &script.ExecutableUnit{
ID: "test-string-success",
DataProvider: ctxProvider,
Content: content,
}
exe := newExe(t, "test-string-success", content, ctxProvider)

evaluator := New(handler, exe)
ctx := t.Context()
Expand Down Expand Up @@ -267,9 +259,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
handler := slog.NewTextHandler(os.Stdout, nil)
ctxProvider := data.NewContextProvider(constants.EvalData)
dummyExe := &script.ExecutableUnit{
DataProvider: ctxProvider,
}
dummyExe := newExe(t, "load-input-data", nil, ctxProvider)

evaluator := New(handler, dummyExe)
ctx := t.Context()
Expand Down Expand Up @@ -340,11 +330,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
handler := slog.NewTextHandler(os.Stdout, nil)
ctxProvider := data.NewContextProvider(constants.EvalData)

exe := &script.ExecutableUnit{
ID: "test-case",
Content: mockContent,
DataProvider: ctxProvider,
}
exe := newExe(t, "test-case", mockContent, ctxProvider)

evaluator := New(handler, exe)

Expand All @@ -366,11 +352,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
handler := slog.NewTextHandler(os.Stdout, nil)
ctxProvider := data.NewContextProvider(constants.EvalData)

exe := &script.ExecutableUnit{
ID: "test-case",
Content: mockContent,
DataProvider: ctxProvider,
}
exe := newExe(t, "test-case", mockContent, ctxProvider)

evaluator := New(handler, exe)

Expand Down Expand Up @@ -403,11 +385,7 @@ func TestEvaluator_Evaluate(t *testing.T) {

// Create executor unit
handler := slog.NewTextHandler(os.Stdout, nil)
execUnit := &script.ExecutableUnit{
ID: "test-cancel",
Content: content,
DataProvider: data.NewContextProvider(constants.EvalData),
}
execUnit := newExe(t, "test-cancel", content, data.NewContextProvider(constants.EvalData))

evaluator := New(handler, execUnit)

Expand Down Expand Up @@ -442,11 +420,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
mockPlugin.On("Close", mock.Anything).Return(nil)

content := createMockExecutable(mockPlugin, "main")
exe := &script.ExecutableUnit{
ID: "test-error-exit",
DataProvider: ctxProvider,
Content: content,
}
exe := newExe(t, "test-error-exit", content, ctxProvider)

evaluator := New(handler, exe)
ctx := t.Context()
Expand Down Expand Up @@ -480,11 +454,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
mockPlugin.On("Close", mock.Anything).Return(nil)

content := createMockExecutable(mockPlugin, "main")
exe := &script.ExecutableUnit{
ID: "test-error-exit-uncapped",
DataProvider: ctxProvider,
Content: content,
}
exe := newExe(t, "test-error-exit-uncapped", content, ctxProvider)

evaluator := New(handler, exe, WithExitOutputMaxBytes(-1))
_, err := evaluator.Eval(t.Context())
Expand All @@ -508,11 +478,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
mockPlugin.On("Close", mock.Anything).Return(nil)

content := createMockExecutable(mockPlugin, "main")
exe := &script.ExecutableUnit{
ID: "test-instance-error",
DataProvider: data.NewContextProvider(constants.EvalData),
Content: content,
}
exe := newExe(t, "test-instance-error", content, data.NewContextProvider(constants.EvalData))

evaluator := New(handler, exe)
ctx := t.Context()
Expand All @@ -529,11 +495,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
mockPlugin := new(MockCompiledPlugin)
content := createMockExecutable(mockPlugin, "main")

exe := &script.ExecutableUnit{
ID: "test-load-input-error",
DataProvider: &mockErrProvider{err: errors.New("provider boom")},
Content: content,
}
exe := newExe(t, "test-load-input-error", content, &mockErrProvider{err: errors.New("provider boom")})

evaluator := New(handler, exe)
_, err := evaluator.Eval(t.Context())
Expand All @@ -551,13 +513,9 @@ func TestEvaluator_Evaluate(t *testing.T) {
mockPlugin := new(MockCompiledPlugin)
content := createMockExecutable(mockPlugin, "main")

exe := &script.ExecutableUnit{
ID: "test-convert-format-error",
DataProvider: &mockMapProvider{data: map[string]any{
"bad": make(chan int),
}},
Content: content,
}
exe := newExe(t, "test-convert-format-error", content, &mockMapProvider{data: map[string]any{
"bad": make(chan int),
}})

evaluator := New(handler, exe)
_, err := evaluator.Eval(t.Context())
Expand All @@ -578,11 +536,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
// Create a real compiler.Executable with our mock plugin
content := createMockExecutable(mockPlugin, "main")

exe := &script.ExecutableUnit{
ID: "test-nil-handler",
DataProvider: data.NewContextProvider(constants.EvalData),
Content: content,
}
exe := newExe(t, "test-nil-handler", content, data.NewContextProvider(constants.EvalData))

// Create with nil handler
evaluator := New(nil, exe)
Expand Down Expand Up @@ -809,11 +763,7 @@ func TestEvaluator_AddDataToContext(t *testing.T) {
mockPlugin.On("Close", mock.Anything).Return(nil)
content := createMockExecutable(mockPlugin, "main")

return &script.ExecutableUnit{
ID: "test-nil-provider",
DataProvider: nil,
Content: content,
}
return newExe(t, "test-nil-provider", content, nil)
},
inputs: []map[string]any{{"test": "data"}},
wantError: true,
Expand All @@ -828,11 +778,7 @@ func TestEvaluator_AddDataToContext(t *testing.T) {
mockPlugin.On("Close", mock.Anything).Return(nil)
content := createMockExecutable(mockPlugin, "main")

return &script.ExecutableUnit{
ID: "test-valid-data",
DataProvider: data.NewContextProvider(constants.EvalData),
Content: content,
}
return newExe(t, "test-valid-data", content, data.NewContextProvider(constants.EvalData))
},
inputs: []map[string]any{{"test": "data"}},
wantError: false,
Expand All @@ -846,11 +792,7 @@ func TestEvaluator_AddDataToContext(t *testing.T) {
mockPlugin.On("Close", mock.Anything).Return(nil)
content := createMockExecutable(mockPlugin, "main")

return &script.ExecutableUnit{
ID: "test-empty-input",
DataProvider: data.NewContextProvider(constants.EvalData),
Content: content,
}
return newExe(t, "test-empty-input", content, data.NewContextProvider(constants.EvalData))
},
inputs: []map[string]any{{}},
wantError: false,
Expand All @@ -877,11 +819,7 @@ func TestEvaluator_AddDataToContext(t *testing.T) {
mockProvider := &mockErrProvider{
err: errors.New("provider error"),
}
return &script.ExecutableUnit{
ID: "test-err-provider",
DataProvider: mockProvider,
Content: content,
}
return newExe(t, "test-err-provider", content, mockProvider)
},
inputs: []map[string]any{{"test": "data"}},
wantError: true,
Expand Down
71 changes: 71 additions & 0 deletions engines/extism/evaluator/exec_helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package evaluator

import (
"io"
"net/url"
"strings"
"testing"

"github.com/robbyt/go-polyscript/platform/data"
"github.com/robbyt/go-polyscript/platform/script"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

// loaderMock satisfies script/loader.Loader; only GetReader is exercised
// by NewExecutableUnit.
type loaderMock struct{ mock.Mock }

func (m *loaderMock) GetReader() (io.ReadCloser, error) {
args := m.Called()
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(io.ReadCloser), args.Error(1)
}

func (m *loaderMock) GetSourceURL() *url.URL {
args := m.Called()
if args.Get(0) == nil {
return nil
}
return args.Get(0).(*url.URL)
}

// compilerMock satisfies script.Compiler and returns a preconfigured
// ExecutableContent (which may be nil).
type compilerMock struct{ mock.Mock }

func (m *compilerMock) Compile(r io.ReadCloser) (script.ExecutableContent, error) {
args := m.Called(r)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(script.ExecutableContent), args.Error(1)
}

func (m *compilerMock) String() string { return "compilerMock" }

// newExe builds an ExecutableUnit by routing through script.NewExecutableUnit
// with a mock loader+compiler that produce the supplied content. Use a
// non-empty id when content is nil, since NewExecutableUnit otherwise tries
// to derive the id from content.GetSource() and panics on nil.
func newExe(
t *testing.T,
id string,
content script.ExecutableContent,
provider data.Provider,
) *script.ExecutableUnit {
t.Helper()
if id == "" {
id = t.Name()
}
ldr := new(loaderMock)
ldr.On("GetReader").
Return(io.NopCloser(strings.NewReader("dummy")), nil)
cmp := new(compilerMock)
cmp.On("Compile", mock.Anything).Return(content, nil)
exe, err := script.NewExecutableUnit(nil, id, ldr, cmp, provider)
require.NoError(t, err)
return exe
}
Loading