diff --git a/mock/Makefile b/mock/Makefile index 8e7dd3a..57d011b 100644 --- a/mock/Makefile +++ b/mock/Makefile @@ -1,5 +1,3 @@ build: tinygo build -target wasi -o plugin.wasm main.go -test: - extism call plugin.wasm greet --input "world" --wasi diff --git a/mock/main.go b/mock/main.go index 1f429f3..8c329b1 100644 --- a/mock/main.go +++ b/mock/main.go @@ -41,4 +41,22 @@ func reflectByteBufferHost(kPtr uint64) uint64 { return kRet.Offset() } +//go:export noInputWithOutputHost +func noInputWithOutputHost() uint64 { + mem := pdk.AllocateString("noInputWithOutputHost") + return mem.Offset() +} + +//go:export withInputNoOutputHost +func withInputNoOutputHost(ptr uint64) { + mem := pdk.FindMemory(ptr) + data := string(mem.ReadBytes()) + if data != "42" { // JSON-encoded string value from the caller + panic("failed to read expected value") + } +} + +//go:export noInputNoOutputHost +func noInputNoOutputHost() {} + func main() {} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7581d67 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "xtp-bindgen-test", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/runner/package-lock.json b/runner/package-lock.json index 985d50e..e763c1c 100644 --- a/runner/package-lock.json +++ b/runner/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "BSD-3-Clause", "dependencies": { - "@dylibso/xtp-test": "^0.0.8" + "@dylibso/xtp-test": "^0.0.9" }, "devDependencies": { "@extism/js-pdk": "^1.0.0", @@ -18,9 +18,9 @@ } }, "node_modules/@dylibso/xtp-test": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@dylibso/xtp-test/-/xtp-test-0.0.8.tgz", - "integrity": "sha512-XToSxuKdY4M2DFTPm09/6CtANeKmmNZyG8YLxS60bIRTmLV7mDyrILU+kXtCES5Ld041TGEtj3X1fXWNACRhzA==" + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@dylibso/xtp-test/-/xtp-test-0.0.9.tgz", + "integrity": "sha512-FOw7/CgPfFHV3VmWEzqNbyuqjPcS+whvDNiuw66ODHBdlclybOe1JwJv5EMaYMoVgNYvMfJUDIox0Emttze9OQ==" }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", diff --git a/runner/package.json b/runner/package.json index dedf459..b8600d8 100644 --- a/runner/package.json +++ b/runner/package.json @@ -15,6 +15,6 @@ "@extism/js-pdk": "^1.0.0" }, "dependencies": { - "@dylibso/xtp-test": "^0.0.8" + "@dylibso/xtp-test": "^0.0.9" } } diff --git a/runner/src/index.ts b/runner/src/index.ts index eb42dfb..253acfd 100644 --- a/runner/src/index.ts +++ b/runner/src/index.ts @@ -23,37 +23,82 @@ const KitchenSink = { }; export function test() { - let input = JSON.stringify(KitchenSink); - let output = JSON.parse(Test.callString("reflectJsonObject", input)); + Test.group("schema v1-draft encoding types", () => { + let input = JSON.stringify(KitchenSink); + let output: typeof KitchenSink = Test.call("reflectJsonObject", input) + .json(); - matchIdenticalTopLevel(output); - matchIdenticalEmbedded(output.anEmbeddedObject); - output.anEmbeddedObjectArray.forEach(matchIdenticalEmbedded); + matchIdenticalTopLevel(output); + matchIdenticalEmbedded(output.anEmbeddedObject); + output.anEmbeddedObjectArray.forEach(matchIdenticalEmbedded); - // dates and JSON encodings between languages are a little fuzzy. - // so, rather than test stringified equality, we test the value of - // the date in various forms. - matchDate(output); + // dates and JSON encodings between languages are a little fuzzy. + // so, rather than test stringified equality, we test the value of + // the date in various forms. + matchDate(output); - Test.assertEqual( - "reflectJsonObject preserved optional field semantics", - output.anOptionalString, - KitchenSink.anOptionalString, - ); + Test.assertEqual( + "reflectJsonObject preserved optional field semantics", + output.anOptionalString, + KitchenSink.anOptionalString, + ); - let inputS = KitchenSink.aString; - let outputS = Test.callString("reflectUtf8String", inputS); - Test.assertEqual("reflectUtf8String preserved the string", outputS, inputS); + let inputS = KitchenSink.aString; + let outputS = Test.call("reflectUtf8String", inputS).text(); + Test.assertEqual("reflectUtf8String preserved the string", outputS, inputS); - let inputB = (new TextEncoder()).encode(KitchenSink.aString).buffer; - let outputB = Test.callBuffer("reflectByteBuffer", inputB); + let inputB = (new TextEncoder()).encode(KitchenSink.aString).buffer; + let outputB = Test.call("reflectByteBuffer", inputB).arrayBuffer(); - // TODO compare the bytes - Test.assertEqual( - "reflectByteBuffer preserved the buffer length", - outputB.byteLength, - inputB.byteLength, - ); + // TODO compare the bytes + Test.assertEqual( + "reflectByteBuffer preserved the buffer length", + outputB.byteLength, + inputB.byteLength, + ); + }); + + Test.group("check signature and type variations", () => { + // should call a the `noInputWithOutputHost` host function passing it + // a string "noInputWithOutputHost" which it should return + let noInputWithOutputOutput = Test.call( + "noInputWithOutput", + undefined, + ).text(); + Test.assertEqual( + "noInputWithOutput returns expected output", + noInputWithOutputOutput, + "noInputWithOutputHost", + ); + + // should call the `withInputNoOutputHost` host function passing it + // JSON-encoded number 42, which the host function checks for and panics + // if it is not that JSON value + try { + let withInputNoOutputOutput = Test.call( + "withInputNoOutput", + JSON.stringify(42), + ); + Test.assert( + "withInputNoOutput runs and returns no output", + withInputNoOutputOutput.isEmpty(), + `expected empty output, got: ${withInputNoOutputOutput.text()}`, + ); + } catch (e: any) { + Test.assert( + "withInputNoOutput runs without panic", + false, + `host function (withInputNoOutputHost) panic with unexpected argument, must be JSON-encoded 42: ${e.message}`, + ); + } + + const noInputNoOutput = Test.call("noInputNoOutput", undefined); + Test.assert( + "noInputNoOutput is called successfully", + noInputNoOutput.isEmpty(), + `expected empty output, got: ${noInputNoOutput.text()}`, + ); + }); return 0; } diff --git a/schema.yaml b/schema.yaml index 001850d..c35dbdb 100644 --- a/schema.yaml +++ b/schema.yaml @@ -5,24 +5,24 @@ exports: This function takes a KitchenSinkObject and returns a KitchenSinkObject. It should come out the same way it came in. codeSamples: - - lang: typescript - source: | - // pass this through the host function and return it back - return reflectJsonObjectHost(input) - - lang: go - source: |- - reflect, err := ReflectJsonObjectHost(input) - if err != nil { - return KitchenSinkObject{}, err - } + - lang: typescript + source: | + // pass this through the host function and return it back + return reflectJsonObjectHost(input) + - lang: go + source: |- + reflect, err := ReflectJsonObjectHost(input) + if err != nil { + return input, err + } - return *reflect, err - - lang: csharp - source: |- - return Host.ReflectJsonObjectHost(input); - - lang: zig - source: | - return Host.reflectJsonObjectHost(input); + return *reflect, err + - lang: csharp + source: |- + return Host.ReflectJsonObjectHost(input); + - lang: zig + source: | + return Host.reflectJsonObjectHost(input); input: contentType: application/json $ref: "#/components/schemas/KitchenSinkObject" @@ -34,23 +34,23 @@ exports: This function takes a string and returns it. Should come out the same way it came in. codeSamples: - - lang: typescript - source: | - return reflectUtf8StringHost(input) - - lang: go - source: |- - reflect, err := ReflectUtf8StringHost(input) - if err != nil { - return "", err - } + - lang: typescript + source: | + return reflectUtf8StringHost(input) + - lang: go + source: |- + reflect, err := ReflectUtf8StringHost(input) + if err != nil { + return "", err + } - return *reflect, nil - - lang: csharp - source: |- - return Host.ReflectUtf8StringHost(input); - - lang: zig - source: | - return Host.reflectUtf8StringHost(input); + return *reflect, nil + - lang: csharp + source: |- + return Host.ReflectUtf8StringHost(input); + - lang: zig + source: | + return Host.reflectUtf8StringHost(input); input: type: string description: The input string @@ -64,23 +64,23 @@ exports: This function takes a byte buffer and returns it. Should come out the same way it came in. codeSamples: - - lang: typescript - source: | - return reflectByteBufferHost(input) - - lang: go - source: |- - reflect, err := ReflectByteBufferHost(input) - if err != nil { - return nil, err - } + - lang: typescript + source: | + return reflectByteBufferHost(input) + - lang: go + source: |- + reflect, err := ReflectByteBufferHost(input) + if err != nil { + return nil, err + } - return reflect, nil - - lang: csharp - source: |- - return Host.ReflectByteBufferHost(input); - - lang: zig - source: | - return Host.reflectByteBufferHost(input); + return reflect, nil + - lang: csharp + source: |- + return Host.ReflectByteBufferHost(input); + - lang: zig + source: | + return Host.reflectByteBufferHost(input); input: contentType: application/x-binary type: buffer @@ -89,6 +89,61 @@ exports: contentType: application/x-binary type: buffer description: The output byte buffer + + noInputWithOutput: + description: a function that takes no input, but returns an output + output: + contentType: text/plain; charset=utf-8 + type: string + codeSamples: + - lang: typescript + source: | + return noInputWithOutputHost(); + - lang: zig + source: |- + return Host.noInputWithOutputHost(); + - lang: go + source: | + output, err := NoInputWithOutputHost() + if err != nil { + return *output, err + } + + return *output, nil + + withInputNoOutput: + description: a function that takes input, but returns no output + input: + contentType: application/json + type: number + codeSamples: + - lang: typescript + source: | + return withInputNoOutputHost(input); + - lang: zig + source: |- + return Host.withInputNoOutputHost(input); + - lang: go + source: | + err := WithInputNoOutputHost(input) + if err != nil { + return err + } + return nil + + noInputNoOutput: + description: a function that takes no input, and returns no output + codeSamples: + - lang: typescript + source: |- + noInputNoOutputHost(); + - lang: zig + source: |- + return Host.noInputNoOutputHost(); + - lang: go + source: | + return NoInputNoOutputHost() + imports: reflectJsonObjectHost: description: | @@ -125,6 +180,22 @@ imports: contentType: application/x-binary type: buffer description: The output byte buffer + + noInputWithOutputHost: + description: a function that takes no input, but returns an output + output: + contentType: text/plain; charset=utf-8 + type: string + + withInputNoOutputHost: + description: a function that takes input, but returns no output + input: + contentType: application/json + type: number + + noInputNoOutputHost: + description: a function that takes no input, and returns no output + components: schemas: EmbeddedObject: @@ -158,9 +229,9 @@ components: description: A string enum type: string enum: - - option1 - - option2 - - option3 + - option1 + - option2 + - option3 KitchenSinkObject: description: A json object with every type of property properties: