///|
/// WASM integration tests — P0-026.
/// Validates the full pipeline end-to-end via the process_message entry point,
/// which is exactly what the WAT shim calls in the merged WASM module.
//
// Copyright 2026 Mairzzcllo
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
test "wasm: process_message roundtrip produces valid response" {
let req = make_users_request()
let encoded = encode_request(req)
let response_bytes = process_message(encoded)
let resp = decode_response(response_bytes)
inspect(resp.files.length(), content="2")
inspect(resp.files[0].name, content="types.mbt")
inspect(resp.files[1].name, content="queries.mbt")
}
///|
test "wasm: process_message output matches golden" {
let req = make_users_request()
let encoded = encode_request(req)
let response_bytes = process_message(encoded)
let resp = decode_response(response_bytes)
let types_source = @utf8.decode(resp.files[0].contents)
let queries_source = @utf8.decode(resp.files[1].contents)
// types.mbt contains struct definitions and decode functions
inspect(types_source.contains("pub struct User"), content="true")
inspect(types_source.contains("pub fn User::decode"), content="true")
inspect(types_source.contains("pub fn ListUsersRow::decode"), content="true")
inspect(types_source.contains("pub fn GetUserRow::decode"), content="true")
inspect(types_source.contains("pub enum UserRole"), content="true")
// types.mbt does NOT contain query function declarations
inspect(types_source.contains("pub fn query_"), content="false")
// queries.mbt contains query functions
inspect(queries_source.contains("pub fn query_list_users"), content="true")
inspect(queries_source.contains("pub fn query_get_user"), content="true")
inspect(queries_source.contains("pub fn query_delete_user"), content="true")
// queries.mbt uses @runtime qualification (import via consumer moon.pkg)
inspect(
queries_source.contains("import \"Mairzzcllo/moonbit_sqlc_plugin/runtime\""),
content="false",
)
inspect(queries_source.contains("@runtime.DB"), content="true")
}
///|
test "wasm: process_message deterministic output" {
let req = make_users_request()
let encoded = encode_request(req)
let r1 = process_message(encoded)
let r2 = process_message(encoded)
let resp1 = decode_response(r1)
let resp2 = decode_response(r2)
let combined1 = @utf8.decode(resp1.files[0].contents) +
@utf8.decode(resp1.files[1].contents)
let combined2 = @utf8.decode(resp2.files[0].contents) +
@utf8.decode(resp2.files[1].contents)
inspect(combined1, content=combined2)
}
///|
test "wasm: process_message empty request returns empty response via test entry" {
match process_message_outcome(b"") {
Ok(_) => inspect(false, content="true")
Err(msg) => inspect(msg.contains("empty"), content="true")
}
let response_bytes = process_message(b"")
let resp = decode_response(response_bytes)
inspect(resp.files.length(), content="0")
}
///|
test "wasm: encode_request roundtrip with decode_request" {
let req = make_users_request()
let encoded = encode_request(req)
let decoded = decode_request(encoded)
inspect(decoded.settings.version, content=req.settings.version)
inspect(decoded.settings.engine, content=req.settings.engine)
inspect(decoded.queries.length(), content="3")
inspect(decoded.queries[0].name, content="list_users")
inspect(decoded.queries[1].name, content="get_user")
inspect(decoded.queries[2].name, content="delete_user")
}
///|
/// P0-061: process_message returns empty response on error instead of crashing.
/// When process_request raises an error (e.g., path traversal in out_name),
/// process_message catches it and returns an empty GenerateResponse.
test "P0-061: process_message error handling returns empty response" {
let base = make_users_request()
// Create a request with a path traversal out_name to trigger a raise from validate_output_path
let settings_with_traversal = {
version: base.settings.version,
engine: base.settings.engine,
schema: base.settings.schema,
queries: base.settings.queries,
codegen: {
out: "../etc",
plugin: base.settings.codegen.plugin,
options: base.settings.codegen.options,
env: base.settings.codegen.env,
process: base.settings.codegen.process,
wasm: base.settings.codegen.wasm,
},
}
let req : GenerateRequest = {
settings: settings_with_traversal,
catalog: base.catalog,
queries: base.queries,
sqlc_version: base.sqlc_version,
plugin_options: b"package_name=test",
global_options: b"",
}
let encoded = encode_request(req)
let response_bytes = process_message(encoded)
// Should get an empty response (no files) instead of crashing
let resp = decode_response(response_bytes)
inspect(resp.files.length(), content="0")
}
///|
/// Empty protobuf buffer fails via production outcome path; test entry returns empty files.
test "P0-061: process_message empty buffer returns empty response" {
match process_message_outcome(b"") {
Ok(_) => inspect(false, content="true")
Err(msg) => inspect(msg.contains("empty"), content="true")
}
let response_bytes = process_message(b"")
let resp = decode_response(response_bytes)
inspect(resp.files.length(), content="0")
}