@@ -427,3 +427,106 @@ def test_blob_handles_serialization(self):
427427 # BlobCodec.decode() should unpack back to original
428428 decoded = blob_codec .decode (encoded )
429429 assert decoded == data
430+
431+
432+ class TestFilepathCodec :
433+ """Tests for the built-in FilepathCodec."""
434+
435+ def test_filepath_is_registered (self ):
436+ """Test that filepath is automatically registered."""
437+ assert is_codec_registered ("filepath" )
438+
439+ def test_filepath_properties (self ):
440+ """Test FilepathCodec properties."""
441+ filepath_codec = get_codec ("filepath" )
442+ assert filepath_codec .name == "filepath"
443+ # Filepath requires @store, so only test is_store=True
444+ assert filepath_codec .get_dtype (is_store = True ) == "json"
445+
446+ def test_filepath_rejects_hash_section (self ):
447+ """Test that filepath rejects paths starting with _hash/."""
448+ from unittest .mock import MagicMock , patch
449+
450+ filepath_codec = get_codec ("filepath" )
451+
452+ # Mock the backend to avoid actual file operations
453+ with patch ("datajoint.hash_registry.get_store_backend" ) as mock_get_backend :
454+ mock_backend = MagicMock ()
455+ mock_backend .exists .return_value = True
456+ mock_get_backend .return_value = mock_backend
457+
458+ # Test various forms of _hash/ paths
459+ invalid_paths = [
460+ "_hash/abc123" ,
461+ "_hash/schema/file.dat" ,
462+ "/_hash/nested/path.bin" ,
463+ ]
464+
465+ for path in invalid_paths :
466+ with pytest .raises (
467+ ValueError ,
468+ match = r"<filepath@> cannot use reserved sections '_hash/' or '_schema/'" ,
469+ ):
470+ filepath_codec .encode (path , store_name = "test_store" )
471+
472+ def test_filepath_rejects_schema_section (self ):
473+ """Test that filepath rejects paths starting with _schema/."""
474+ from unittest .mock import MagicMock , patch
475+
476+ filepath_codec = get_codec ("filepath" )
477+
478+ # Mock the backend to avoid actual file operations
479+ with patch ("datajoint.hash_registry.get_store_backend" ) as mock_get_backend :
480+ mock_backend = MagicMock ()
481+ mock_backend .exists .return_value = True
482+ mock_get_backend .return_value = mock_backend
483+
484+ # Test various forms of _schema/ paths
485+ invalid_paths = [
486+ "_schema/mytable" ,
487+ "_schema/myschema/mytable/key.dat" ,
488+ "/_schema/nested/data.zarr" ,
489+ ]
490+
491+ for path in invalid_paths :
492+ with pytest .raises (
493+ ValueError ,
494+ match = r"<filepath@> cannot use reserved sections '_hash/' or '_schema/'" ,
495+ ):
496+ filepath_codec .encode (path , store_name = "test_store" )
497+
498+ def test_filepath_allows_user_paths (self ):
499+ """Test that filepath allows any paths outside reserved sections."""
500+ from unittest .mock import MagicMock , patch
501+
502+ filepath_codec = get_codec ("filepath" )
503+
504+ # Mock the backend to avoid actual file operations
505+ with patch ("datajoint.hash_registry.get_store_backend" ) as mock_get_backend :
506+ mock_backend = MagicMock ()
507+ mock_backend .exists .return_value = True
508+ mock_backend .size .return_value = 1024
509+ mock_get_backend .return_value = mock_backend
510+
511+ # Test valid user-managed paths
512+ valid_paths = [
513+ "subject01/session001/data.bin" ,
514+ "raw/experiment_2024/recording.nwb" ,
515+ "processed/analysis_v2/results.csv" ,
516+ "my_hash_file.dat" , # "hash" in name is fine
517+ "my_schema_backup.sql" , # "schema" in name is fine
518+ ]
519+
520+ for path in valid_paths :
521+ result = filepath_codec .encode (path , store_name = "test_store" )
522+ assert isinstance (result , dict )
523+ assert result ["path" ] == path
524+ assert result ["store" ] == "test_store"
525+ assert result ["size" ] == 1024
526+ assert result ["is_dir" ] is False
527+ assert "timestamp" in result
528+
529+ def test_filepath_in_list_codecs (self ):
530+ """Test that filepath appears in list_codecs."""
531+ codecs = list_codecs ()
532+ assert "filepath" in codecs
0 commit comments