diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91ec9fd2..12d3cc2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,10 +71,22 @@ jobs: # 连库的测试只有这个 job 跑得动——rust job 没有数据库,那些测试在那边 # 会跳过并显示绿色。不在这里跑一遍,等于写了测试却永远没执行过, # 比没写更糟:它会让人以为 SQL 有覆盖 + # 整套 store 集成测试都在这里跑,而且**没有库就失败**(UTOPIA_TEST_REQUIRE_DB): + # 从前只跑 graph_changes,其余二十几个在没有库的 backend job 里静默跳过, + # 绿色是假的(#248) - name: 连库测试 - run: cargo test -p utopia-store --test graph_changes + run: | + set -o pipefail + cargo test -p utopia-store 2>&1 | tee store-tests.log env: UTOPIA_DATABASE_URL: postgres://utopia:utopia@localhost:5432/utopia + UTOPIA_TEST_REQUIRE_DB: "1" + - name: 摘要 + if: always() + run: | + passed=$(grep -o '[0-9]* passed' store-tests.log | awk '{s+=$1} END {print s+0}') + failed=$(grep -o '[0-9]* failed' store-tests.log | awk '{s+=$1} END {print s+0}') + echo "utopia-store against Postgres: **${passed} passed**, ${failed} failed — a missing database fails this job instead of skipping" >> "$GITHUB_STEP_SUMMARY" web: runs-on: ubuntu-latest diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 425617c5..4e678bd7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,12 +65,13 @@ cd web && pnpm install --frozen-lockfile && pnpm build # build type-checks This is the easiest thing to get wrong here. A green `cargo test --workspace` does not mean everything ran. A number of tests begin like this: ```rust -let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("skipping: UTOPIA_DATABASE_URL not set"); +let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; ``` +`test_db::url()` reads `UTOPIA_DATABASE_URL`. With `UTOPIA_TEST_REQUIRE_DB=1` also set, a missing database is a failure rather than a skip — that is how the `migrations` job in CI runs the whole `utopia-store` suite, so a green run there means the SQL was exercised. Use the same guard in new tests; do not read the env var directly. + They guard what the compiler cannot see: table aliases inside SQL strings, how `NULL` behaves in a comparison, rows an `INNER JOIN` silently drops, whether a recursive CTE expands the same ancestor twice under diamond inheritance. `cargo check` and clippy say nothing about any of it. If you touched SQL under `crates/utopia-store/`, set it and run again: diff --git a/crates/utopia-store/src/lib.rs b/crates/utopia-store/src/lib.rs index 3c312517..cccaeb26 100644 --- a/crates/utopia-store/src/lib.rs +++ b/crates/utopia-store/src/lib.rs @@ -26,5 +26,6 @@ pub mod review; pub mod settings; pub mod sources; pub mod temporal; +pub mod test_db; pub mod tokens; pub mod workspaces; diff --git a/crates/utopia-store/src/test_db.rs b/crates/utopia-store/src/test_db.rs new file mode 100644 index 00000000..4812a547 --- /dev/null +++ b/crates/utopia-store/src/test_db.rs @@ -0,0 +1,29 @@ +//! 集成测试连库的入口(#248)。 +//! +//! 每个连库测试都以同一句开头:没有 `UTOPIA_DATABASE_URL` 就跳过而不是失败, +//! 本地随手 `cargo test` 不必先起库。可 CI 上也这么跳,绿色就成了假的:backend job +//! 没有库,24 个 store 集成测试全部静默返回,而有库的 migrations job 只跑了一个。 +//! +//! 所以跳过要分场合:设了 `UTOPIA_TEST_REQUIRE_DB` 的地方(CI 的连库 job), +//! 没有库就是失败——「本该跑的没跑」得看得见。 + +/// 连库测试用的数据库地址。`None` = 这次跳过。 +/// +/// 设了 `UTOPIA_TEST_REQUIRE_DB` 而没有地址时 panic:这是给 CI 的——那里跳过 +/// 等于测试根本没执行,不能显示绿色 +pub fn url() -> Option { + match std::env::var("UTOPIA_DATABASE_URL") { + Ok(u) if !u.trim().is_empty() => Some(u), + _ => { + if std::env::var_os("UTOPIA_TEST_REQUIRE_DB").is_some() { + panic!( + "UTOPIA_TEST_REQUIRE_DB is set but UTOPIA_DATABASE_URL is not: this run must not skip database-backed tests" + ); + } + eprintln!( + "跳过:未设 UTOPIA_DATABASE_URL(设 UTOPIA_TEST_REQUIRE_DB=1 让跳过变成失败)" + ); + None + } + } +} diff --git a/crates/utopia-store/tests/a_contradiction_points_upstream.rs b/crates/utopia-store/tests/a_contradiction_points_upstream.rs index 04c1020c..9b446d63 100644 --- a/crates/utopia-store/tests/a_contradiction_points_upstream.rs +++ b/crates/utopia-store/tests/a_contradiction_points_upstream.rs @@ -173,8 +173,7 @@ async fn open_contradictions( #[tokio::test] async fn a_contradiction_points_upstream() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_declared_disjointness_keeps_names_apart.rs b/crates/utopia-store/tests/a_declared_disjointness_keeps_names_apart.rs index 112f8323..1d49520b 100644 --- a/crates/utopia-store/tests/a_declared_disjointness_keeps_names_apart.rs +++ b/crates/utopia-store/tests/a_declared_disjointness_keeps_names_apart.rs @@ -127,8 +127,7 @@ async fn drift_reviews( #[tokio::test] async fn a_declared_disjointness_keeps_names_apart() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_direction_is_judged_by_range_too.rs b/crates/utopia-store/tests/a_direction_is_judged_by_range_too.rs index cf68cd9a..5cb6a4cf 100644 --- a/crates/utopia-store/tests/a_direction_is_judged_by_range_too.rs +++ b/crates/utopia-store/tests/a_direction_is_judged_by_range_too.rs @@ -107,8 +107,7 @@ async fn seed(pool: &PgPool) -> anyhow::Result { #[tokio::test] async fn direction_is_judged_by_range_too() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_fact_awaits_a_nod.rs b/crates/utopia-store/tests/a_fact_awaits_a_nod.rs index 8314b347..97c843bb 100644 --- a/crates/utopia-store/tests/a_fact_awaits_a_nod.rs +++ b/crates/utopia-store/tests/a_fact_awaits_a_nod.rs @@ -95,8 +95,7 @@ async fn live_facts(pool: &PgPool, kb: Uuid) -> anyhow::Result { #[tokio::test] async fn a_remembered_fact_waits_for_a_nod() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_mapping_is_not_a_fact.rs b/crates/utopia-store/tests/a_mapping_is_not_a_fact.rs index 88d81812..04b53e60 100644 --- a/crates/utopia-store/tests/a_mapping_is_not_a_fact.rs +++ b/crates/utopia-store/tests/a_mapping_is_not_a_fact.rs @@ -45,8 +45,7 @@ async fn fixture(pool: &PgPool) -> anyhow::Result<(Uuid, Uuid, Uuid)> { #[tokio::test] async fn one_concept_one_source_one_mapping() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; @@ -116,8 +115,7 @@ async fn one_concept_one_source_one_mapping() -> anyhow::Result<()> { #[tokio::test] async fn a_rejected_mapping_does_not_come_back() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_proof_reaches_the_sentence.rs b/crates/utopia-store/tests/a_proof_reaches_the_sentence.rs index e7139a27..231c0350 100644 --- a/crates/utopia-store/tests/a_proof_reaches_the_sentence.rs +++ b/crates/utopia-store/tests/a_proof_reaches_the_sentence.rs @@ -158,8 +158,7 @@ async fn asserted( #[tokio::test] async fn a_proof_reaches_the_sentence() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_relation_points_only_inside_its_own_kb.rs b/crates/utopia-store/tests/a_relation_points_only_inside_its_own_kb.rs index 152cd670..1ad73f8f 100644 --- a/crates/utopia-store/tests/a_relation_points_only_inside_its_own_kb.rs +++ b/crates/utopia-store/tests/a_relation_points_only_inside_its_own_kb.rs @@ -66,8 +66,7 @@ async fn plain(pool: &PgPool, kb: Uuid, key: &str) -> anyhow::Result { #[tokio::test] async fn a_relation_points_only_inside_its_own_kb() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_retired_account.rs b/crates/utopia-store/tests/a_retired_account.rs index 3e1dcc82..fd4374c8 100644 --- a/crates/utopia-store/tests/a_retired_account.rs +++ b/crates/utopia-store/tests/a_retired_account.rs @@ -32,8 +32,7 @@ async fn org_with_two_admins(pool: &PgPool) -> anyhow::Result<(Uuid, Uuid, Uuid) #[tokio::test] async fn a_retired_account_cannot_get_back_in() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; @@ -88,8 +87,7 @@ async fn a_retired_account_cannot_get_back_in() -> anyhow::Result<()> { #[tokio::test] async fn the_last_admin_and_oneself_are_protected() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_signature_holds_on_every_path.rs b/crates/utopia-store/tests/a_signature_holds_on_every_path.rs index 207909b7..b9eecb64 100644 --- a/crates/utopia-store/tests/a_signature_holds_on_every_path.rs +++ b/crates/utopia-store/tests/a_signature_holds_on_every_path.rs @@ -192,8 +192,7 @@ async fn open_signature_breaks(pool: &PgPool, kb: Uuid) -> anyhow::Result anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_source_reaches_only_where_it_was_granted.rs b/crates/utopia-store/tests/a_source_reaches_only_where_it_was_granted.rs index 400e0a9c..a9bca6b9 100644 --- a/crates/utopia-store/tests/a_source_reaches_only_where_it_was_granted.rs +++ b/crates/utopia-store/tests/a_source_reaches_only_where_it_was_granted.rs @@ -87,8 +87,7 @@ async fn seed(pool: &PgPool) -> anyhow::Result { #[tokio::test] async fn a_source_reaches_only_where_it_was_granted() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_token_is_the_person_but_not_all_of_them.rs b/crates/utopia-store/tests/a_token_is_the_person_but_not_all_of_them.rs index 51b32441..0e338fb1 100644 --- a/crates/utopia-store/tests/a_token_is_the_person_but_not_all_of_them.rs +++ b/crates/utopia-store/tests/a_token_is_the_person_but_not_all_of_them.rs @@ -66,8 +66,7 @@ async fn seed(pool: &PgPool) -> anyhow::Result { #[tokio::test] async fn a_token_is_the_person_but_not_all_of_them() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/a_viewer_never_sees_a_credential.rs b/crates/utopia-store/tests/a_viewer_never_sees_a_credential.rs index 5efcab44..269f9feb 100644 --- a/crates/utopia-store/tests/a_viewer_never_sees_a_credential.rs +++ b/crates/utopia-store/tests/a_viewer_never_sees_a_credential.rs @@ -39,8 +39,7 @@ async fn seed(pool: &PgPool) -> anyhow::Result<(Uuid, Uuid)> { #[tokio::test] async fn a_viewer_never_sees_a_credential() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/adopt_swap.rs b/crates/utopia-store/tests/adopt_swap.rs index 2f36f9d4..c6e65f30 100644 --- a/crates/utopia-store/tests/adopt_swap.rs +++ b/crates/utopia-store/tests/adopt_swap.rs @@ -12,8 +12,7 @@ use uuid::Uuid; #[tokio::test] async fn adopting_a_passive_wording_flips_subject_and_object() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/adopting_an_iri_adopts_the_shape.rs b/crates/utopia-store/tests/adopting_an_iri_adopts_the_shape.rs index 73178562..2ae36a76 100644 --- a/crates/utopia-store/tests/adopting_an_iri_adopts_the_shape.rs +++ b/crates/utopia-store/tests/adopting_an_iri_adopts_the_shape.rs @@ -15,8 +15,7 @@ use uuid::Uuid; #[tokio::test] async fn adopting_an_iri_turns_the_class_square() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/axioms_judge_the_ledger.rs b/crates/utopia-store/tests/axioms_judge_the_ledger.rs index ef351fc2..8e0dc318 100644 --- a/crates/utopia-store/tests/axioms_judge_the_ledger.rs +++ b/crates/utopia-store/tests/axioms_judge_the_ledger.rs @@ -121,8 +121,7 @@ async fn open_kinds(pool: &PgPool, kb: Uuid) -> anyhow::Result> { #[tokio::test] async fn the_ontology_is_the_only_judge() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/axioms_reach_the_database.rs b/crates/utopia-store/tests/axioms_reach_the_database.rs index 9ee60f46..8fffd4a8 100644 --- a/crates/utopia-store/tests/axioms_reach_the_database.rs +++ b/crates/utopia-store/tests/axioms_reach_the_database.rs @@ -38,8 +38,7 @@ async fn kb(pool: &PgPool) -> anyhow::Result<(Uuid, Uuid)> { #[tokio::test] async fn every_axiom_survives_the_bulk_insert() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/derived_facts_are_second_class.rs b/crates/utopia-store/tests/derived_facts_are_second_class.rs index 866d291b..16183c6e 100644 --- a/crates/utopia-store/tests/derived_facts_are_second_class.rs +++ b/crates/utopia-store/tests/derived_facts_are_second_class.rs @@ -138,8 +138,7 @@ async fn live_derived(pool: &PgPool, kb: Uuid) -> anyhow::Result anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/ended_when_unknown.rs b/crates/utopia-store/tests/ended_when_unknown.rs index 66410422..fd324130 100644 --- a/crates/utopia-store/tests/ended_when_unknown.rs +++ b/crates/utopia-store/tests/ended_when_unknown.rs @@ -106,8 +106,7 @@ async fn shape(pool: &PgPool, id: Uuid) -> anyhow::Result<(bool, Option) #[tokio::test] async fn a_relation_the_text_says_is_over_is_not_stored_as_ongoing() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; @@ -166,8 +165,7 @@ async fn a_relation_the_text_says_is_over_is_not_stored_as_ongoing() -> anyhow:: /// 一条自己都不知道何时结束的断言,没有资格给别人定结束时刻。 #[tokio::test] async fn an_already_ended_fact_is_not_treated_as_an_open_claim() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/graph_changes.rs b/crates/utopia-store/tests/graph_changes.rs index aa9f381b..9c130038 100644 --- a/crates/utopia-store/tests/graph_changes.rs +++ b/crates/utopia-store/tests/graph_changes.rs @@ -156,8 +156,7 @@ fn shape(rows: &[utopia_core::models::GraphChange]) -> Vec { #[tokio::test] async fn ledger_events_are_derived_as_specified() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/human_type_decisions.rs b/crates/utopia-store/tests/human_type_decisions.rs index bb15e61c..081fd85a 100644 --- a/crates/utopia-store/tests/human_type_decisions.rs +++ b/crates/utopia-store/tests/human_type_decisions.rs @@ -99,8 +99,7 @@ async fn source_of(pool: &PgPool, id: Uuid) -> anyhow::Result { /// 主战场:取材条件里的「现类还有子类就纳入」会把人拍过板的实体一并捞回来。 #[tokio::test] async fn type_resolution_leaves_human_decisions_alone() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; @@ -138,8 +137,7 @@ async fn type_resolution_leaves_human_decisions_alone() -> anyhow::Result<()> { /// 撤销时也就不会遇到它们,不必额外还原 `type_source`。 #[tokio::test] async fn adopting_a_new_class_does_not_claim_human_typed_entities() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; @@ -190,8 +188,7 @@ async fn adopting_a_new_class_does_not_claim_human_typed_entities() -> anyhow::R /// 就是没有」,于是下一次抽取会给它安一个类型。 #[tokio::test] async fn extraction_does_not_fill_in_a_type_a_human_left_empty() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; @@ -270,8 +267,7 @@ async fn extraction_does_not_fill_in_a_type_a_human_left_empty() -> anyhow::Resu /// 引擎自动裁决的不是。不必为此加新参数——#112 加 actor 时它就已经在那儿了。 #[tokio::test] async fn who_approved_a_retype_decides_whether_it_is_protected() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; @@ -322,8 +318,7 @@ async fn who_approved_a_retype_decides_whether_it_is_protected() -> anyhow::Resu /// `ontology.types_resolved` 审计里,后者才该决定 `type_source`。 #[tokio::test] async fn an_engine_retype_does_not_lock_the_entity_out_of_the_next_round() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/miss_dismissal.rs b/crates/utopia-store/tests/miss_dismissal.rs index a3a5b951..01530113 100644 --- a/crates/utopia-store/tests/miss_dismissal.rs +++ b/crates/utopia-store/tests/miss_dismissal.rs @@ -44,8 +44,7 @@ fn count_of(rows: &[utopia_core::models::OntologyMiss], key: &str) -> Option anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/no_predicate_still_shows.rs b/crates/utopia-store/tests/no_predicate_still_shows.rs index f5637c0d..27f0ff11 100644 --- a/crates/utopia-store/tests/no_predicate_still_shows.rs +++ b/crates/utopia-store/tests/no_predicate_still_shows.rs @@ -153,8 +153,7 @@ async fn seed(pool: &PgPool) -> anyhow::Result { #[tokio::test] async fn a_fact_without_a_predicate_is_still_visible_everywhere() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/proposal_counts.rs b/crates/utopia-store/tests/proposal_counts.rs index 8bc88d1e..95a6db80 100644 --- a/crates/utopia-store/tests/proposal_counts.rs +++ b/crates/utopia-store/tests/proposal_counts.rs @@ -130,8 +130,7 @@ async fn seed(pool: &PgPool) -> anyhow::Result { #[tokio::test] async fn spread_counts_all_evidence_while_rewrite_count_stays_on_the_backlog() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; @@ -164,8 +163,7 @@ async fn spread_counts_all_evidence_while_rewrite_count_stays_on_the_backlog() - /// 丢了(`m.kind = relation_type`),clippy 全绿,任务在运行时才炸。 #[tokio::test] async fn document_ids_come_back_for_each_wording() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/the_backstop_can_be_raised.rs b/crates/utopia-store/tests/the_backstop_can_be_raised.rs index 3bc916c1..2eeee5d1 100644 --- a/crates/utopia-store/tests/the_backstop_can_be_raised.rs +++ b/crates/utopia-store/tests/the_backstop_can_be_raised.rs @@ -19,8 +19,7 @@ use sqlx::PgPool; /// 逐个试边界而不是只试一个:漂移可能出在任何一档上,而这几次查询很便宜。 #[tokio::test] async fn every_value_rust_accepts_the_database_accepts_too() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; @@ -61,8 +60,7 @@ async fn every_value_rust_accepts_the_database_accepts_too() -> anyhow::Result<( /// 有行的库跑一个数、空表的库跑另一个数,而两者都不报错。 #[tokio::test] async fn the_two_defaults_say_the_same_number() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?; diff --git a/crates/utopia-store/tests/the_floor_under_retrieval.rs b/crates/utopia-store/tests/the_floor_under_retrieval.rs index a0d408f9..900e0cb1 100644 --- a/crates/utopia-store/tests/the_floor_under_retrieval.rs +++ b/crates/utopia-store/tests/the_floor_under_retrieval.rs @@ -82,8 +82,7 @@ async fn seed(pool: &PgPool) -> anyhow::Result<(Uuid, Vec<(&'static str, Uuid)>) #[tokio::test] async fn a_retrieved_leaf_brings_its_ancestors() -> anyhow::Result<()> { - let Ok(url) = std::env::var("UTOPIA_DATABASE_URL") else { - eprintln!("跳过:未设 UTOPIA_DATABASE_URL"); + let Some(url) = utopia_store::test_db::url() else { return Ok(()); }; let pool = PgPool::connect(&url).await?;