2828
2929from app .core .security import crypto_timestamp_serializer
3030from app .sep .apps .framework .base import BaseApp
31- from app .sep .artifact_constants import ARTIFACT_DOWNLOAD_SALT
31+ from app .sep .artifact_constants import ARTIFACT_DOWNLOAD_SALT , STATIC_ARTIFACT_BASE_DIRS
3232from app .sep .routes .artifacts import collect_base_dirs
33+ from app .sep .snippets .constants import ARTIFACT_TYPE_SNIPPET
34+
35+ _SNIPPETS_DIR_TARGET = "app.sep.snippets.config.snippets_settings.SNIPPETS_DIR"
3336
3437
3538def _make_token (payload : dict , salt : str = ARTIFACT_DOWNLOAD_SALT ) -> str :
@@ -60,29 +63,69 @@ def test_raises_on_duplicate_artifact_type(self, mocker) -> None:
6063 collect_base_dirs ()
6164
6265 def test_flattens_distinct_artifact_types (self , mocker ) -> None :
63- """Merge distinct per-app declarations into one map."""
64- snippet_dir = Path ("/tmp/snippets" )
66+ """Merge distinct per-app declarations over the static seed."""
6567 dipper_dir = Path ("/tmp/dipper" )
66- first = BaseApp (
67- name = "snippets" ,
68- uri_path = "/snippets" ,
69- artifact_base_dirs = {"snippet" : lambda : snippet_dir },
70- )
71- second = BaseApp (
68+ other_dir = Path ("/tmp/other" )
69+ dipper = BaseApp (
7270 name = "dipper" ,
7371 uri_path = "/dipper" ,
7472 artifact_base_dirs = {"dipper" : lambda : dipper_dir },
7573 )
74+ other = BaseApp (
75+ name = "other" ,
76+ uri_path = "/other" ,
77+ artifact_base_dirs = {"other" : lambda : other_dir },
78+ )
7679 mocker .patch (
7780 "app.sep.routes.artifacts.get_app_registry" ,
78- return_value = [first , second ],
81+ return_value = [dipper , other ],
7982 )
8083
8184 result = collect_base_dirs ()
8285
83- assert result .keys () == {"snippet" , "dipper" }
84- assert result ["snippet" ]() == snippet_dir
86+ assert result .keys () == {ARTIFACT_TYPE_SNIPPET , "dipper" , "other" }
8587 assert result ["dipper" ]() == dipper_dir
88+ assert result ["other" ]() == other_dir
89+
90+ def test_seeds_the_static_snippet_type_without_the_snippets_app (
91+ self , mocker
92+ ) -> None :
93+ """Resolve the snippet type from the static map, with no app declaring it."""
94+ mocker .patch (
95+ "app.sep.routes.artifacts.get_app_registry" ,
96+ return_value = [BaseApp (name = "atw" , uri_path = "/atw" )],
97+ )
98+
99+ assert ARTIFACT_TYPE_SNIPPET in collect_base_dirs ()
100+
101+ def test_raises_when_an_app_redeclares_a_static_type (self , mocker ) -> None :
102+ """Reject an app that re-declares a statically registered type."""
103+ redeclaring = BaseApp (
104+ name = "snippets" ,
105+ uri_path = "/snippets" ,
106+ artifact_base_dirs = {ARTIFACT_TYPE_SNIPPET : lambda : Path ("/tmp/other" )},
107+ )
108+ mocker .patch (
109+ "app.sep.routes.artifacts.get_app_registry" , return_value = [redeclaring ]
110+ )
111+
112+ with pytest .raises (ValueError , match = ARTIFACT_TYPE_SNIPPET ):
113+ collect_base_dirs ()
114+
115+ def test_does_not_mutate_the_static_map (self , mocker ) -> None :
116+ """Return a fresh dict so an app declaration cannot leak into the constant."""
117+ declaring = BaseApp (
118+ name = "dipper" ,
119+ uri_path = "/dipper" ,
120+ artifact_base_dirs = {"dipper" : lambda : Path ("/tmp/dipper" )},
121+ )
122+ mocker .patch (
123+ "app.sep.routes.artifacts.get_app_registry" , return_value = [declaring ]
124+ )
125+
126+ collect_base_dirs ()
127+
128+ assert "dipper" not in STATIC_ARTIFACT_BASE_DIRS
86129
87130
88131class TestDownloadArtifact :
@@ -100,9 +143,7 @@ def test_valid_snippet_token_existing_file_returns_200(self, test_client, tmp_pa
100143 }
101144 token = _make_token (payload )
102145
103- with patch (
104- "app.sep.apps.snippets.app.snippets_settings.SNIPPETS_DIR" , tmp_path
105- ):
146+ with patch (_SNIPPETS_DIR_TARGET , tmp_path ):
106147 response = test_client .get (f"/artifacts/download/{ token } " )
107148
108149 assert response .status_code == HTTP_200_OK
@@ -143,7 +184,7 @@ def test_expired_token_returns_400(self, test_client, tmp_path):
143184
144185 with (
145186 patch ("app.sep.routes.artifacts.sep_settings.ARTIFACT_DOWNLOAD_TTL" , 60 ),
146- patch ("app.sep.apps.snippets.app.snippets_settings.SNIPPETS_DIR" , tmp_path ),
187+ patch (_SNIPPETS_DIR_TARGET , tmp_path ),
147188 ):
148189 response = test_client .get (
149190 f"/artifacts/download/{ token } " , follow_redirects = False
@@ -176,9 +217,7 @@ def test_valid_token_nonexistent_file_returns_404(self, test_client, tmp_path):
176217 }
177218 token = _make_token (payload )
178219
179- with patch (
180- "app.sep.apps.snippets.app.snippets_settings.SNIPPETS_DIR" , tmp_path
181- ):
220+ with patch (_SNIPPETS_DIR_TARGET , tmp_path ):
182221 response = test_client .get (
183222 f"/artifacts/download/{ token } " , follow_redirects = False
184223 )
@@ -194,9 +233,7 @@ def test_path_traversal_in_filename_returns_400(self, test_client, tmp_path):
194233 }
195234 token = _make_token (payload )
196235
197- with patch (
198- "app.sep.apps.snippets.app.snippets_settings.SNIPPETS_DIR" , tmp_path
199- ):
236+ with patch (_SNIPPETS_DIR_TARGET , tmp_path ):
200237 response = test_client .get (
201238 f"/artifacts/download/{ token } " , follow_redirects = False
202239 )
0 commit comments