From 00c365656376f755e0a359eaeafce9dea4aed6d8 Mon Sep 17 00:00:00 2001 From: Quirin Pamp Date: Thu, 2 Nov 2023 16:00:52 +0100 Subject: [PATCH] Add duplicate package tests [noissue] --- .../functional/api/test_duplicate_packages.py | 127 ++++++++++++++++++ .../data/packages/duplicates/README.md | 1 + .../packages/duplicates/frigg_1.0_ppc64.deb | Bin 0 -> 2120 bytes pulp_deb/tests/functional/utils.py | 11 ++ 4 files changed, 139 insertions(+) create mode 100644 pulp_deb/tests/functional/api/test_duplicate_packages.py create mode 100644 pulp_deb/tests/functional/data/packages/duplicates/README.md create mode 100644 pulp_deb/tests/functional/data/packages/duplicates/frigg_1.0_ppc64.deb diff --git a/pulp_deb/tests/functional/api/test_duplicate_packages.py b/pulp_deb/tests/functional/api/test_duplicate_packages.py new file mode 100644 index 000000000..6bb347e44 --- /dev/null +++ b/pulp_deb/tests/functional/api/test_duplicate_packages.py @@ -0,0 +1,127 @@ +"""Tests relating to duplicate package handling. + +By "duplicate package" we mean two packages with the same Package, Version, and Architecture fields, +but different checksums. By contrast we refer to two packages with the same checksum (but stored at +a different relative_path within some repo) as two "identical packages". So defined, an APT repo may +contain identical packages, but may not contain any duplicates. + +To ensure this is the case we use the handle_duplicate_packages function. As such, these tests are +primarily intended to test this function. +""" +import pytest +from uuid import uuid4 + +from pulpcore.tests.functional.utils import PulpTaskError + +from pulp_deb.tests.functional.constants import DEB_PACKAGE_RELPATH +from pulp_deb.tests.functional.utils import ( + get_counts_from_content_summary, + get_local_package_absolute_path, + get_local_duplicate_package_absolute_path, +) + + +def test_upload_package_and_duplicate( + apt_package_api, + deb_get_content_summary, + deb_get_repository_by_href, + deb_package_factory, + deb_repository_factory, +): + """Test uploading a package to a repo, and then uploading it's duplicate. + + The expectation is that uploading the duplicate will kick the older duplicate (along with the + associated PackageReleaseComponent) out of the repo. Only the newer duplicate and its PRC will + remain. + """ + # Generate an empty test repo. + repository = deb_repository_factory() + assert repository.latest_version_href.endswith("/0/") + repository_href = repository.pulp_href + + # Upload a test package to a component in the repo. + package_upload_params = { + "file": get_local_package_absolute_path(DEB_PACKAGE_RELPATH), + "relative_path": DEB_PACKAGE_RELPATH, + "distribution": str(uuid4()), + "component": str(uuid4()), + "repository": repository_href, + } + deb_package_factory(**package_upload_params) + + # Assert that the uploaded package has arrived in the repo. + repository = deb_get_repository_by_href(repository_href) + assert repository.latest_version_href.endswith("/1/") + content_counts = get_counts_from_content_summary(deb_get_content_summary(repository).added) + assert content_counts == { + "deb.package": 1, + "deb.package_release_component": 1, + "deb.release_architecture": 1, + "deb.release_component": 1, + } + package1_sha256 = ( + apt_package_api.list( + repository_version_added=repository.latest_version_href, fields=["sha256"] + ) + .results[0] + .sha256 + ) + + # Upload a duplicate of the first package into the repo. + package_upload_params["file"] = get_local_duplicate_package_absolute_path(DEB_PACKAGE_RELPATH) + deb_package_factory(**package_upload_params) + + # Assert that only the newer duplicate is now in the repo. + repository = deb_get_repository_by_href(repository_href) + assert repository.latest_version_href.endswith("/2/") + content_summary = deb_get_content_summary(repository) + content_counts_added = get_counts_from_content_summary(content_summary.added) + content_counts_removed = get_counts_from_content_summary(content_summary.removed) + assert content_counts_added == { + "deb.package": 1, + "deb.package_release_component": 1, + } + assert content_counts_removed == { + "deb.package": 1, + "deb.package_release_component": 1, + } + package2_sha256 = ( + apt_package_api.list( + repository_version_added=repository.latest_version_href, fields=["sha256"] + ) + .results[0] + .sha256 + ) + assert package1_sha256 != package2_sha256 + + +def test_add_duplicates_to_repo( + deb_modify_repository, + deb_package_factory, + deb_repository_factory, +): + """Test adding two duplicate packages to a repository in a single modify action. + + The expectation is that this will raise a ValueError. + """ + # Upload two duplicate packages. + package_upload_params = { + "file": get_local_package_absolute_path(DEB_PACKAGE_RELPATH), + "relative_path": DEB_PACKAGE_RELPATH, + } + href1 = deb_package_factory(**package_upload_params).pulp_href + package_upload_params["file"] = get_local_duplicate_package_absolute_path(DEB_PACKAGE_RELPATH) + href2 = deb_package_factory(**package_upload_params).pulp_href + + # Generate an empty test repo. + repository = deb_repository_factory() + assert repository.latest_version_href.endswith("/0/") + + # Add the duplicates to the repository. + with pytest.raises(PulpTaskError) as exception: + deb_modify_repository(repository, {"add_content_units": [href1, href2]}) + + # Assert the error message. + assert "Cannot create repository version since there are newly added packages with" in str( + exception.value + ) diff --git a/pulp_deb/tests/functional/data/packages/duplicates/README.md b/pulp_deb/tests/functional/data/packages/duplicates/README.md new file mode 100644 index 000000000..ceb2c75a2 --- /dev/null +++ b/pulp_deb/tests/functional/data/packages/duplicates/README.md @@ -0,0 +1 @@ +This is a duplicate of the package with a different checksum! diff --git a/pulp_deb/tests/functional/data/packages/duplicates/frigg_1.0_ppc64.deb b/pulp_deb/tests/functional/data/packages/duplicates/frigg_1.0_ppc64.deb new file mode 100644 index 0000000000000000000000000000000000000000..ab424443b27efbb93a9b4c5c2932c01fe43e29dc GIT binary patch literal 2120 zcmbuv=1g6G(xl(856{0zwGk zx&a~K1QJD2QPBWlW^87FFh(F06=6H`|9%4)3;~BLD(<}h1}|)=2NMpA2#+L1gy}^R zNP5xd|9Rio$ndZIEtB)!H~>Isf%VWvsEa8W=c)vh2K*-%I_^}@sBZ%Yjsavu+T||- zv%3yBpea&41HY9X-%s_ph!Lcg2coV0!{X_ zf&){!zEVQR{xGG{`_1 z=MBWp@rGFq6Thjo{HiN(|8BSlgvmmpMm>+`D-mJzR%3)lhSZ=(^imy${ zp(Fd6s^DeE8WQ?)+DHA!koRAsSWc$6-U#9?pBF=FShsSI?xEqst4C&_vFvhrZ9UAMFk^dnftiGDIPWFB1a?;wMnW0h zd8j;YvYvA7C0-0%njL^q(|CL7A;K2_gA>c%lLx+##5sm?p2>xFdTv-vz1F03=nq;w zcU!!axLO5X#LD8n-4w{|Z+wNm9Us_xp%AK2F#r{t@fzjTS7r4b#V72uP-P6>qgvDy z-WxtJ;F4ru+Q{{2{Z&)d=(8G_Vbi#g^rO3O~b%*Q;j7@Y#}w^U{lP=O^$ z62N+&ng>c`RJ!hk=`Jz}c;U6&WQB#vFqTZ3WM+oQEN*VhD`^&0k@RNf3=_b8HqCpq z)PV}!NQE(~k}Wrs#ChF#D`c)O>41k*h5AO**NVzp(?V?|9V+k~dOE}HoxC#jfvB-s zSh|9Jc#PJPu+jebQ*2pQh7hpUOZjXexD?huoo}+vqw2o|`cq$@OO76Q<_JEYP zmW6!QsJQOay5vfZjtm=RXn+BVHR-JfTQtTtND3TFD{g(|xQ`sU*Sp>y`1oMAsJmjn zX-2oz{mhZu1%e-H1ZxVX(I#qx&RbKb7isxkA-rp>+4C2p)3W`K;I2aK>;}IqbcH-M zQ2vAZx;hm+qFKsSaBUJ4Ngm_ zmEOAm&E{PcN8-9#Wvk`3+-)Uf^2)?7B-@-Vo8fN89j~|>trQ;HLEpOf#XBPcPTI|| zl#Qh;<2RqIm6j-WsJ%o0;}SG=Ciz}%dHU!29$p8I_?8pDCVsDK&GK#d9-{;s>tC`7 zUYP^w2^h;*ly#ggvA+L0n5QyqtI<|I88NBZV{DWck(Fztf+|o65w=HWoNE@3YG7`_ zK9WsPN8a!0d?Mds<&W9l6V_~jmtdEFopq^E!lypQjx9l_sV@x^Th7bW8OnLTgbGE} zjQg}oCq(;xjJtfjtR0NeFmp3L&h|og9b`b(rx;h@=gLGHyWE%(1%;{T??vxBmXCON zhv=}M{7}j(&y;Wso*#Eb&`Tz!-y-+bJ!*_ke6t-r3c0tr8c-bKpqq7Le^*ou-5V4z zebtBIYORI3@!T^V?DU9r1L!Scx?-16T*My_I*y_j<8rxOD<4uZ5Z56pd>($aonOd) zjDes0MD^-&fsfy!4~IBJnJiYvLHOErmeo1x&>E*4w&(1yOMV|NlFJ_-uMg>=K}+la z)GGtk$K5adAWjMi9rbjw>?tD((^n?on=NG-Q<(mg!Nsi8zzQZ~@)f6{OvdzKko@!h z^1vb%n3>lq|!DnYyMp1oUiwdhtLEUCHB1dHZHI#>Dk>@Kej=GNBH_+g3rQbJ4a zYS=@_LCWTmBO3A7_G#6l)K@#5^CDwX)1X)MTnR*90o78eOlYg0gzd9Hw*R$X^0RfT mkWaoBlpKU;hb#=*lwy literal 0 HcmV?d00001 diff --git a/pulp_deb/tests/functional/utils.py b/pulp_deb/tests/functional/utils.py index cd325c40e..55f78a4dc 100644 --- a/pulp_deb/tests/functional/utils.py +++ b/pulp_deb/tests/functional/utils.py @@ -76,6 +76,17 @@ def get_local_package_absolute_path(package_name): return p.joinpath(f"data/packages/{package_name}") +def get_local_duplicate_package_absolute_path(package_name): + """Looks up the local package of the given name under the relative path + 'data/packages/' and returns the absolute path. + + :param package_name: Name of the package to look up. + :returns: The absolute path to the package. + """ + p = Path(__file__).parent.absolute() + return p.joinpath(f"data/packages/duplicates/{package_name}") + + def gen_distribution(**kwargs): """Returns a semi-random dict for use in creating a Distribution.""" data = {"base_path": str(uuid4()), "name": str(uuid4())}