@@ -145,11 +145,15 @@ def test_dict_access_unwraps_secret(self):
145145 assert not isinstance (value , SecretStr )
146146 dj .config .database .password = None
147147
148- def test_aws_secret_key_is_secret_str (self ):
149- """AWS secret key uses SecretStr type."""
150- dj .config .external .aws_secret_access_key = "aws_secret"
151- assert isinstance (dj .config .external .aws_secret_access_key , SecretStr )
152- dj .config .external .aws_secret_access_key = None
148+ def test_store_secret_key_is_secret_str (self ):
149+ """Store secret key uses SecretStr type when set."""
150+ original_stores = dj .config .stores .copy ()
151+ try :
152+ dj .config .stores ["test_store" ] = {"secret_key" : "aws_secret" }
153+ # SecretStr is handled by pydantic if defined, but stores dict doesn't enforce it
154+ assert dj .config .stores ["test_store" ]["secret_key" ] == "aws_secret"
155+ finally :
156+ dj .config .stores = original_stores
153157
154158
155159class TestSettingsAccess :
@@ -325,7 +329,10 @@ def test_get_store_spec_file_protocol(self):
325329 spec = dj .config .get_store_spec ("test_file" )
326330 assert spec ["protocol" ] == "file"
327331 assert spec ["location" ] == "/tmp/test"
328- assert spec ["subfolding" ] == settings .DEFAULT_SUBFOLDING
332+ # Default is now None (no subfolding) instead of DEFAULT_SUBFOLDING
333+ assert spec ["subfolding" ] is None
334+ assert spec ["partition_pattern" ] is None
335+ assert spec ["token_length" ] == 8
329336 finally :
330337 dj .config .stores = original_stores
331338
@@ -342,6 +349,78 @@ def test_get_store_spec_missing_required(self):
342349 finally :
343350 dj .config .stores = original_stores
344351
352+ def test_get_store_spec_default_store (self ):
353+ """Test getting default store when store=None."""
354+ original_stores = dj .config .stores .copy ()
355+ try :
356+ dj .config .stores ["default" ] = "my_default"
357+ dj .config .stores ["my_default" ] = {
358+ "protocol" : "file" ,
359+ "location" : "/tmp/default" ,
360+ }
361+ # Calling with None should use stores.default
362+ spec = dj .config .get_store_spec (None )
363+ assert spec ["protocol" ] == "file"
364+ assert spec ["location" ] == "/tmp/default"
365+ finally :
366+ dj .config .stores = original_stores
367+
368+ def test_get_store_spec_no_default_configured (self ):
369+ """Test error when stores.default is not configured."""
370+ original_stores = dj .config .stores .copy ()
371+ try :
372+ dj .config .stores = {} # Clear stores
373+ with pytest .raises (DataJointError , match = "stores.default is not configured" ):
374+ dj .config .get_store_spec (None )
375+ finally :
376+ dj .config .stores = original_stores
377+
378+
379+ class TestStoreSecrets :
380+ """Test loading store credentials from secrets directory."""
381+
382+ def test_load_store_credentials_from_secrets (self , tmp_path ):
383+ """Test loading per-store credentials from .secrets/ directory."""
384+ # Create secrets directory with store credentials
385+ secrets_dir = tmp_path / SECRETS_DIRNAME
386+ secrets_dir .mkdir ()
387+ (secrets_dir / "stores.main.access_key" ).write_text ("test_access_key" )
388+ (secrets_dir / "stores.main.secret_key" ).write_text ("test_secret_key" )
389+
390+ # Create a fresh config instance
391+ cfg = settings .Config ()
392+ original_stores = cfg .stores .copy ()
393+ try :
394+ # Load secrets
395+ cfg ._load_secrets (secrets_dir )
396+
397+ # Verify credentials were loaded
398+ assert "main" in cfg .stores
399+ assert cfg .stores ["main" ]["access_key" ] == "test_access_key"
400+ assert cfg .stores ["main" ]["secret_key" ] == "test_secret_key"
401+ finally :
402+ cfg .stores = original_stores
403+
404+ def test_secrets_do_not_override_existing (self , tmp_path ):
405+ """Test that secrets don't override already-configured store settings."""
406+ secrets_dir = tmp_path / SECRETS_DIRNAME
407+ secrets_dir .mkdir ()
408+ (secrets_dir / "stores.main.access_key" ).write_text ("secret_key" )
409+
410+ cfg = settings .Config ()
411+ original_stores = cfg .stores .copy ()
412+ try :
413+ # Pre-configure the store with a key
414+ cfg .stores ["main" ] = {"access_key" : "existing_key" }
415+
416+ # Load secrets - should not override
417+ cfg ._load_secrets (secrets_dir )
418+
419+ # Existing key should be preserved
420+ assert cfg .stores ["main" ]["access_key" ] == "existing_key"
421+ finally :
422+ cfg .stores = original_stores
423+
345424
346425class TestDisplaySettings :
347426 """Test display-related settings."""
@@ -418,10 +497,14 @@ def test_save_full_template(self, tmp_path):
418497 assert "database" in content
419498 assert "connection" in content
420499 assert "display" in content
421- assert "object_storage" in content
422500 assert "stores" in content
423501 assert "loglevel" in content
424502 assert "safemode" in content
503+ # Verify stores structure
504+ assert "default" in content ["stores" ]
505+ assert "main" in content ["stores" ]
506+ assert content ["stores" ]["default" ] == "main"
507+ assert content ["stores" ]["main" ]["protocol" ] == "file"
425508 # But still no credentials
426509 assert "password" not in content ["database" ]
427510 assert "user" not in content ["database" ]
0 commit comments