extern crate floz_orm as floz;
use floz_orm::prelude::*;
use floz_orm::types::*;
#[model("users")]
pub struct User {
#[col(auto, key)]
pub id: i32,
#[col(max = 100)]
pub name: Varchar,
pub age: Option<i16>,
pub bio: Text,
}
#[test]
fn user_struct_exists() {
let u = User::default();
assert_eq!(u.id, 0);
assert_eq!(u.name, "");
assert!(u.age.is_none());
assert_eq!(u.bio, "");
assert_eq!(u._dirty_flags, 0);
}
#[test]
fn user_table_exists() {
assert_eq!(UserTable::TABLE_NAME, "users");
assert_eq!(UserTable::COLUMNS, ["id", "name", "age", "bio"]);
}
#[test]
fn user_column_constants() {
assert_eq!(UserTable::id.name(), "id");
assert_eq!(UserTable::id.table(), "users");
assert_eq!(UserTable::name.name(), "name");
assert_eq!(UserTable::age.name(), "age");
assert_eq!(UserTable::bio.name(), "bio");
}
#[test]
fn user_column_dsl_operators() {
let _ = UserTable::age.gt(25i16);
let _ = UserTable::name.eq("Alice");
let _ = UserTable::name.contains("foo");
let _ = UserTable::age.is_null();
let _ = UserTable::id.in_list(vec![1, 2, 3]);
}
#[test]
fn user_dirty_tracking() {
let mut u = User::default();
assert!(!u.is_dirty());
u.set_name("Alice".to_string());
assert!(u.is_dirty());
assert!(u.is_field_dirty(1));
u.set_age(Some(30));
assert!(u.is_field_dirty(2));
assert!(!u.is_field_dirty(0)); assert!(!u.is_field_dirty(3));
u.clear_dirty();
assert!(!u.is_dirty());
}
#[test]
fn user_default_does_not_set_dirty() {
let u = User::default();
assert_eq!(u._dirty_flags, 0);
}
#[test]
fn user_serde_roundtrip() {
let mut u = User::default();
u.id = 1;
u.name = "Alice".to_string();
u.age = Some(30);
u.bio = "Hello!".to_string();
u._dirty_flags = 0xFF;
let json = serde_json::to_string(&u).unwrap();
assert!(!json.contains("dirty"));
let u2: User = serde_json::from_str(&json).unwrap();
assert_eq!(u2.name, "Alice");
assert_eq!(u2._dirty_flags, 0); }
#[model("audit.logs")]
pub struct AuditLog {
pub event: Text,
pub timestamp: TimestampTz,
}
#[test]
fn schema_qualified_table() {
assert_eq!(AuditLogTable::TABLE_NAME, "audit.logs");
}
#[test]
fn datetime_with_tz() {
let log = AuditLog::default();
let _: chrono::DateTime<chrono::Utc> = log.timestamp;
}
#[model("products")]
pub struct Product {
#[col(name = "id", key)]
pub product_id: i32,
#[col(name = "display_name", max = 255)]
pub product_name: Varchar,
}
#[test]
fn renamed_columns() {
assert_eq!(ProductTable::product_id.name(), "id");
assert_eq!(ProductTable::product_name.name(), "display_name");
let p = Product::default();
let _ = p.product_id;
let _ = p.product_name;
}
#[model("authors")]
pub struct Author {
#[col(key)]
pub id: i32,
#[col(max = 100)]
pub name: Varchar,
}
#[model("books")]
pub struct Book {
#[col(key)]
pub id: i32,
pub title: Text,
pub author_id: i32,
}
#[test]
fn multiple_models() {
assert_eq!(AuthorTable::TABLE_NAME, "authors");
assert_eq!(BookTable::TABLE_NAME, "books");
let _ = Author::default();
let _ = Book::default();
}
#[model("type_test")]
pub struct TypeTest {
pub a_int: i32,
pub b_short: i16,
pub c_bigint: i64,
pub d_real: f32,
pub e_double: f64,
pub f_text: Text,
pub g_bool: bool,
pub h_uuid: uuid::Uuid,
#[col(max = 50)]
pub i_varchar: Varchar,
pub j_binary: Bytes,
pub k_date: Date,
pub l_time: chrono::NaiveTime,
pub m_naive: Timestamp,
pub n_tz: TimestampTz,
}
#[test]
fn all_types_default() {
let t = TypeTest::default();
assert_eq!(t.a_int, 0i32);
assert_eq!(t.b_short, 0i16);
assert_eq!(t.c_bigint, 0i64);
assert_eq!(t.d_real, 0.0f32);
assert_eq!(t.e_double, 0.0f64);
assert_eq!(t.f_text, "");
assert!(!t.g_bool);
assert_eq!(t.h_uuid, uuid::Uuid::nil());
assert_eq!(t.i_varchar, "");
assert!(t.j_binary.is_empty());
}
#[model("nullable_test")]
pub struct NullableTest {
pub id: i32,
pub name: Option<Text>,
pub age: Option<i16>,
pub score: Option<f64>,
pub tag: Option<uuid::Uuid>,
}
#[test]
fn nullable_defaults() {
let t = NullableTest::default();
assert_eq!(t.id, 0);
assert!(t.name.is_none());
assert!(t.age.is_none());
assert!(t.score.is_none());
assert!(t.tag.is_none());
}
#[cfg(all(feature = "postgres", not(feature = "sqlite")))]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, sqlx::Type, utoipa::ToSchema)]
#[sqlx(type_name = "varchar", rename_all = "lowercase")]
pub enum Status {
Active,
Inactive,
}
#[cfg(all(feature = "postgres", not(feature = "sqlite")))]
impl Default for Status {
fn default() -> Self {
Status::Active
}
}
#[cfg(all(feature = "postgres", not(feature = "sqlite")))]
impl std::fmt::Display for Status {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Status::Active => write!(f, "active"),
Status::Inactive => write!(f, "inactive"),
}
}
}
#[cfg(all(feature = "postgres", not(feature = "sqlite")))]
#[model("exotics")]
pub struct WithExotics {
#[col(auto, key)]
pub id: i32,
pub data: Json,
pub meta: Option<Jsonb>,
pub status: Status,
}
#[cfg(all(feature = "postgres", not(feature = "sqlite")))]
#[test]
fn exotic_columns() {
let e = WithExotics::default();
assert_eq!(e.data, serde_json::Value::Null);
assert!(e.meta.is_none());
assert!(matches!(e.status, Status::Active));
}