@@ -53,6 +53,8 @@ fn create_test_spec_with_key_prefix(key_prefix: Option<String>) -> ExperimentalM
5353
5454 ExperimentalMongoSpec {
5555 connection_string : std:: env:: var ( "NATIVELINK_TEST_MONGO_URL" ) . unwrap_or ( default_connection) ,
56+ username : String :: new ( ) ,
57+ password : String :: new ( ) ,
5658 database : format ! (
5759 "nltest_{}_{}" ,
5860 timestamp,
@@ -83,11 +85,13 @@ pub struct TestMongoHelper {
8385 pub database_name : String ,
8486}
8587
88+ fn in_ci ( ) -> bool {
89+ std:: env:: var ( "CI" ) . is_ok ( ) || std:: env:: var ( "GITHUB_ACTIONS" ) . is_ok ( )
90+ }
91+
8692fn non_ci_test_store_access ( ) {
8793 // Check if this is a local development environment without credentials
88- let is_ci = std:: env:: var ( "CI" ) . is_ok ( ) || std:: env:: var ( "GITHUB_ACTIONS" ) . is_ok ( ) ;
89-
90- if !is_ci {
94+ if !in_ci ( ) {
9195 eprintln ! ( "\n 🔒 MongoDB tests require access to the NativeLink test database." ) ;
9296 eprintln ! ( " For local development access, please email: [email protected] " ) ; 9397 eprintln ! ( " " ) ;
@@ -177,18 +181,68 @@ async fn create_test_store() -> Result<Arc<ExperimentalMongoStore>, Error> {
177181 ExperimentalMongoStore :: new ( spec) . await
178182}
179183
184+ enum MongoDBState {
185+ Ok ( TestMongoHelper ) ,
186+ Broken ( Error ) ,
187+ Skipping ,
188+ }
189+
180190/// Utility to check if `MongoDB` is available for testing.
181191/// Returns an error that can be used to skip tests when `MongoDB` is not available.
182- async fn check_mongodb_available ( ) -> Result < ( ) , Error > {
183- TestMongoHelper :: new_or_skip ( ) . await . map ( |_| ( ) )
192+ async fn check_mongodb_available ( ) -> MongoDBState {
193+ match TestMongoHelper :: new_or_skip ( ) . await {
194+ Ok ( helper) => MongoDBState :: Ok ( helper) ,
195+ Err ( err) if err. code == Code :: Unavailable && !in_ci ( ) => {
196+ eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
197+ MongoDBState :: Skipping
198+ }
199+ Err ( err) => {
200+ eprintln ! ( "Mongo test error: {err}" ) ;
201+ MongoDBState :: Broken ( err)
202+ }
203+ }
204+ }
205+
206+ #[ nativelink_test]
207+ async fn connect_with_username_and_password ( ) -> Result < ( ) , Error > {
208+ match check_mongodb_available ( ) . await {
209+ MongoDBState :: Ok ( _helper) => { }
210+ MongoDBState :: Broken ( err) => {
211+ return Err ( err) ;
212+ }
213+ MongoDBState :: Skipping => {
214+ return Ok ( ( ) ) ;
215+ }
216+ }
217+
218+ let mut spec = create_test_spec ( ) ;
219+ // These are incorrect, but should allow us to connect and fail with bad login
220+ // v.s. "cannot do login at all" or "config failure"
221+ spec. username = "foo" . to_string ( ) ;
222+ spec. password = "bar" . to_string ( ) ;
223+ let store = ExperimentalMongoStore :: new ( spec)
224+ . await
225+ . expect ( "Working store" ) ;
226+ let digest = DigestInfo :: try_new ( VALID_HASH1 , 2 ) ?;
227+ let result = store. has ( digest) . await ?;
228+ assert ! (
229+ result. is_some( ) ,
230+ "Expected mongo store to have hash: {VALID_HASH1}" ,
231+ ) ;
232+ Ok ( ( ) )
184233}
185234
186235#[ nativelink_test]
187236async fn upload_and_get_data ( ) -> Result < ( ) , Error > {
188237 // Create test helper with automatic cleanup
189- let Ok ( helper) = TestMongoHelper :: new_or_skip ( ) . await else {
190- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
191- return Ok ( ( ) ) ;
238+ let helper = match check_mongodb_available ( ) . await {
239+ MongoDBState :: Ok ( helper) => helper,
240+ MongoDBState :: Broken ( err) => {
241+ return Err ( err) ;
242+ }
243+ MongoDBState :: Skipping => {
244+ return Ok ( ( ) ) ;
245+ }
192246 } ;
193247
194248 // Construct the data we want to send.
@@ -252,9 +306,14 @@ async fn upload_and_get_data_without_prefix() -> Result<(), Error> {
252306#[ nativelink_test]
253307async fn upload_empty_data ( ) -> Result < ( ) , Error > {
254308 // Skip test if MongoDB is not available
255- if check_mongodb_available ( ) . await . is_err ( ) {
256- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
257- return Ok ( ( ) ) ;
309+ match check_mongodb_available ( ) . await {
310+ MongoDBState :: Ok ( _helper) => { }
311+ MongoDBState :: Broken ( err) => {
312+ return Err ( err) ;
313+ }
314+ MongoDBState :: Skipping => {
315+ return Ok ( ( ) ) ;
316+ }
258317 }
259318
260319 let data = Bytes :: from_static ( b"" ) ;
@@ -277,9 +336,14 @@ async fn upload_empty_data() -> Result<(), Error> {
277336#[ allow( clippy:: items_after_statements) ]
278337async fn test_large_downloads_are_chunked ( ) -> Result < ( ) , Error > {
279338 // Skip test if MongoDB is not available
280- if check_mongodb_available ( ) . await . is_err ( ) {
281- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
282- return Ok ( ( ) ) ;
339+ match check_mongodb_available ( ) . await {
340+ MongoDBState :: Ok ( _helper) => { }
341+ MongoDBState :: Broken ( err) => {
342+ return Err ( err) ;
343+ }
344+ MongoDBState :: Skipping => {
345+ return Ok ( ( ) ) ;
346+ }
283347 }
284348
285349 const READ_CHUNK_SIZE : usize = 1024 ;
@@ -316,9 +380,14 @@ async fn test_large_downloads_are_chunked() -> Result<(), Error> {
316380#[ allow( clippy:: items_after_statements) ]
317381async fn yield_between_sending_packets_in_update ( ) -> Result < ( ) , Error > {
318382 // Skip test if MongoDB is not available
319- if check_mongodb_available ( ) . await . is_err ( ) {
320- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
321- return Ok ( ( ) ) ;
383+ match check_mongodb_available ( ) . await {
384+ MongoDBState :: Ok ( _helper) => { }
385+ MongoDBState :: Broken ( err) => {
386+ return Err ( err) ;
387+ }
388+ MongoDBState :: Skipping => {
389+ return Ok ( ( ) ) ;
390+ }
322391 }
323392
324393 const READ_CHUNK_SIZE : usize = 1024 ;
@@ -372,9 +441,14 @@ async fn yield_between_sending_packets_in_update() -> Result<(), Error> {
372441#[ nativelink_test]
373442async fn zero_len_items_exist_check ( ) -> Result < ( ) , Error > {
374443 // Skip test if MongoDB is not available
375- if check_mongodb_available ( ) . await . is_err ( ) {
376- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
377- return Ok ( ( ) ) ;
444+ match check_mongodb_available ( ) . await {
445+ MongoDBState :: Ok ( _helper) => { }
446+ MongoDBState :: Broken ( err) => {
447+ return Err ( err) ;
448+ }
449+ MongoDBState :: Skipping => {
450+ return Ok ( ( ) ) ;
451+ }
378452 }
379453
380454 let digest = DigestInfo :: try_new ( VALID_HASH1 , 0 ) ?;
@@ -427,9 +501,14 @@ async fn test_empty_connection_string() -> Result<(), Error> {
427501#[ nativelink_test]
428502async fn test_default_values ( ) -> Result < ( ) , Error > {
429503 // Skip test if MongoDB is not available
430- if check_mongodb_available ( ) . await . is_err ( ) {
431- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
432- return Ok ( ( ) ) ;
504+ match check_mongodb_available ( ) . await {
505+ MongoDBState :: Ok ( _helper) => { }
506+ MongoDBState :: Broken ( err) => {
507+ return Err ( err) ;
508+ }
509+ MongoDBState :: Skipping => {
510+ return Ok ( ( ) ) ;
511+ }
433512 }
434513
435514 let mut spec = create_test_spec ( ) ;
@@ -464,9 +543,14 @@ async fn test_default_values() -> Result<(), Error> {
464543#[ nativelink_test]
465544async fn dont_loop_forever_on_empty ( ) -> Result < ( ) , Error > {
466545 // Skip test if MongoDB is not available
467- if check_mongodb_available ( ) . await . is_err ( ) {
468- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
469- return Ok ( ( ) ) ;
546+ match check_mongodb_available ( ) . await {
547+ MongoDBState :: Ok ( _helper) => { }
548+ MongoDBState :: Broken ( err) => {
549+ return Err ( err) ;
550+ }
551+ MongoDBState :: Skipping => {
552+ return Ok ( ( ) ) ;
553+ }
470554 }
471555
472556 let store = create_test_store ( ) . await ?;
@@ -502,9 +586,14 @@ async fn dont_loop_forever_on_empty() -> Result<(), Error> {
502586#[ nativelink_test]
503587async fn test_partial_reads ( ) -> Result < ( ) , Error > {
504588 // Create test helper with automatic cleanup
505- let Ok ( helper) = TestMongoHelper :: new_or_skip ( ) . await else {
506- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
507- return Ok ( ( ) ) ;
589+ let helper = match check_mongodb_available ( ) . await {
590+ MongoDBState :: Ok ( helper) => helper,
591+ MongoDBState :: Broken ( err) => {
592+ return Err ( err) ;
593+ }
594+ MongoDBState :: Skipping => {
595+ return Ok ( ( ) ) ;
596+ }
508597 } ;
509598
510599 let data = Bytes :: from_static ( b"Hello, MongoDB World!" ) ;
@@ -620,9 +709,14 @@ async fn test_database_lifecycle() -> Result<(), Error> {
620709#[ allow( clippy:: use_debug) ]
621710async fn create_ten_cas_entries ( ) -> Result < ( ) , Error > {
622711 // Create test helper with automatic cleanup
623- let Ok ( helper) = TestMongoHelper :: new_or_skip ( ) . await else {
624- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
625- return Ok ( ( ) ) ;
712+ let helper = match check_mongodb_available ( ) . await {
713+ MongoDBState :: Ok ( helper) => helper,
714+ MongoDBState :: Broken ( err) => {
715+ return Err ( err) ;
716+ }
717+ MongoDBState :: Skipping => {
718+ return Ok ( ( ) ) ;
719+ }
626720 } ;
627721
628722 eprintln ! (
@@ -848,9 +942,14 @@ impl SchedulerStoreDecodeTo for TestSchedulerKey {
848942#[ nativelink_test]
849943async fn test_scheduler_store_operations ( ) -> Result < ( ) , Error > {
850944 // Create test helper
851- let Ok ( helper) = TestMongoHelper :: new_or_skip ( ) . await else {
852- eprintln ! ( "Skipping MongoDB test - MongoDB not available" ) ;
853- return Ok ( ( ) ) ;
945+ let helper = match check_mongodb_available ( ) . await {
946+ MongoDBState :: Ok ( helper) => helper,
947+ MongoDBState :: Broken ( err) => {
948+ return Err ( err) ;
949+ }
950+ MongoDBState :: Skipping => {
951+ return Ok ( ( ) ) ;
952+ }
854953 } ;
855954
856955 eprintln ! (
@@ -1062,6 +1161,8 @@ async fn test_non_w_config() -> Result<(), Error> {
10621161 Error :: new( Code :: InvalidArgument , "write_concern_w not set, but j and/or timeout set. Please set 'write_concern_w' to a non-default value. See https://www.mongodb.com/docs/manual/reference/write-concern/#w-option for options." . to_string( ) ) ,
10631162 ExperimentalMongoStore :: new( ExperimentalMongoSpec {
10641163 connection_string: "mongodb://dummy" . to_string( ) ,
1164+ username: String :: new( ) ,
1165+ password: String :: new( ) ,
10651166 database: "dummy" . to_string( ) ,
10661167 cas_collection: "test_cas" . to_string( ) ,
10671168 scheduler_collection: "test_scheduler" . to_string( ) ,
0 commit comments