diff --git a/src/control/repos/indexer.rs b/src/control/repos/indexer.rs --- a/src/control/repos/indexer.rs +++ b/src/control/repos/indexer.rs @@ -479,6 +479,9 @@ &self, ) -> Result> + Send + 'static>> { + if self.state.ephemeral { + miette::bail!("CAR generation is not supported in ephemeral mode"); + } if !self.state.stores_record_bodies() { miette::bail!("block storage is not available in this mode"); } @@ -547,11 +550,10 @@ // sanity check: rebuilt root should match stored commit data in full-index mode let computed_root = mst.get_pointer().await.into_diagnostic()?; if computed_root != atp_commit.data { - tracing::warn!( - computed = %computed_root, - stored = %atp_commit.data, - did = %self.did, - "mst root mismatch (expected in filter mode)", + miette::bail!( + "mst root mismatch for {}: computed {computed_root} != stored {}", + self.did, + atp_commit.data, ); } store @@ -667,4 +669,63 @@ Ok(()) } + + #[tokio::test] + #[cfg(not(feature = "backlinks"))] + async fn test_generate_car_ephemeral_rejected() -> miette::Result<()> { + let tmp = tempfile::tempdir().into_diagnostic()?; + let mut config = crate::config::Config::default(); + config.database_path = tmp.path().to_path_buf(); + config.ephemeral = true; + + let state = Arc::new(AppState::new(&config)?); + let repos = ReposControl(state); + let did = Did::new("did:plc:testephemeral").into_diagnostic()?; + let handle = repos.get(&did); + let res = handle.generate_car().await; + let Err(err) = res else { + panic!("generate_car in ephemeral mode should fail"); + }; + assert!(err.to_string().contains("ephemeral mode")); + Ok(()) + } + #[tokio::test] + async fn test_generate_car_root_mismatch_fails() -> miette::Result<()> { + let tmp = tempfile::tempdir().into_diagnostic()?; + let mut config = crate::config::Config::default(); + config.database_path = tmp.path().to_path_buf(); + config.ephemeral = false; + + let state = Arc::new(AppState::new(&config)?); + let did = Did::new("did:plc:testmismatch").into_diagnostic()?; + + let dummy_cid = jacquard_repo::mst::util::compute_cid(b"dummy_root").into_diagnostic()?; + let repo_state = crate::types::RepoState { + root: Some(crate::types::v2::Commit { + version: 3, + rev: crate::db::types::DbTid::from(&jacquard_common::types::string::Tid::now_0()), + data: dummy_cid, + prev: None, + sig: bytes::Bytes::new(), + }), + ..crate::types::RepoState::synced() + }; + + let did_key = keys::repo_key(&did); + state + .db + .repos + .insert(&did_key, crate::db::ser_repo_state(&repo_state)?) + .into_diagnostic()?; + + let repos = ReposControl(state); + let handle = repos.get(&did); + + let res = handle.generate_car().await; + let Err(err) = res else { + panic!("generate_car with mismatched root should fail"); + }; + assert!(err.to_string().contains("mst root mismatch")); + Ok(()) + } }