diff --git a/.github/workflows/native-bazel.yaml b/.github/workflows/native-bazel.yaml index b7d80606a..d4d6b2cc9 100644 --- a/.github/workflows/native-bazel.yaml +++ b/.github/workflows/native-bazel.yaml @@ -28,6 +28,7 @@ jobs: name: ${{ matrix.os }} runs-on: ${{ matrix.os }} timeout-minutes: 30 + environment: production steps: - name: Checkout uses: >- # v4.2.2 @@ -59,3 +60,5 @@ jobs: exit 1 fi shell: bash + env: + NATIVELINK_TEST_MONGO_URL: ${{ secrets.NATIVELINK_TEST_MONGO_URL}} diff --git a/.github/workflows/native-cargo.yaml b/.github/workflows/native-cargo.yaml index 47a67264f..efd3e2ef5 100644 --- a/.github/workflows/native-cargo.yaml +++ b/.github/workflows/native-cargo.yaml @@ -32,6 +32,7 @@ jobs: name: ${{ matrix.os }} / ${{ matrix.toolchain }} runs-on: ${{ matrix.os }} timeout-minutes: 30 + environment: production steps: - name: Checkout uses: >- # v4.2.2 @@ -46,6 +47,8 @@ jobs: - name: Test on ${{ runner.os }} run: cargo test --all --profile=smol + env: + NATIVELINK_TEST_MONGO_URL: ${{ secrets.NATIVELINK_TEST_MONGO_URL}} # Not a default target, but need to make sure we don't actually break it - name: Test worker_find_logging diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml index 50540916b..2176fe0d8 100644 --- a/.github/workflows/nix.yaml +++ b/.github/workflows/nix.yaml @@ -58,6 +58,7 @@ jobs: name: Cargo Dev / ${{ matrix.os }} runs-on: ${{ matrix.os }} timeout-minutes: 45 + environment: production steps: - name: Checkout uses: >- # v4.2.2 @@ -70,6 +71,8 @@ jobs: run: > nix develop --impure --command bash -c "cargo test --all --profile=smol" + env: + NATIVELINK_TEST_MONGO_URL: ${{ secrets.NATIVELINK_TEST_MONGO_URL }} installation: strategy: diff --git a/.github/workflows/sanitizers.yaml b/.github/workflows/sanitizers.yaml index 11a44aea7..4365ec0ad 100644 --- a/.github/workflows/sanitizers.yaml +++ b/.github/workflows/sanitizers.yaml @@ -29,6 +29,7 @@ jobs: name: ${{ matrix.sanitizer }} / ${{ matrix.os }} runs-on: ${{ matrix.os }} timeout-minutes: 30 + environment: production steps: - name: Checkout uses: >- # v4.2.2 @@ -44,3 +45,5 @@ jobs: - name: Run Bazel tests run: bazel test --config=${{ matrix.sanitizer }} --verbose_failures //... shell: bash + env: + NATIVELINK_TEST_MONGO_URL: ${{ secrets.NATIVELINK_TEST_MONGO_URL}} diff --git a/nativelink-store/BUILD.bazel b/nativelink-store/BUILD.bazel index f2dd1bcb1..b2563647b 100644 --- a/nativelink-store/BUILD.bazel +++ b/nativelink-store/BUILD.bazel @@ -110,6 +110,11 @@ rust_test_suite( "tests/size_partitioning_store_test.rs", "tests/verify_store_test.rs", ], + env_inherit = [ + "NATIVELINK_TEST_MONGO_URL", + "CI", + "GITHUB_ACTIONS", + ], proc_macro_deps = [ "//nativelink-macro", "@crates//:async-trait", diff --git a/nativelink-store/tests/mongo_store_test.rs b/nativelink-store/tests/mongo_store_test.rs index cdbfdb142..518dc0360 100644 --- a/nativelink-store/tests/mongo_store_test.rs +++ b/nativelink-store/tests/mongo_store_test.rs @@ -83,11 +83,13 @@ pub struct TestMongoHelper { pub database_name: String, } +fn in_ci() -> bool { + std::env::var("CI").is_ok() || std::env::var("GITHUB_ACTIONS").is_ok() +} + fn non_ci_test_store_access() { // Check if this is a local development environment without credentials - let is_ci = std::env::var("CI").is_ok() || std::env::var("GITHUB_ACTIONS").is_ok(); - - if !is_ci { + if !in_ci() { eprintln!("\nšŸ”’ MongoDB tests require access to the NativeLink test database."); eprintln!(" For local development access, please email: marcus@tracemachina.com"); eprintln!(" "); @@ -177,18 +179,64 @@ async fn create_test_store() -> Result, Error> { ExperimentalMongoStore::new(spec).await } +enum MongoDBState { + Ok(TestMongoHelper), + Broken(Error), + Skipping, +} + /// Utility to check if `MongoDB` is available for testing. /// Returns an error that can be used to skip tests when `MongoDB` is not available. -async fn check_mongodb_available() -> Result<(), Error> { - TestMongoHelper::new_or_skip().await.map(|_| ()) +async fn check_mongodb_available() -> MongoDBState { + match TestMongoHelper::new_or_skip().await { + Ok(helper) => MongoDBState::Ok(helper), + Err(err) if err.code == Code::Unavailable && !in_ci() => { + eprintln!("Skipping MongoDB test - MongoDB not available"); + MongoDBState::Skipping + } + Err(err) => { + eprintln!("Mongo test error: {err}"); + MongoDBState::Broken(err) + } + } +} + +#[nativelink_test] +async fn connect_with_username_and_password() -> Result<(), Error> { + match check_mongodb_available().await { + MongoDBState::Ok(_helper) => {} + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } + } + + let spec = create_test_spec(); + let store = ExperimentalMongoStore::new(spec) + .await + .expect("Working store"); + let digest = DigestInfo::try_new(VALID_HASH1, 2)?; + let result = store.has(digest).await?; + assert!( + result.is_some(), + "Expected mongo store to have hash: {VALID_HASH1}", + ); + Ok(()) } #[nativelink_test] async fn upload_and_get_data() -> Result<(), Error> { // Create test helper with automatic cleanup - let Ok(helper) = TestMongoHelper::new_or_skip().await else { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + let helper = match check_mongodb_available().await { + MongoDBState::Ok(helper) => helper, + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } }; // Construct the data we want to send. @@ -252,9 +300,14 @@ async fn upload_and_get_data_without_prefix() -> Result<(), Error> { #[nativelink_test] async fn upload_empty_data() -> Result<(), Error> { // Skip test if MongoDB is not available - if check_mongodb_available().await.is_err() { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + match check_mongodb_available().await { + MongoDBState::Ok(_helper) => {} + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } } let data = Bytes::from_static(b""); @@ -277,9 +330,14 @@ async fn upload_empty_data() -> Result<(), Error> { #[allow(clippy::items_after_statements)] async fn test_large_downloads_are_chunked() -> Result<(), Error> { // Skip test if MongoDB is not available - if check_mongodb_available().await.is_err() { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + match check_mongodb_available().await { + MongoDBState::Ok(_helper) => {} + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } } const READ_CHUNK_SIZE: usize = 1024; @@ -316,9 +374,14 @@ async fn test_large_downloads_are_chunked() -> Result<(), Error> { #[allow(clippy::items_after_statements)] async fn yield_between_sending_packets_in_update() -> Result<(), Error> { // Skip test if MongoDB is not available - if check_mongodb_available().await.is_err() { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + match check_mongodb_available().await { + MongoDBState::Ok(_helper) => {} + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } } const READ_CHUNK_SIZE: usize = 1024; @@ -372,9 +435,14 @@ async fn yield_between_sending_packets_in_update() -> Result<(), Error> { #[nativelink_test] async fn zero_len_items_exist_check() -> Result<(), Error> { // Skip test if MongoDB is not available - if check_mongodb_available().await.is_err() { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + match check_mongodb_available().await { + MongoDBState::Ok(_helper) => {} + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } } let digest = DigestInfo::try_new(VALID_HASH1, 0)?; @@ -427,9 +495,14 @@ async fn test_empty_connection_string() -> Result<(), Error> { #[nativelink_test] async fn test_default_values() -> Result<(), Error> { // Skip test if MongoDB is not available - if check_mongodb_available().await.is_err() { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + match check_mongodb_available().await { + MongoDBState::Ok(_helper) => {} + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } } let mut spec = create_test_spec(); @@ -464,9 +537,14 @@ async fn test_default_values() -> Result<(), Error> { #[nativelink_test] async fn dont_loop_forever_on_empty() -> Result<(), Error> { // Skip test if MongoDB is not available - if check_mongodb_available().await.is_err() { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + match check_mongodb_available().await { + MongoDBState::Ok(_helper) => {} + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } } let store = create_test_store().await?; @@ -502,9 +580,14 @@ async fn dont_loop_forever_on_empty() -> Result<(), Error> { #[nativelink_test] async fn test_partial_reads() -> Result<(), Error> { // Create test helper with automatic cleanup - let Ok(helper) = TestMongoHelper::new_or_skip().await else { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + let helper = match check_mongodb_available().await { + MongoDBState::Ok(helper) => helper, + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } }; let data = Bytes::from_static(b"Hello, MongoDB World!"); @@ -620,9 +703,14 @@ async fn test_database_lifecycle() -> Result<(), Error> { #[allow(clippy::use_debug)] async fn create_ten_cas_entries() -> Result<(), Error> { // Create test helper with automatic cleanup - let Ok(helper) = TestMongoHelper::new_or_skip().await else { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + let helper = match check_mongodb_available().await { + MongoDBState::Ok(helper) => helper, + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } }; eprintln!( @@ -848,9 +936,14 @@ impl SchedulerStoreDecodeTo for TestSchedulerKey { #[nativelink_test] async fn test_scheduler_store_operations() -> Result<(), Error> { // Create test helper - let Ok(helper) = TestMongoHelper::new_or_skip().await else { - eprintln!("Skipping MongoDB test - MongoDB not available"); - return Ok(()); + let helper = match check_mongodb_available().await { + MongoDBState::Ok(helper) => helper, + MongoDBState::Broken(err) => { + return Err(err); + } + MongoDBState::Skipping => { + return Ok(()); + } }; eprintln!(