diff --git a/crates/db/src/vocab.rs b/crates/db/src/vocab.rs index d2b33f8..389278a 100644 --- a/crates/db/src/vocab.rs +++ b/crates/db/src/vocab.rs @@ -39,10 +39,7 @@ where .fetch_optional(executor) .await?; - Ok(row.map(|r| Vocabulary { - id: VocabularyId::from_uuid(r.get("id")), - key: r.get("key"), - })) + row.map(map_vocabulary).transpose() } /// Insert a term and its labels. Multiple statements — pass a transaction @@ -129,6 +126,13 @@ where Ok(found.map(|_| TermRef::new(term_id, vocabulary_id))) } +fn map_vocabulary(row: sqlx::postgres::PgRow) -> Result { + Ok(Vocabulary { + id: VocabularyId::from_uuid(row.try_get("id")?), + key: row.try_get("key")?, + }) +} + fn map_term(row: sqlx::postgres::PgRow) -> Result { let labels: sqlx::types::Json> = row.try_get("labels")?; diff --git a/crates/db/tests/vocab.rs b/crates/db/tests/vocab.rs index 48e32dd..69af596 100644 --- a/crates/db/tests/vocab.rs +++ b/crates/db/tests/vocab.rs @@ -73,6 +73,48 @@ async fn term_with_multilingual_labels_round_trips(pool: PgPool) { assert_eq!(listed[0].id, term_id); } +#[sqlx::test] +async fn term_with_no_labels_round_trips_empty(pool: PgPool) { + let db = Db::from_pool(pool); + let v = vocab::create_vocabulary(db.pool(), "material") + .await + .unwrap(); + + let mut tx = db.pool().begin().await.unwrap(); + let term_id = vocab::add_term( + &mut *tx, + &NewTerm { + vocabulary_id: v.id, + external_uri: None, + labels: vec![], + }, + ) + .await + .unwrap(); + tx.commit().await.unwrap(); + + let term = vocab::term_by_id(db.pool(), term_id) + .await + .unwrap() + .unwrap(); + assert!(term.labels.is_empty()); +} + +#[sqlx::test] +async fn duplicate_vocabulary_key_is_rejected(pool: PgPool) { + let db = Db::from_pool(pool); + vocab::create_vocabulary(db.pool(), "material") + .await + .unwrap(); + let err = vocab::create_vocabulary(db.pool(), "material") + .await + .unwrap_err(); + assert!( + matches!(err, sqlx::Error::Database(_)), + "expected a unique-violation DB error, got: {err:?}" + ); +} + #[sqlx::test] async fn resolve_term_checks_vocabulary_membership(pool: PgPool) { let db = Db::from_pool(pool);