Skip to content

Commit d034c26

Browse files
committed
Add Model::delete_all and timestamp upsert tests
Add a convenience Model::delete_all() method that performs an explicit full-table deletion via Self::query().delete_all(), keeping filtered deletions using query().where_*(...).delete() to avoid accidental unfiltered bulk deletes. Add a TimestampUser model and corresponding Postgres integration test: create/drop/truncate table, verify insert_or_update preserves chrono::DateTime<Tz> values for created_at/updated_at on both insert and conflict-update paths.
1 parent 00858ca commit d034c26

2 files changed

Lines changed: 91 additions & 0 deletions

File tree

src/model.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,18 @@ pub trait Model:
131131
crud::count::<Self>().await
132132
}
133133

134+
/// Delete every record for this model.
135+
///
136+
/// This is an explicit full-table operation. Filtered deletions should keep
137+
/// using `Self::query().where_*(...).delete()` so accidental unfiltered bulk
138+
/// deletes remain blocked by default.
139+
async fn delete_all() -> Result<u64>
140+
where
141+
Self: Sized,
142+
{
143+
Self::query().delete_all().await
144+
}
145+
134146
/// Check if any records exist
135147
///
136148
/// # Example

tests/postgres_integration_tests.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,18 @@ pub struct TestSoftDelete {
115115
pub deleted_at: Option<chrono::DateTime<chrono::Utc>>,
116116
}
117117

118+
#[derive(Model, PartialEq)]
119+
#[tideorm(table = "timestamp_users")]
120+
pub struct TimestampUser {
121+
#[tideorm(primary_key, auto_increment)]
122+
pub id: i64,
123+
pub email: String,
124+
pub name: String,
125+
pub login_count: i32,
126+
pub created_at: chrono::DateTime<chrono::Utc>,
127+
pub updated_at: chrono::DateTime<chrono::Utc>,
128+
}
129+
118130
// =============================================================================
119131
// SINGLE INTEGRATION TEST - Runs all scenarios sequentially
120132
// =============================================================================
@@ -138,6 +150,7 @@ async fn postgres_integration_tests() {
138150
let _ = Database::execute("DROP TABLE IF EXISTS test_soft_deletes CASCADE").await;
139151
let _ = Database::execute("DROP TABLE IF EXISTS test_posts CASCADE").await;
140152
let _ = Database::execute("DROP TABLE IF EXISTS test_users CASCADE").await;
153+
let _ = Database::execute("DROP TABLE IF EXISTS timestamp_users CASCADE").await;
141154
let _ = Database::execute("DROP TABLE IF EXISTS callback_users CASCADE").await;
142155

143156
Database::execute(
@@ -180,6 +193,21 @@ async fn postgres_integration_tests() {
180193
.await
181194
.expect("Failed to create test_soft_deletes table");
182195

196+
Database::execute(
197+
r#"
198+
CREATE TABLE timestamp_users (
199+
id BIGSERIAL PRIMARY KEY,
200+
email VARCHAR(255) NOT NULL UNIQUE,
201+
name VARCHAR(255) NOT NULL,
202+
login_count INTEGER NOT NULL DEFAULT 0,
203+
created_at TIMESTAMPTZ NOT NULL,
204+
updated_at TIMESTAMPTZ NOT NULL
205+
)
206+
"#,
207+
)
208+
.await
209+
.expect("Failed to create timestamp_users table");
210+
183211
Database::execute(
184212
r#"
185213
CREATE TABLE callback_users (
@@ -952,6 +980,57 @@ async fn postgres_integration_tests() {
952980
}
953981
println!();
954982

983+
println!("🕒 Testing: Upsert With Timestamp Columns");
984+
{
985+
let _ = Database::execute("TRUNCATE TABLE timestamp_users RESTART IDENTITY CASCADE").await;
986+
987+
let created_at = chrono::Utc::now();
988+
let updated_at = created_at + chrono::TimeDelta::minutes(15);
989+
990+
let inserted = TimestampUser::insert_or_update(
991+
TimestampUser {
992+
id: 0,
993+
email: "typed-upsert@example.com".into(),
994+
name: "Initial Timestamp User".into(),
995+
login_count: 1,
996+
created_at,
997+
updated_at,
998+
},
999+
vec!["email"],
1000+
)
1001+
.await
1002+
.expect("insert_or_update should preserve timestamp parameter types on insert");
1003+
1004+
assert_eq!(inserted.email, "typed-upsert@example.com");
1005+
assert_eq!(inserted.login_count, 1);
1006+
assert_eq!(inserted.created_at, created_at);
1007+
assert_eq!(inserted.updated_at, updated_at);
1008+
1009+
let next_updated_at = updated_at + chrono::TimeDelta::minutes(30);
1010+
let updated = TimestampUser::insert_or_update(
1011+
TimestampUser {
1012+
id: inserted.id,
1013+
email: "typed-upsert@example.com".into(),
1014+
name: "Updated Timestamp User".into(),
1015+
login_count: 2,
1016+
created_at,
1017+
updated_at: next_updated_at,
1018+
},
1019+
vec!["email"],
1020+
)
1021+
.await
1022+
.expect("insert_or_update should preserve timestamp parameter types on conflict update");
1023+
1024+
assert_eq!(updated.id, inserted.id);
1025+
assert_eq!(updated.name, "Updated Timestamp User");
1026+
assert_eq!(updated.login_count, 2);
1027+
assert_eq!(updated.created_at, created_at);
1028+
assert_eq!(updated.updated_at, next_updated_at);
1029+
1030+
println!(" ✓ insert_or_update preserves timestamp column types");
1031+
}
1032+
println!();
1033+
9551034
// =========================================================================
9561035
// BATCH UPDATE TESTS
9571036
// =========================================================================

0 commit comments

Comments
 (0)