Land release blocker fixes and workspace cleanup
This commit is contained in:
@@ -62,6 +62,13 @@ pub async fn insert_transcript(
|
||||
pool: &SqlitePool,
|
||||
params: &InsertTranscriptParams<'_>,
|
||||
) -> Result<()> {
|
||||
if !profile_exists(pool, params.profile_id).await? {
|
||||
return Err(KonError::StorageError(format!(
|
||||
"Insert transcript failed: unknown profile id '{}'",
|
||||
params.profile_id
|
||||
)));
|
||||
}
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO transcripts (id, text, source, profile_id, title, audio_path, duration, engine, model_id, inference_ms, sample_rate, audio_channels, format_mode, remove_fillers, british_english, anti_hallucination)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
@@ -731,6 +738,12 @@ pub async fn delete_profile(pool: &SqlitePool, id: &str) -> Result<()> {
|
||||
"Default profile cannot be deleted".into(),
|
||||
));
|
||||
}
|
||||
let transcript_count = transcript_count_for_profile(pool, id).await?;
|
||||
if transcript_count > 0 {
|
||||
return Err(KonError::StorageError(format!(
|
||||
"Cannot delete profile while {transcript_count} transcript(s) still reference it; reassign transcripts first"
|
||||
)));
|
||||
}
|
||||
sqlx::query("DELETE FROM profiles WHERE id = ?")
|
||||
.bind(id)
|
||||
.execute(pool)
|
||||
@@ -789,6 +802,23 @@ pub async fn delete_profile_term(pool: &SqlitePool, id: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn profile_exists(pool: &SqlitePool, id: &str) -> Result<bool> {
|
||||
let exists: Option<i64> = sqlx::query_scalar("SELECT 1 FROM profiles WHERE id = ? LIMIT 1")
|
||||
.bind(id)
|
||||
.fetch_optional(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Profile existence check failed: {e}")))?;
|
||||
Ok(exists.is_some())
|
||||
}
|
||||
|
||||
async fn transcript_count_for_profile(pool: &SqlitePool, id: &str) -> Result<i64> {
|
||||
sqlx::query_scalar("SELECT COUNT(*) FROM transcripts WHERE profile_id = ?")
|
||||
.bind(id)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Profile transcript count failed: {e}")))
|
||||
}
|
||||
|
||||
// --- Error Logging ---
|
||||
|
||||
/// Log a structured error to the `error_log` table.
|
||||
@@ -865,9 +895,14 @@ mod tests {
|
||||
|
||||
async fn test_pool() -> SqlitePool {
|
||||
let pool = SqlitePoolOptions::new()
|
||||
.max_connections(1)
|
||||
.connect("sqlite::memory:")
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query("PRAGMA foreign_keys = ON")
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
run_migrations(&pool).await.unwrap();
|
||||
pool
|
||||
}
|
||||
@@ -1047,11 +1082,19 @@ mod tests {
|
||||
insert_task(&pool, "p1", "Ship release", "inbox", None, None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
insert_subtask(&pool, "s1", "Final test", "p1").await.unwrap();
|
||||
insert_subtask(&pool, "s2", "Tag release", "p1").await.unwrap();
|
||||
insert_subtask(&pool, "s1", "Final test", "p1")
|
||||
.await
|
||||
.unwrap();
|
||||
insert_subtask(&pool, "s2", "Tag release", "p1")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
complete_subtask_and_check_parent(&pool, "s1").await.unwrap();
|
||||
complete_subtask_and_check_parent(&pool, "s2").await.unwrap();
|
||||
complete_subtask_and_check_parent(&pool, "s1")
|
||||
.await
|
||||
.unwrap();
|
||||
complete_subtask_and_check_parent(&pool, "s2")
|
||||
.await
|
||||
.unwrap();
|
||||
let parent = get_task_by_id(&pool, "p1").await.unwrap().unwrap();
|
||||
assert!(parent.done);
|
||||
|
||||
@@ -1353,6 +1396,72 @@ mod tests {
|
||||
assert_eq!(profiles.len(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn insert_transcript_rejects_unknown_profile_id() {
|
||||
let pool = test_pool().await;
|
||||
let err = insert_transcript(
|
||||
&pool,
|
||||
&InsertTranscriptParams {
|
||||
id: "bad-profile",
|
||||
text: "Hello",
|
||||
source: "microphone",
|
||||
profile_id: "profile-missing",
|
||||
title: None,
|
||||
audio_path: None,
|
||||
duration: 0.0,
|
||||
engine: None,
|
||||
model_id: None,
|
||||
inference_ms: None,
|
||||
sample_rate: None,
|
||||
audio_channels: None,
|
||||
format_mode: None,
|
||||
remove_fillers: false,
|
||||
british_english: false,
|
||||
anti_hallucination: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect_err("unknown profile id must be rejected");
|
||||
|
||||
let msg = err.to_string();
|
||||
assert!(msg.contains("unknown profile id"), "got: {msg}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_profile_rejects_when_transcripts_reference_it() {
|
||||
let pool = test_pool().await;
|
||||
let profile = create_profile(&pool, "Referenced", "").await.unwrap();
|
||||
insert_transcript(
|
||||
&pool,
|
||||
&InsertTranscriptParams {
|
||||
id: "referenced-transcript",
|
||||
text: "Hello",
|
||||
source: "microphone",
|
||||
profile_id: &profile.id,
|
||||
title: None,
|
||||
audio_path: None,
|
||||
duration: 0.0,
|
||||
engine: None,
|
||||
model_id: None,
|
||||
inference_ms: None,
|
||||
sample_rate: None,
|
||||
audio_channels: None,
|
||||
format_mode: None,
|
||||
remove_fillers: false,
|
||||
british_english: false,
|
||||
anti_hallucination: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let err = delete_profile(&pool, &profile.id)
|
||||
.await
|
||||
.expect_err("profile with transcript references must not delete");
|
||||
let msg = err.to_string();
|
||||
assert!(msg.contains("reassign transcripts first"), "got: {msg}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_profile_terms_happy_path() {
|
||||
let pool = test_pool().await;
|
||||
|
||||
Reference in New Issue
Block a user