@@ -144,44 +144,41 @@ def test_content_create_new_metadata(
144144 assert getattr (content , k ) == v
145145
146146
147+ def get_package_url (package , filename ):
148+ with PyPISimple () as client :
149+ page = client .get_project_page (package )
150+ for package in page .packages :
151+ if package .filename == filename :
152+ return package .url
153+ raise ValueError (f"Package { filename } not found" )
154+
155+
147156@pytest .mark .parallel
148157def test_upload_metadata_23_spec (python_content_factory ):
149158 """Test that packages using metadata spec 2.3 can be uploaded to pulp."""
150159 filename = "urllib3-2.2.2-py3-none-any.whl"
151- with PyPISimple () as client :
152- page = client .get_project_page ("urllib3" )
153- for package in page .packages :
154- if package .filename == filename :
155- content = python_content_factory (filename , url = package .url )
156- assert content .metadata_version == "2.3"
157- break
160+ url = get_package_url ("urllib3" , filename )
161+ content = python_content_factory (filename , url = url )
162+ assert content .metadata_version == "2.3"
158163
159164
160165@pytest .mark .parallel
161166def test_upload_requires_python (python_content_factory ):
162167 filename = "pip-24.3.1-py3-none-any.whl"
163- with PyPISimple () as client :
164- page = client .get_project_page ("pip" )
165- for package in page .packages :
166- if package .filename == filename :
167- content = python_content_factory (filename , url = package .url )
168- assert content .requires_python == ">=3.8"
169- break
168+ url = get_package_url ("pip" , filename )
169+ content = python_content_factory (filename , url = url )
170+ assert content .requires_python == ">=3.8"
170171
171172
172173@pytest .mark .parallel
173174def test_upload_metadata_24_spec (python_content_factory ):
174175 """Test that packages using metadata spec 2.4 can be uploaded to pulp."""
175176 filename = "setuptools-80.9.0.tar.gz"
176- with PyPISimple () as client :
177- page = client .get_project_page ("setuptools" )
178- for package in page .packages :
179- if package .filename == filename :
180- content = python_content_factory (filename , url = package .url )
181- assert content .metadata_version == "2.4"
182- assert content .license_expression == "MIT"
183- assert content .license_file == '["LICENSE"]'
184- break
177+ url = get_package_url ("setuptools" , filename )
178+ content = python_content_factory (filename , url = url )
179+ assert content .metadata_version == "2.4"
180+ assert content .license_expression == "MIT"
181+ assert content .license_file == '["LICENSE"]'
185182
186183
187184@pytest .mark .parallel
@@ -203,3 +200,85 @@ def test_package_creation_with_metadata(
203200 ensure_metadata (
204201 pulp_content_url , distro .base_path , PYTHON_WHEEL_FILENAME , "shelf-reader" , "0.1"
205202 )
203+
204+
205+ @pytest .mark .parallel
206+ def test_upload_duplicate_filenames_disabled (
207+ monitor_task ,
208+ python_bindings ,
209+ python_repo_factory ,
210+ ):
211+ """
212+ When upload_duplicate_filenames=False, uploading a file whose filename already exists
213+ in the repository with a different sha256 is rejected, while re-uploading the same
214+ file (matching sha256) succeeds idempotently.
215+ """
216+ repo = python_repo_factory (upload_duplicate_filenames = False )
217+
218+ # First upload succeeds
219+ content_body = {"relative_path" : PYTHON_EGG_FILENAME , "file_url" : PYTHON_EGG_URL }
220+ response = python_bindings .ContentPackagesApi .create (repository = repo .pulp_href , ** content_body )
221+ task = monitor_task (response .task )
222+ content = python_bindings .ContentPackagesApi .read (task .created_resources [- 1 ])
223+ assert content .filename == PYTHON_EGG_FILENAME
224+
225+ # Re-upload same artifact with same filename — should succeed (idempotent)
226+ response = python_bindings .ContentPackagesApi .create (repository = repo .pulp_href , ** content_body )
227+ task = monitor_task (response .task )
228+ assert content .pulp_href in task .created_resources
229+
230+ # Upload a different artifact with the same filename — should be rejected
231+ second_filename = "pip-26.0.1.tar.gz"
232+ second_url = get_package_url ("pip" , second_filename )
233+ content_body = {"relative_path" : PYTHON_EGG_FILENAME , "file_url" : second_url }
234+ with pytest .raises (PulpTaskError ) as exc :
235+ response = python_bindings .ContentPackagesApi .create (
236+ repository = repo .pulp_href , ** content_body
237+ )
238+ monitor_task (response .task )
239+ assert "already exists in repository" in exc .value .task .error ["description" ]
240+ assert "upload_duplicate_filenames" in exc .value .task .error ["description" ]
241+
242+ # Verify the repository still has only the original content
243+ repo = python_bindings .RepositoriesPythonApi .read (repo .pulp_href )
244+ content_list = python_bindings .ContentPackagesApi .list (
245+ repository_version = repo .latest_version_href
246+ )
247+ assert content_list .count == 1
248+ assert content_list .results [0 ].sha256 == content .sha256
249+
250+
251+ @pytest .mark .parallel
252+ def test_upload_duplicate_filename_allowed_by_default (
253+ monitor_task ,
254+ python_bindings ,
255+ python_repo_factory ,
256+ ):
257+ """
258+ By default (upload_duplicate_filenames=True), uploading a file with the same filename
259+ but different sha256 replaces the existing content in the repository.
260+ """
261+ repo = python_repo_factory ()
262+ assert repo .upload_duplicate_filenames is True
263+
264+ content_body = {"relative_path" : PYTHON_EGG_FILENAME , "file_url" : PYTHON_EGG_URL }
265+ response = python_bindings .ContentPackagesApi .create (repository = repo .pulp_href , ** content_body )
266+ task = monitor_task (response .task )
267+ content1 = python_bindings .ContentPackagesApi .read (task .created_resources [- 1 ])
268+
269+ # Upload a different artifact with the same filename — should succeed and replace
270+ second_filename = "pip-26.0.tar.gz"
271+ second_url = get_package_url ("pip" , second_filename )
272+ content_body = {"relative_path" : PYTHON_EGG_FILENAME , "file_url" : second_url }
273+ response = python_bindings .ContentPackagesApi .create (repository = repo .pulp_href , ** content_body )
274+ task = monitor_task (response .task )
275+ content2 = python_bindings .ContentPackagesApi .read (task .created_resources [- 1 ])
276+ assert content2 .pulp_href != content1 .pulp_href
277+
278+ # Verify the repo has only the new content
279+ repo = python_bindings .RepositoriesPythonApi .read (repo .pulp_href )
280+ content_list = python_bindings .ContentPackagesApi .list (
281+ repository_version = repo .latest_version_href
282+ )
283+ assert content_list .count == 1
284+ assert content_list .results [0 ].sha256 == content2 .sha256
0 commit comments