use super::*;
use sea_orm::{ConnectionTrait, Database, DatabaseBackend, Statement};
use sea_orm_migration::MigratorTrait;
use time::OffsetDateTime;
#[tokio::test]
async fn detects_real_sqlite_uq_violation() {
use sea_orm::ConnectOptions;
let mut opts = ConnectOptions::new("sqlite::memory:".to_string());
opts.max_connections(1);
let db = Database::connect(opts).await.expect("connect");
crate::infra::db::Migrator::up(&db, None)
.await
.expect("migrations");
let session_id = uuid::Uuid::new_v4();
let session_type_id = uuid::Uuid::new_v4();
let now = OffsetDateTime::now_utc().to_string();
db.execute(Statement::from_string(
DatabaseBackend::Sqlite,
format!(
"INSERT INTO session_types (session_type_id, name, plugin_instance_id, \
created_at, updated_at) VALUES ('{session_type_id}', 'test', NULL, \
'{now}', '{now}')",
),
))
.await
.expect("seed session_type");
db.execute(Statement::from_string(
DatabaseBackend::Sqlite,
format!(
"INSERT INTO sessions (session_id, tenant_id, user_id, client_id, \
session_type_id, enabled_capabilities, metadata, lifecycle_state, \
share_token, deleted_at, scheduled_hard_delete_at, created_at, \
updated_at) VALUES ('{session_id}', 't', 'u', NULL, \
'{session_type_id}', NULL, NULL, 'active', NULL, NULL, NULL, '{now}', \
'{now}')",
),
))
.await
.expect("seed session");
let root_id = uuid::Uuid::new_v4();
db.execute(Statement::from_string(
DatabaseBackend::Sqlite,
format!(
"INSERT INTO messages (message_id, session_id, parent_message_id, role, \
file_ids, variant_index, is_active, is_complete, \
is_hidden_from_user, is_hidden_from_backend, metadata, created_at) \
VALUES ('{root_id}', '{session_id}', NULL, 'user', \
NULL, 0, 1, 1, 0, 0, NULL, '{now}')",
),
))
.await
.expect("seed root message");
let child_id = uuid::Uuid::new_v4();
db.execute(Statement::from_string(
DatabaseBackend::Sqlite,
format!(
"INSERT INTO messages (message_id, session_id, parent_message_id, role, \
file_ids, variant_index, is_active, is_complete, \
is_hidden_from_user, is_hidden_from_backend, metadata, created_at) \
VALUES ('{child_id}', '{session_id}', '{root_id}', 'assistant', \
NULL, 0, 1, 1, 0, 0, NULL, '{now}')",
),
))
.await
.expect("seed first child message");
let dup_id = uuid::Uuid::new_v4();
let err = db
.execute(Statement::from_string(
DatabaseBackend::Sqlite,
format!(
"INSERT INTO messages (message_id, session_id, parent_message_id, \
role, file_ids, variant_index, is_active, is_complete, \
is_hidden_from_user, is_hidden_from_backend, metadata, created_at) \
VALUES ('{dup_id}', '{session_id}', '{root_id}', 'assistant', \
NULL, 0, 1, 1, 0, 0, NULL, '{now}')",
),
))
.await
.expect_err("duplicate variant_index must violate uq_messages_session_parent_variant");
assert!(
is_variant_unique_violation(&err),
"real SQLite UQ violation must classify as retryable; got: {err:?}",
);
assert!(
matches!(err.sql_err(), Some(SqlErr::UniqueConstraintViolation(_))),
"DbErr::sql_err() must surface UniqueConstraintViolation; got: {err:?}",
);
}
#[tokio::test]
async fn detects_real_sqlite_root_uq_violation() {
use sea_orm::ConnectOptions;
let mut opts = ConnectOptions::new("sqlite::memory:".to_string());
opts.max_connections(1);
let db = Database::connect(opts).await.expect("connect");
crate::infra::db::Migrator::up(&db, None)
.await
.expect("migrations");
let session_id = uuid::Uuid::new_v4();
let session_type_id = uuid::Uuid::new_v4();
let now = OffsetDateTime::now_utc().to_string();
db.execute(Statement::from_string(
DatabaseBackend::Sqlite,
format!(
"INSERT INTO session_types (session_type_id, name, plugin_instance_id, \
created_at, updated_at) VALUES ('{session_type_id}', 'test', NULL, \
'{now}', '{now}')",
),
))
.await
.expect("seed session_type");
db.execute(Statement::from_string(
DatabaseBackend::Sqlite,
format!(
"INSERT INTO sessions (session_id, tenant_id, user_id, client_id, \
session_type_id, enabled_capabilities, metadata, lifecycle_state, \
share_token, deleted_at, scheduled_hard_delete_at, created_at, \
updated_at) VALUES ('{session_id}', 't', 'u', NULL, \
'{session_type_id}', NULL, NULL, 'active', NULL, NULL, NULL, '{now}', \
'{now}')",
),
))
.await
.expect("seed session");
let insert_root = |id: uuid::Uuid| {
Statement::from_string(
DatabaseBackend::Sqlite,
format!(
"INSERT INTO messages (message_id, session_id, parent_message_id, \
role, file_ids, variant_index, is_active, is_complete, \
is_hidden_from_user, is_hidden_from_backend, metadata, created_at) \
VALUES ('{id}', '{session_id}', NULL, 'user', \
NULL, 0, 1, 1, 0, 0, NULL, '{now}')",
),
)
};
db.execute(insert_root(uuid::Uuid::new_v4()))
.await
.expect("seed first root");
let err = db
.execute(insert_root(uuid::Uuid::new_v4()))
.await
.expect_err("second root with same variant_index must violate the root UNIQUE index");
assert!(
is_variant_unique_violation(&err),
"root variant collision must classify as retryable; got: {err:?}",
);
}
#[test]
fn rejects_non_unique_dberr() {
let err = DbErr::Custom("some unrelated error".to_string());
assert!(
!is_variant_unique_violation(&err),
"non-unique DbErr must not classify as retryable",
);
}