use serde::{Deserialize, Serialize};
use umbral_openapi::OpenApiPlugin;
use umbral_rest::RestPlugin;
#[derive(Debug, Clone, sqlx::FromRow, Serialize, Deserialize, umbral::orm::Model)]
#[umbral(table = "gc_widget")]
pub struct GcWidget {
pub id: i64,
pub name: String,
pub secret_token: String,
}
#[tokio::test]
async fn gen_client_writes_typed_files_and_respects_hide() {
let settings = umbral::Settings::from_env().expect("figment defaults");
let pool = umbral::db::connect_sqlite("sqlite::memory:")
.await
.expect("in-memory sqlite");
let app = umbral::App::builder()
.settings(settings)
.database("default", pool)
.model::<GcWidget>()
.plugin(RestPlugin::default().hide("gc_widget", "secret_token"))
.plugin(OpenApiPlugin::default())
.build_deferred()
.expect("App::build_deferred");
let out = tempfile::tempdir().expect("tempdir");
let out_path = out.path().to_str().unwrap().to_string();
let argv: Vec<std::ffi::OsString> = ["umbral", "gen-client", "--out", &out_path]
.iter()
.map(std::ffi::OsString::from)
.collect();
umbral_cli::dispatch_with_argv(app, argv)
.await
.expect("gen-client dispatch");
let dts = std::fs::read_to_string(out.path().join("client.d.ts")).expect("client.d.ts written");
let js = std::fs::read_to_string(out.path().join("client.js")).expect("client.js written");
assert!(js.contains("export class Umbral {"), "got:\n{js}");
assert!(
!js.contains("import "),
"client.js must be self-contained; got:\n{js}"
);
assert!(dts.contains("export interface GcWidget {"), "got:\n{dts}");
let row = dts
.split("export interface GcWidget {")
.nth(1)
.and_then(|s| s.split('}').next())
.expect("GcWidget interface");
assert!(
!row.contains("secret_token"),
"a hidden column must not appear in the response row type; got:\n{row}",
);
assert!(
row.contains("name"),
"a visible column must be in the row; got:\n{row}"
);
let create = dts
.split("export interface GcWidgetCreate {")
.nth(1)
.and_then(|s| s.split("}\n").next())
.expect("GcWidgetCreate block");
assert!(
create.contains("secret_token"),
"a hidden-but-writable column stays in the create DTO; got:\n{create}",
);
assert!(
dts.contains(r#" "gc_widget": { row: GcWidget;"#),
"the client must map the exposed table; got:\n{dts}",
);
assert!(
dts.contains("Base path: /api"),
"default REST base path; got:\n{dts}"
);
let filters = dts
.split("export interface GcWidgetFilters {")
.nth(1)
.and_then(|s| s.split("}\n").next())
.expect("GcWidgetFilters block");
assert!(
!filters.contains("secret_token"),
"a hidden column must not be filterable; got:\n{filters}",
);
assert!(
filters.contains(r#""name""#),
"a visible column must be; got:\n{filters}"
);
}