///|
/// Golden Tests 閳?P0-011.
/// End-to-end pipeline verification with fixed inputs.
//
// 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.
//
const GOLDEN_USERS : String = "/// users table\npub struct User {\n id: Int64\n name: String\n /// user email address\n email: Option[String]\n}\n\n/// Decode a Row into a User\npub fn User::decode(row: @runtime.Row) -> Result[User, @runtime.DBError] {\n match row.index_of(\"id\") {\n Ok(_idx_0) => match row.get_int64(_idx_0) {\n Ok(id) => match row.index_of(\"name\") {\n Ok(_idx_1) => match row.get_string(_idx_1) {\n Ok(name) => match row.index_of(\"email\") {\n Ok(_idx_2) => match row.get_nullable_string(_idx_2) {\n Ok(email) => Ok({ id: id, name: name, email: email })\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n}\n\npub enum UserRole {\n Admin\n Editor\n Viewer\n}\n\n/// List all users\npub struct ListUsersRow {\n id: Int64\n name: String\n /// user email address\n email: Option[String]\n}\n\n/// Decode a Row into a ListUsersRow\npub fn ListUsersRow::decode(row: @runtime.Row) -> Result[ListUsersRow, @runtime.DBError] {\n match row.index_of(\"id\") {\n Ok(_idx_0) => match row.get_int64(_idx_0) {\n Ok(id) => match row.index_of(\"name\") {\n Ok(_idx_1) => match row.get_string(_idx_1) {\n Ok(name) => match row.index_of(\"email\") {\n Ok(_idx_2) => match row.get_nullable_string(_idx_2) {\n Ok(email) => Ok({ id: id, name: name, email: email })\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n}\n\n/// Get user by ID\npub struct GetUserRow {\n id: Int64\n name: String\n /// user email address\n email: Option[String]\n}\n\n/// Decode a Row into a GetUserRow\npub fn GetUserRow::decode(row: @runtime.Row) -> Result[GetUserRow, @runtime.DBError] {\n match row.index_of(\"id\") {\n Ok(_idx_0) => match row.get_int64(_idx_0) {\n Ok(id) => match row.index_of(\"name\") {\n Ok(_idx_1) => match row.get_string(_idx_1) {\n Ok(name) => match row.index_of(\"email\") {\n Ok(_idx_2) => match row.get_nullable_string(_idx_2) {\n Ok(email) => Ok({ id: id, name: name, email: email })\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n}\n\n/// List all users\npub fn query_list_users(db: @runtime.DB) -> Result[Array[ListUsersRow], @runtime.DBError] {\n let sql = \"SELECT id, name, email FROM users\"\n let params = []\n match db.query(sql, params) {\n Ok(iter) => match iter.collect() {\n Ok(rows) => {\n let result = []\n for row in rows {\n match ListUsersRow::decode(row) {\n Ok(v) => result.push(v)\n Err(e) => return Err(e)\n }\n }\n Ok(result)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n}\n\n/// List all users\npub fn query_list_users_tx(tx: @runtime.Transaction) -> Result[Array[ListUsersRow], @runtime.DBError] {\n let sql = \"SELECT id, name, email FROM users\"\n let params = []\n match tx.query(sql, params) {\n Ok(iter) => match iter.collect() {\n Ok(rows) => {\n let result = []\n for row in rows {\n match ListUsersRow::decode(row) {\n Ok(v) => result.push(v)\n Err(e) => return Err(e)\n }\n }\n Ok(result)\n }\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n}\n\n/// Get user by ID\npub fn query_get_user(db: @runtime.DB, id: Int64) -> Result[GetUserRow, @runtime.DBError] {\n let sql = \"SELECT id, name, email FROM users WHERE id = $1\"\n let params = [@runtime.Value::from_int64(id)]\n match db.query(sql, params) {\n Ok(rows) => match rows.collect_limited(2) {\n Ok(collected) => match collected.length() {\n 0 => Err(@runtime.db_err_no_rows())\n 1 => GetUserRow::decode(collected[0])\n _ => Err(@runtime.db_err_too_many_rows(collected.length(), sql))\n }\n Err(TooManyRows(n, _)) => Err(@runtime.db_err_too_many_rows(n, sql))\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n}\n\n/// Get user by ID\npub fn query_get_user_tx(tx: @runtime.Transaction, id: Int64) -> Result[GetUserRow, @runtime.DBError] {\n let sql = \"SELECT id, name, email FROM users WHERE id = $1\"\n let params = [@runtime.Value::from_int64(id)]\n match tx.query(sql, params) {\n Ok(rows) => match rows.collect_limited(2) {\n Ok(collected) => match collected.length() {\n 0 => Err(@runtime.db_err_no_rows())\n 1 => GetUserRow::decode(collected[0])\n _ => Err(@runtime.db_err_too_many_rows(collected.length(), sql))\n }\n Err(TooManyRows(n, _)) => Err(@runtime.db_err_too_many_rows(n, sql))\n Err(e) => Err(e)\n }\n Err(e) => Err(e)\n }\n}\n\npub fn query_delete_user(db: @runtime.DB, id: Int64) -> Result[@runtime.ExecResult, @runtime.DBError] {\n let sql = \"DELETE FROM users WHERE id = $1\"\n let params = [@runtime.Value::from_int64(id)]\n match db.exec(sql, params) {\n Ok(n) => Ok(@runtime.ExecResult::new(0L, n))\n Err(e) => Err(e)\n }\n}\n\npub fn query_delete_user_tx(tx: @runtime.Transaction, id: Int64) -> Result[@runtime.ExecResult, @runtime.DBError] {\n let sql = \"DELETE FROM users WHERE id = $1\"\n let params = [@runtime.Value::from_int64(id)]\n match tx.exec(sql, params) {\n Ok(n) => Ok(@runtime.ExecResult::new(0L, n))\n Err(e) => Err(e)\n }\n}\n"
///|
test "golden: full pipeline matches expected output" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result, content=GOLDEN_USERS)
}
///|
test "golden: realistic types 閳?bigserial閳墳nt64, pg_catalog.int8閳墳nt64, TEXT[]閳墺ption[Array[String]]" {
let req = make_realistic_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("id: Int64"), content="true")
inspect(result.contains("author_id: Int64"), content="true")
inspect(result.contains("title: String"), content="true")
inspect(result.contains("tags: Option[Array[String]]"), content="true")
inspect(result.contains("created_at: @runtime.DateTime"), content="true")
}
///|
test "golden: realistic types 閳?decode body uses typed getters for Int64 columns" {
let req = make_realistic_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("row.get_int64("), content="true")
}
///|
test "golden: deterministic output" {
let req = make_users_request()
let result1 = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
let result2 = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result1, content=result2)
}
///|
test "golden: all PG types 閳?struct field types" {
let req = make_all_types_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("is_active: Bool"), content="true")
inspect(result.contains("score: Double"), content="true")
inspect(result.contains("data: Option[Bytes]"), content="true")
inspect(result.contains("created_date: @runtime.Date"), content="true")
inspect(result.contains("created_at: @runtime.DateTime"), content="true")
inspect(
result.contains("updated_at: Option[@runtime.DateTime]"),
content="true",
)
inspect(result.contains("metadata: Option[JsonValue]"), content="true")
inspect(result.contains("price: @runtime.Decimal"), content="true")
inspect(result.contains("uid: @runtime.Uuid"), content="true")
inspect(result.contains("device_ip: Option[@runtime.IpAddr]"), content="true")
inspect(result.contains("small_num: Int64"), content="true")
inspect(result.contains("real_num: Double"), content="true")
inspect(result.contains("label: String"), content="true")
inspect(result.contains("event_time: Option[@runtime.Time]"), content="true")
inspect(
result.contains("duration: Option[@runtime.Duration]"),
content="true",
)
inspect(result.contains("tags: Option[Array[String]]"), content="true")
}
///|
test "golden: all PG types 閳?decode getters" {
let req = make_all_types_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// P0-063: all indices are now row.index_of("column_name") instead of hardcoded integers
inspect(result.contains("row.index_of("), content="true")
inspect(result.contains("row.get_int64"), content="true")
inspect(result.contains("row.get_bool"), content="true")
inspect(result.contains("row.get_double"), content="true")
inspect(result.contains("row.get_nullable_bytes"), content="true")
inspect(result.contains("row.get_date"), content="true")
inspect(result.contains("row.get_datetime"), content="true")
inspect(result.contains("row.get_nullable_datetime"), content="true")
inspect(result.contains("row.get_nullable_json"), content="true")
inspect(result.contains("row.get_decimal"), content="true")
inspect(result.contains("row.get_uuid"), content="true")
inspect(result.contains("row.get_nullable_ipaddr"), content="true")
inspect(result.contains("row.get_string"), content="true")
inspect(result.contains("row.get_nullable_time"), content="true")
inspect(result.contains("row.get_nullable_duration"), content="true")
inspect(result.contains("row.decode_nullable_array_string"), content="true")
// Also verify that column names appear in index_of calls
inspect(result.contains("row.index_of(\"id\")"), content="true")
inspect(result.contains("row.index_of(\"is_active\")"), content="true")
inspect(result.contains("row.index_of(\"score\")"), content="true")
}
///|
test "golden: all PG types 閳?Value constructors in params" {
let req = make_all_types_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("@runtime.Value::from_int64(id)"), content="true")
inspect(
result.contains("@runtime.Value::from_bool(is_active)"),
content="true",
)
inspect(result.contains("@runtime.Value::from_double(score)"), content="true")
inspect(result.contains("@runtime.Value::from_string(label)"), content="true")
}
///|
test "golden: emit_sql_as_comment=true includes SQL text in doc comments" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: true,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// Doc comment contains SQL text when enabled
inspect(result.contains("/// List all users"), content="true")
inspect(
result.contains("/// SELECT id, name, email FROM users"),
content="true",
)
}
///|
test "golden: emit_sql_as_comment=false omits SQL text from doc comments" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// Doc comment does NOT contain SQL text when disabled
inspect(result.contains("/// List all users"), content="true")
inspect(
result.contains("/// SELECT id, name, email FROM users"),
content="false",
)
// But SQL still appears in function body as string literal
inspect(
result.contains("let sql = \"SELECT id, name, email FROM users\""),
content="true",
)
}
///|
test "golden: emit_json_tags=true with json_tags_case_style=camel produces camelCase tags" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: true,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "camel",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("@json.tag(\"id\")"), content="true")
inspect(result.contains("@json.tag(\"name\")"), content="true")
inspect(result.contains("@json.tag(\"email\")"), content="true")
}
///|
test "golden: emit_json_tags=true with json_tags_case_style=pascal produces PascalCase tags" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: true,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "pascal",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("@json.tag(\"Id\")"), content="true")
inspect(result.contains("@json.tag(\"Name\")"), content="true")
inspect(result.contains("@json.tag(\"Email\")"), content="true")
}
///|
test "golden: initialisms=ID,URL affects PascalCase naming in query types" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "ID,URL",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// With initialism "ID", "get_user" type should be GetUserRow (no change, "User" is not "ID")
inspect(result.contains("GetUserRow"), content="true")
inspect(result.contains("ListUsersRow"), content="true")
}
///|
test "golden: full pipeline with emit_json_tags=true + json_tags_case_style=camel" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: true,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "ID,URL",
json_tags_case_style: "camel",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// Check JSON tags are camelCase
inspect(result.contains("@json.tag(\"id\")"), content="true")
inspect(result.contains("@json.tag(\"name\")"), content="true")
inspect(result.contains("@json.tag(\"email\")"), content="true")
// Check SQL appears in body and is referenced in doc (emit_sql_as_comment=false default)
inspect(result.contains("let sql"), content="true")
}
///|
test "golden: emit_db_tags=true adds db tag annotations" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: true,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("@db.tag(\"id\")"), content="true")
inspect(result.contains("@db.tag(\"name\")"), content="true")
}
///|
test "golden: emit_exact_table_names=true does not crash" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: true,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("pub struct User"), content="true")
inspect(result.contains("pub fn "), content="true")
}
///|
test "golden: emit_empty_slices=true generates Err(NoRows) arm" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: true,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// With emit_empty_slices=true, Err(NoRows) is converted to Ok([])
inspect(result.contains("Err(NoRows) => Ok([])"), content="true")
// Other errors are still propagated
inspect(result.contains("Err(e) => Err(e)"), content="true")
// The buggy pattern Err(e) => Ok([]) must NOT appear
inspect(result.contains("Err(e) => Ok([])"), content="false")
}
///|
test "golden: emit_methods_with_db_argument=true does not crash" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: true,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// Must at least produce valid output containing struct definitions
inspect(result.contains("pub struct "), content="true")
inspect(result.contains("pub fn "), content="true")
}
///|
test "golden: omit_unused_structs=true does not crash" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: true,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// Must at least produce valid output containing struct and function definitions
inspect(result.contains("pub struct "), content="true")
inspect(result.contains("pub fn "), content="true")
// The Users struct IS referenced (via query columns), so it should still be included
inspect(result.contains("pub struct User"), content="true")
}
///|
test "golden: multi-file output produces types.mbt and queries.mbt" {
let req = make_users_request()
let sources = generate_sources(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(sources.length(), content="2")
let types_file = sources[0]
let queries_file = sources[1]
inspect(types_file.0, content="types.mbt")
inspect(queries_file.0, content="queries.mbt")
// types.mbt contains struct definitions and decode functions
inspect(types_file.1.contains("pub struct User"), content="true")
inspect(types_file.1.contains("User::decode"), content="true")
inspect(types_file.1.contains("pub enum UserRole"), content="true")
// queries.mbt contains query functions
inspect(queries_file.1.contains("pub fn query_list_users"), content="true")
inspect(queries_file.1.contains("pub fn query_get_user"), content="true")
inspect(queries_file.1.contains("pub fn query_delete_user"), content="true")
// types.mbt does NOT contain query functions
inspect(types_file.1.contains("pub fn query_"), content="false")
// queries.mbt does NOT contain struct definitions
inspect(queries_file.1.contains("pub struct "), content="false")
// ::decode appears in query function bodies (e.g. ListUsersRow::decode(row))
// but NOT as top-level pub fn declarations
inspect(queries_file.1.contains("pub fn Users::decode"), content="false")
inspect(
queries_file.1.contains("pub fn ListUsersRow::decode"),
content="false",
)
// Generated code uses @runtime qualification; package/import belong in consumer moon.pkg
inspect(
types_file.1.contains("import \"Mairzzcllo/moonbit_sqlc_plugin/runtime\""),
content="false",
)
inspect(
queries_file.1.contains("import \"Mairzzcllo/moonbit_sqlc_plugin/runtime\""),
content="false",
)
inspect(types_file.1.contains("package testdb"), content="false")
inspect(queries_file.1.contains("package testdb"), content="false")
inspect(types_file.1.contains("@runtime.Row"), content="true")
inspect(queries_file.1.contains("@runtime.DB"), content="true")
}
///|
test "golden: column_overrides and nullable_overrides affect Users struct" {
let req = make_users_request()
let col_overrides : Array[ColumnOverride] = [
{ table: "users", column: "id", moonbit_type: "String" },
]
let null_overrides : Array[String] = ["int4"]
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: col_overrides,
nullable_overrides: null_overrides,
strict_types: false,
runtime_alias: "@runtime",
})
// Column override changes id to String, nullable override wraps it in Option
inspect(result.contains("id: Option[String]"), content="true")
// name is text, not_null, not affected by any override
inspect(result.contains("name: String"), content="true")
// email is text, nullable, not affected by any override
inspect(result.contains("email: Option[String]"), content="true")
// Query result types use nullable_override (int4 becomes Option)
inspect(result.contains("id: Option[Int64]"), content="true")
}
// === P0-065: Keyword escape golden tests ===
///|
test "golden: keyword column name 'type' is escaped to 'ty' in struct field" {
let req = make_keyword_test_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// MoonBit field uses 'ty' (escaped from 'type')
inspect(result.contains("ty: String"), content="true")
// Original column name 'type' should not appear as a field name (must be escaped)
// But it SHOULD appear in the decode index_of call and in comments
inspect(result.contains("row.index_of(\"type\")"), content="true")
// The original column name 'type' appears in the SQL string
inspect(result.contains("type FROM"), content="true")
}
///|
test "golden: keyword column name 'type' does not leak into struct field" {
let req = make_keyword_test_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// The field line should NOT contain "type:" (that would be unescaped keyword)
// Instead it should contain "ty:" (the escaped form)
inspect(result.contains("type:"), content="false")
// The decode body uses row.index_of("type") 閳?the original DB column name
inspect(result.contains("row.index_of(\"type\")"), content="true")
// Verify 'id' field is also present (sanity check)
inspect(result.contains("id: Int64"), content="true")
}
///|
test "golden: multi-file output with keyword columns works" {
let req = make_keyword_test_request()
let sources = generate_sources(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(sources.length(), content="2")
let types_file = sources[0]
// types.mbt contains the keyword-escaped struct field
inspect(types_file.1.contains("ty: String"), content="true")
// decode uses original column name
inspect(types_file.1.contains("row.index_of(\"type\")"), content="true")
}
// === P1-043: Golden tests for new generators ===
///|
test "golden: array types 閳?struct fields use Array[T]" {
let req = make_array_types_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("pub struct ArrayTest"), content="true")
inspect(result.contains("scores: "), content="true")
inspect(result.contains("labels: "), content="true")
}
///|
test "golden: array types 閳?decode uses array getters" {
let req = make_array_types_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("row.index_of(\"scores\")"), content="true")
inspect(result.contains("row.index_of(\"labels\")"), content="true")
inspect(result.contains("query_list_array_test"), content="true")
}
///|
test "golden: enum types 閳?enum definition and decode from_string" {
let req = make_enum_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("pub enum UserRole"), content="true")
inspect(result.contains("Admin"), content="true")
inspect(result.contains("Editor"), content="true")
inspect(result.contains("Viewer"), content="true")
}
///|
test "golden: enum types 閳?decode uses from_string" {
let req = make_enum_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("pub enum UserRole"), content="true")
inspect(result.contains("Admin"), content="true")
inspect(result.contains("Editor"), content="true")
inspect(result.contains("Viewer"), content="true")
inspect(result.contains("pub struct People"), content="true")
}
///|
test "golden: commands 閳?execrows generates db.execrows call" {
let req = make_commands_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("pub fn query_update_command"), content="true")
inspect(result.contains("db.execrows("), content="true")
}
///|
test "golden: commands 閳?copyfrom generates db.copyfrom call" {
let req = make_commands_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("pub fn query_copy_command"), content="true")
inspect(result.contains("db.copyfrom("), content="true")
}
///|
test "golden: commands 閳?batch generates db.batch call" {
let req = make_commands_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("pub fn query_batch_command"), content="true")
inspect(result.contains("db.batch("), content="true")
}
///|
test "golden: commands 閳?execlastid generates db.execlastid call" {
let req = make_commands_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("pub fn query_insert_command"), content="true")
inspect(result.contains("db.execlastid("), content="true")
}
///|
test "golden: multi-table 閳?generates both table structs" {
let req = make_multi_table_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
inspect(result.contains("pub struct User"), content="true")
inspect(result.contains("pub struct Post"), content="true")
inspect(result.contains("pub fn query_list_users"), content="true")
inspect(result.contains("pub fn query_list_posts"), content="true")
}
///|
test "golden: multi-table 閳?Posts struct has three columns" {
let req = make_multi_table_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// Posts struct should have id, title, author_id
inspect(result.contains("id: Int64"), content="true")
inspect(result.contains("title: String"), content="true")
inspect(result.contains("author_id: Int64"), content="true")
}
///|
test "golden: parameterized 閳?no table struct generated" {
let req = make_parameterized_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// Query functions with parameter types should exist
inspect(result.contains("pub fn query_select_one"), content="true")
inspect(result.contains("pub fn query_select_sum"), content="true")
inspect(result.contains("pub fn query_select_greeting"), content="true")
}
///|
test "golden: parameterized 閳?return types use correct mapping" {
let req = make_parameterized_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// select_one: :one 閳?Result[SelectOneRow, DBError]
inspect(result.contains("pub struct SelectOneRow"), content="true")
// select_sum: :many 閳?Result[Array[SelectSumRow], DBError]
inspect(result.contains("pub struct SelectSumRow"), content="true")
// select_greeting: :one 閳?Result[SelectGreetingRow, DBError]
inspect(result.contains("pub struct SelectGreetingRow"), content="true")
}
///|
test "golden: parameterized 閳?Int64 params generate Int64(..) constructor" {
let req = make_parameterized_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@runtime",
})
// select_one uses $1::int4
inspect(result.contains("@runtime.Value::from_int64("), content="true")
// select_greeting uses $1::text
inspect(result.contains("@runtime.Value::from_string("), content="true")
}
///|
test "golden: runtime_alias=@myrt uses custom qualifier" {
let req = make_users_request()
let result = generate_source(req, {
package_name: "testdb",
overrides: [],
emit_json_tags: false,
emit_db_tags: false,
renames: [],
emit_sql_as_comment: false,
omit_unused_structs: false,
emit_empty_slices: false,
initialisms: "",
json_tags_case_style: "snake",
emit_exact_table_names: false,
emit_methods_with_db_argument: false,
column_overrides: [],
nullable_overrides: [],
strict_types: false,
runtime_alias: "@myrt",
})
inspect(result.contains("@myrt.DB"), content="true")
inspect(result.contains("@myrt.Row"), content="true")
inspect(result.contains("@myrt.Value::from_int64"), content="true")
inspect(result.contains("@runtime.DB"), content="false")
}