import re from http import HTTPStatus from unittest import mock import pytest from fastapi.testclient import TestClient from sqlalchemy import and_ from aurweb import asgi, db, time from aurweb.models.account_type import USER_ID, AccountType from aurweb.models.dependency_type import DependencyType from aurweb.models.package import Package from aurweb.models.package_base import PackageBase from aurweb.models.package_comaintainer import PackageComaintainer from aurweb.models.package_comment import PackageComment from aurweb.models.package_dependency import PackageDependency from aurweb.models.package_notification import PackageNotification from aurweb.models.package_relation import PackageRelation from aurweb.models.package_request import ACCEPTED_ID, PackageRequest from aurweb.models.package_vote import PackageVote from aurweb.models.relation_type import PROVIDES_ID, RelationType from aurweb.models.request_type import DELETION_ID, MERGE_ID, RequestType from aurweb.models.user import User from aurweb.testing.email import Email from aurweb.testing.html import get_errors, get_successes, parse_root from aurweb.testing.requests import Request def package_endpoint(package: Package) -> str: return f"/packages/{package.Name}" def create_package(pkgname: str, maintainer: User) -> Package: pkgbase = db.create(PackageBase, Name=pkgname, Maintainer=maintainer) return db.create(Package, Name=pkgbase.Name, PackageBase=pkgbase) def create_package_dep( package: Package, depname: str, dep_type_name: str = "depends" ) -> PackageDependency: dep_type = db.query(DependencyType, DependencyType.Name == dep_type_name).first() return db.create( PackageDependency, DependencyType=dep_type, Package=package, DepName=depname ) def create_package_rel(package: Package, relname: str) -> PackageRelation: rel_type = db.query(RelationType, RelationType.ID == PROVIDES_ID).first() return db.create( PackageRelation, RelationType=rel_type, Package=package, RelName=relname ) @pytest.fixture(autouse=True) def setup(db_test): return @pytest.fixture def client() -> TestClient: """Yield a FastAPI TestClient.""" client = TestClient(app=asgi.app) # disable redirects for our tests client.follow_redirects = False yield client def create_user(username: str) -> User: with db.begin(): user = db.create( User, Username=username, Email=f"{username}@example.org", Passwd="testPassword", AccountTypeID=USER_ID, ) return user @pytest.fixture def user() -> User: """Yield a user.""" user = create_user("test") yield user @pytest.fixture def maintainer() -> User: """Yield a specific User used to maintain packages.""" account_type = db.query(AccountType, AccountType.ID == USER_ID).first() with db.begin(): maintainer = db.create( User, Username="test_maintainer", Email="test_maintainer@example.org", Passwd="testPassword", AccountType=account_type, ) yield maintainer @pytest.fixture def comaintainer() -> User: """Yield a specific User used to maintain packages.""" account_type = db.query(AccountType, AccountType.ID == USER_ID).first() with db.begin(): comaintainer = db.create( User, Username="test_comaintainer", Email="test_comaintainer@example.org", Passwd="testPassword", AccountType=account_type, ) yield comaintainer @pytest.fixture def tu_user(): tu_type = db.query(AccountType, AccountType.AccountType == "Trusted User").first() with db.begin(): tu_user = db.create( User, Username="test_tu", Email="test_tu@example.org", RealName="Test TU", Passwd="testPassword", AccountType=tu_type, ) yield tu_user @pytest.fixture def package(maintainer: User) -> Package: """Yield a Package created by user.""" now = time.utcnow() with db.begin(): pkgbase = db.create( PackageBase, Name="test-package", Maintainer=maintainer, Packager=maintainer, Submitter=maintainer, ModifiedTS=now, ) package = db.create(Package, PackageBase=pkgbase, Name=pkgbase.Name) yield package @pytest.fixture def pkgbase(package: Package) -> PackageBase: yield package.PackageBase @pytest.fixture def target(maintainer: User) -> PackageBase: """Merge target.""" now = time.utcnow() with db.begin(): pkgbase = db.create( PackageBase, Name="target-package", Maintainer=maintainer, Packager=maintainer, Submitter=maintainer, ModifiedTS=now, ) db.create(Package, PackageBase=pkgbase, Name=pkgbase.Name) yield pkgbase @pytest.fixture def pkgreq(user: User, pkgbase: PackageBase) -> PackageRequest: """Yield a PackageRequest related to `pkgbase`.""" with db.begin(): pkgreq = db.create( PackageRequest, ReqTypeID=DELETION_ID, User=user, PackageBase=pkgbase, PackageBaseName=pkgbase.Name, Comments=f"Deletion request for {pkgbase.Name}", ClosureComment=str(), ) yield pkgreq @pytest.fixture def comment(user: User, package: Package) -> PackageComment: pkgbase = package.PackageBase now = time.utcnow() with db.begin(): comment = db.create( PackageComment, User=user, PackageBase=pkgbase, Comments="Test comment.", RenderedComment=str(), CommentTS=now, ) yield comment @pytest.fixture def packages(maintainer: User) -> list[Package]: """Yield 55 packages named pkg_0 .. pkg_54.""" packages_ = [] now = time.utcnow() with db.begin(): for i in range(55): pkgbase = db.create( PackageBase, Name=f"pkg_{i}", Maintainer=maintainer, Packager=maintainer, Submitter=maintainer, ModifiedTS=now, ) package = db.create(Package, PackageBase=pkgbase, Name=f"pkg_{i}") packages_.append(package) yield packages_ @pytest.fixture def requests(user: User, packages: list[Package]) -> list[PackageRequest]: pkgreqs = [] deletion_type = db.query(RequestType).filter(RequestType.ID == DELETION_ID).first() with db.begin(): for i in range(55): pkgreq = db.create( PackageRequest, RequestType=deletion_type, User=user, PackageBase=packages[i].PackageBase, PackageBaseName=packages[i].Name, Comments=f"Deletion request for pkg_{i}", ClosureComment=str(), ) pkgreqs.append(pkgreq) yield pkgreqs def test_pkgbase_not_found(client: TestClient): with client as request: resp = request.get("/pkgbase/not_found") assert resp.status_code == int(HTTPStatus.NOT_FOUND) def test_pkgbase_redirect(client: TestClient, package: Package): with client as request: resp = request.get(f"/pkgbase/{package.Name}") assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/packages/{package.Name}" def test_pkgbase(client: TestClient, package: Package): with db.begin(): second = db.create(Package, Name="second-pkg", PackageBase=package.PackageBase) expected = [package.Name, second.Name] with client as request: resp = request.get(f"/pkgbase/{package.Name}") assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) # Check the details box title. title = root.find('.//div[@id="pkgdetails"]/h2') title, pkgname = title.text.split(": ") assert title == "Package Base Details" assert pkgname == package.Name pkgs = root.findall('.//div[@id="pkgs"]/ul/li/a') for i, name in enumerate(expected): assert pkgs[i].text.strip() == name def test_pkgbase_maintainer( client: TestClient, user: User, maintainer: User, package: Package ): """ Test that the Maintainer field is beind displayed correctly. Co-maintainers are displayed, if they exist, within a parens after the maintainer. """ with db.begin(): db.create( PackageComaintainer, User=user, PackageBase=package.PackageBase, Priority=1 ) with client as request: resp = request.get(f"/pkgbase/{package.Name}", follow_redirects=True) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) maint = root.xpath('//table[@id="pkginfo"]/tr[@class="pkgmaint"]/td')[0] maint, comaint = maint.text.strip().split() assert maint == maintainer.Username assert comaint == f"({user.Username})" def test_pkgbase_voters(client: TestClient, tu_user: User, package: Package): pkgbase = package.PackageBase endpoint = f"/pkgbase/{pkgbase.Name}/voters" now = time.utcnow() with db.begin(): db.create(PackageVote, User=tu_user, PackageBase=pkgbase, VoteTS=now) cookies = {"AURSID": tu_user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) # We should've gotten one link to the voter, tu_user. root = parse_root(resp.text) rows = root.xpath('//div[@class="box"]//ul/li/a') assert len(rows) == 1 assert rows[0].text.strip() == tu_user.Username def test_pkgbase_voters_unauthorized(client: TestClient, user: User, package: Package): pkgbase = package.PackageBase endpoint = f"/pkgbase/{pkgbase.Name}/voters" now = time.utcnow() with db.begin(): db.create(PackageVote, User=user, PackageBase=pkgbase, VoteTS=now) with client as request: resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}" def test_pkgbase_comment_not_found( client: TestClient, maintainer: User, package: Package ): cookies = {"AURSID": maintainer.login(Request(), "testPassword")} comment_id = 12345 # A non-existing comment. endpoint = f"/pkgbase/{package.PackageBase.Name}/comments/{comment_id}" with client as request: request.cookies = cookies resp = request.post(endpoint, data={"comment": "Failure"}) assert resp.status_code == int(HTTPStatus.NOT_FOUND) def test_pkgbase_comment_form_unauthorized( client: TestClient, user: User, maintainer: User, package: Package ): now = time.utcnow() with db.begin(): comment = db.create( PackageComment, PackageBase=package.PackageBase, User=maintainer, Comments="Test", RenderedComment=str(), CommentTS=now, ) cookies = {"AURSID": user.login(Request(), "testPassword")} pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}/comments/{comment.ID}/form" with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.UNAUTHORIZED) def test_pkgbase_comment_form_not_found( client: TestClient, maintainer: User, package: Package ): cookies = {"AURSID": maintainer.login(Request(), "testPassword")} comment_id = 12345 # A non-existing comment. pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}/comments/{comment_id}/form" with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.NOT_FOUND) def test_pkgbase_comments_missing_comment( client: TestClient, maintainer: User, package: Package ): cookies = {"AURSID": maintainer.login(Request(), "testPassword")} endpoint = f"/pkgbase/{package.PackageBase.Name}/comments" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) def test_pkgbase_comments( client: TestClient, maintainer: User, user: User, package: Package ): """This test includes tests against the following routes: - GET /pkgbase/{name} (to check notification checkbox) - POST /pkgbase/{name}/comments - GET /pkgbase/{name} (to check comments) - Tested against a comment created with the POST route - GET /pkgbase/{name}/comments/{id}/form - Tested against a comment created with the POST route """ cookies = {"AURSID": maintainer.login(Request(), "testPassword")} pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}" with client as request: request.cookies = cookies resp = request.get( endpoint, follow_redirects=True, ) assert resp.status_code == int(HTTPStatus.OK) # Make sure we got our checkbox for enabling notifications root = parse_root(resp.text) input = root.find('//input[@id="id_enable_notifications"]') assert input is not None # create notification with db.begin(): user.CommentNotify = 1 db.create(PackageNotification, PackageBase=package.PackageBase, User=user) # post a comment endpoint = f"/pkgbase/{pkgbasename}/comments" with client as request: request.cookies = cookies resp = request.post( endpoint, data={"comment": "Test comment.", "enable_notifications": True}, ) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # user should've gotten a CommentNotification email. assert Email.count() == 1 expected_prefix = f"/pkgbase/{pkgbasename}" prefix_len = len(expected_prefix) assert resp.headers.get("location")[:prefix_len] == expected_prefix with client as request: resp = request.get(resp.headers.get("location"), follow_redirects=True) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) headers = root.xpath('//h4[@class="comment-header"]') bodies = root.xpath('//div[@class="article-content"]/div/p') assert len(headers) == 1 assert len(bodies) == 1 assert bodies[0].text.strip() == "Test comment." comment_id = headers[0].attrib["id"].split("-")[-1] # Since we've enabled notifications already, # there should be no checkbox on our page input = root.find('//input[@id="id_enable_notifications"]') assert input is None # Test the non-javascript version of comment editing by # visiting the /pkgbase/{name}/comments/{id}/edit route. with client as request: request.cookies = cookies resp = request.get(f"{endpoint}/{comment_id}/edit") assert resp.status_code == int(HTTPStatus.OK) # Clear up the PackageNotification. This doubles as testing # that the notification was created and clears it up so we can # test enabling it during edit. pkgbase = package.PackageBase db_notif = pkgbase.notifications.filter( PackageNotification.UserID == maintainer.ID ).first() with db.begin(): db.delete(db_notif) # Now, let's edit the comment we just created. comment_id = int(headers[0].attrib["id"].split("-")[-1]) endpoint = f"/pkgbase/{pkgbasename}/comments/{comment_id}" with client as request: request.cookies = cookies resp = request.post( endpoint, data={"comment": "Edited comment.", "enable_notifications": True}, ) assert resp.status_code == int(HTTPStatus.SEE_OTHER) with client as request: resp = request.get(resp.headers.get("location"), follow_redirects=True) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) headers = root.xpath('//h4[@class="comment-header"]') bodies = root.xpath('//div[@class="article-content"]/div/p') assert len(headers) == 1 assert len(bodies) == 1 assert bodies[0].text.strip() == "Edited comment." # Ensure that a notification was created. db_notif = pkgbase.notifications.filter( PackageNotification.UserID == maintainer.ID ).first() assert db_notif is not None # Now, let's edit again, but cancel. endpoint = f"/pkgbase/{pkgbasename}/comments/{comment_id}" with client as request: request.cookies = cookies resp = request.post( endpoint, data={"comment": "Edited comment with cancel.", "cancel": True}, ) assert resp.status_code == int(HTTPStatus.SEE_OTHER) with client as request: resp = request.get(resp.headers.get("location"), follow_redirects=True) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) bodies = root.xpath('//div[@class="article-content"]/div/p') # Make sure our comment was NOT changed assert bodies[0].text.strip() == "Edited comment." # Delete notification for next test. with db.begin(): db.delete(db_notif) # Let's edit the comment again; This time we don't change the text. # However we do enable notifications. with client as request: request.cookies = cookies resp = request.post( endpoint, data={"comment": "Edited comment.", "enable_notifications": True}, ) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Ensure that a notification was created. db_notif = pkgbase.notifications.filter( PackageNotification.UserID == maintainer.ID ).first() assert db_notif is not None # Don't supply a comment; should return BAD_REQUEST. with client as request: request.cookies = cookies fail_resp = request.post(endpoint) assert fail_resp.status_code == int(HTTPStatus.BAD_REQUEST) # Now, test the form route, which should return form markup # via JSON. endpoint = f"{endpoint}/form" with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) data = resp.json() assert "form" in data def test_pkgbase_comment_edit_unauthorized( client: TestClient, user: User, maintainer: User, package: Package, comment: PackageComment, ): pkgbase = package.PackageBase cookies = {"AURSID": maintainer.login(Request(), "testPassword")} with client as request: request.cookies = cookies endp = f"/pkgbase/{pkgbase.Name}/comments/{comment.ID}" response = request.post( endp, data={"comment": "abcd im trying to change this comment."}, ) assert response.status_code == HTTPStatus.UNAUTHORIZED def test_pkgbase_comment_delete( client: TestClient, maintainer: User, user: User, package: Package, comment: PackageComment, ): # Test the unauthorized case of comment deletion. cookies = {"AURSID": user.login(Request(), "testPassword")} pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}/comments/{comment.ID}/delete" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) expected = f"/pkgbase/{pkgbasename}" assert resp.headers.get("location") == expected # Test the unauthorized case of comment undeletion. maint_cookies = {"AURSID": maintainer.login(Request(), "testPassword")} endpoint = f"/pkgbase/{pkgbasename}/comments/{comment.ID}/undelete" with client as request: request.cookies = maint_cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.UNAUTHORIZED) # And move on to undeleting it. with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) def test_pkgbase_comment_delete_unauthorized( client: TestClient, maintainer: User, package: Package, comment: PackageComment ): # Test the unauthorized case of comment deletion. cookies = {"AURSID": maintainer.login(Request(), "testPassword")} pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}/comments/{comment.ID}/delete" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.UNAUTHORIZED) def test_pkgbase_comment_delete_not_found( client: TestClient, maintainer: User, package: Package ): cookies = {"AURSID": maintainer.login(Request(), "testPassword")} comment_id = 12345 # Non-existing comment. pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}/comments/{comment_id}/delete" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.NOT_FOUND) def test_pkgbase_comment_undelete_not_found( client: TestClient, maintainer: User, package: Package ): cookies = {"AURSID": maintainer.login(Request(), "testPassword")} comment_id = 12345 # Non-existing comment. pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}/comments/{comment_id}/undelete" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.NOT_FOUND) def test_pkgbase_comment_pin_as_co( client: TestClient, package: Package, comment: PackageComment ): comaint = create_user("comaint1") with db.begin(): db.create( PackageComaintainer, PackageBase=package.PackageBase, User=comaint, Priority=1, ) # Pin the comment. pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}/comments/{comment.ID}/pin" cookies = {"AURSID": comaint.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Assert that PinnedTS got set. assert comment.PinnedTS > 0 # Unpin the comment we just pinned. endpoint = f"/pkgbase/{pkgbasename}/comments/{comment.ID}/unpin" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Let's assert that PinnedTS was unset. assert comment.PinnedTS == 0 def test_pkgbase_comment_pin( client: TestClient, maintainer: User, package: Package, comment: PackageComment ): cookies = {"AURSID": maintainer.login(Request(), "testPassword")} comment_id = comment.ID pkgbasename = package.PackageBase.Name # Pin the comment. endpoint = f"/pkgbase/{pkgbasename}/comments/{comment_id}/pin" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Assert that PinnedTS got set. assert comment.PinnedTS > 0 # Unpin the comment we just pinned. endpoint = f"/pkgbase/{pkgbasename}/comments/{comment_id}/unpin" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Let's assert that PinnedTS was unset. assert comment.PinnedTS == 0 def test_pkgbase_comment_pin_unauthorized( client: TestClient, user: User, package: Package, comment: PackageComment ): cookies = {"AURSID": user.login(Request(), "testPassword")} comment_id = comment.ID pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}/comments/{comment_id}/pin" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.UNAUTHORIZED) def test_pkgbase_comment_unpin_unauthorized( client: TestClient, user: User, package: Package, comment: PackageComment ): cookies = {"AURSID": user.login(Request(), "testPassword")} comment_id = comment.ID pkgbasename = package.PackageBase.Name endpoint = f"/pkgbase/{pkgbasename}/comments/{comment_id}/unpin" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.UNAUTHORIZED) def test_pkgbase_comaintainers_not_found(client: TestClient, maintainer: User): cookies = {"AURSID": maintainer.login(Request(), "testPassword")} endpoint = "/pkgbase/fake/comaintainers" with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.NOT_FOUND) def test_pkgbase_comaintainers_post_not_found(client: TestClient, maintainer: User): cookies = {"AURSID": maintainer.login(Request(), "testPassword")} endpoint = "/pkgbase/fake/comaintainers" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.NOT_FOUND) def test_pkgbase_comaintainers_unauthorized( client: TestClient, user: User, package: Package ): pkgbase = package.PackageBase endpoint = f"/pkgbase/{pkgbase.Name}/comaintainers" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}" def test_pkgbase_comaintainers_post_unauthorized( client: TestClient, user: User, package: Package ): pkgbase = package.PackageBase endpoint = f"/pkgbase/{pkgbase.Name}/comaintainers" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}" def test_pkgbase_comaintainers_post_invalid_user( client: TestClient, maintainer: User, package: Package ): pkgbase = package.PackageBase endpoint = f"/pkgbase/{pkgbase.Name}/comaintainers" cookies = {"AURSID": maintainer.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post(endpoint, data={"users": "\nfake\n"}) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) error = root.xpath('//ul[@class="errorlist"]/li')[0] assert error.text.strip() == "Invalid user name: fake" def test_pkgbase_comaintainers( client: TestClient, user: User, maintainer: User, package: Package ): pkgbase = package.PackageBase endpoint = f"/pkgbase/{pkgbase.Name}/comaintainers" cookies = {"AURSID": maintainer.login(Request(), "testPassword")} # Start off by adding user as a comaintainer to package. # The maintainer username given should be ignored. with client as request: request.cookies = cookies resp = request.post( endpoint, data={"users": f"\n{user.Username}\n{maintainer.Username}\n"}, ) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}" # Do it again to exercise the last_priority bump path. with client as request: request.cookies = cookies resp = request.post( endpoint, data={"users": f"\n{user.Username}\n{maintainer.Username}\n"}, ) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}" # Now that we've added a comaintainer to the pkgbase, # let's perform a GET request to make sure that the backend produces # the user we added in the users textarea. with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) users = root.xpath('//textarea[@id="id_users"]')[0] assert users.text.strip() == user.Username # Finish off by removing all the comaintainers. with client as request: request.cookies = cookies resp = request.post(endpoint, data={"users": str()}) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}" with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) users = root.xpath('//textarea[@id="id_users"]')[0] assert users is not None and users.text is None def test_pkgbase_request_not_found(client: TestClient, user: User): pkgbase_name = "fake" endpoint = f"/pkgbase/{pkgbase_name}/request" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.NOT_FOUND) def test_pkgbase_request(client: TestClient, user: User, package: Package): pkgbase = package.PackageBase endpoint = f"/pkgbase/{pkgbase.Name}/request" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) def test_pkgbase_request_post_not_found(client: TestClient, user: User): cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post("/pkgbase/fake/request", data={"type": "fake"}) assert resp.status_code == int(HTTPStatus.NOT_FOUND) def test_pkgbase_request_post_invalid_type( client: TestClient, user: User, package: Package ): endpoint = f"/pkgbase/{package.PackageBase.Name}/request" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post(endpoint, data={"type": "fake"}) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) def test_pkgbase_request_post_no_comment_error( client: TestClient, user: User, package: Package ): endpoint = f"/pkgbase/{package.PackageBase.Name}/request" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post( endpoint, data={ "type": "deletion", "comments": "", # An empty comment field causes an error. }, ) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) error = root.xpath('//ul[@class="errorlist"]/li')[0] expected = "The comment field must not be empty." assert error.text.strip() == expected def test_pkgbase_request_post_merge_not_found_error( client: TestClient, user: User, package: Package ): endpoint = f"/pkgbase/{package.PackageBase.Name}/request" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post( endpoint, data={ "type": "merge", "merge_into": "fake", # There is no PackageBase.Name "fake" "comments": "We want to merge this.", }, ) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) error = root.xpath('//ul[@class="errorlist"]/li')[0] expected = "The package base you want to merge into does not exist." assert error.text.strip() == expected def test_pkgbase_request_post_merge_no_merge_into_error( client: TestClient, user: User, package: Package ): endpoint = f"/pkgbase/{package.PackageBase.Name}/request" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post( endpoint, data={ "type": "merge", "merge_into": "", # There is no PackageBase.Name "fake" "comments": "We want to merge this.", }, ) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) error = root.xpath('//ul[@class="errorlist"]/li')[0] expected = 'The "Merge into" field must not be empty.' assert error.text.strip() == expected def test_pkgbase_request_post_merge_self_error( client: TestClient, user: User, package: Package ): endpoint = f"/pkgbase/{package.PackageBase.Name}/request" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post( endpoint, data={ "type": "merge", "merge_into": package.PackageBase.Name, "comments": "We want to merge this.", }, ) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) error = root.xpath('//ul[@class="errorlist"]/li')[0] expected = "You cannot merge a package base into itself." assert error.text.strip() == expected def test_pkgbase_flag( client: TestClient, user: User, maintainer: User, package: Package ): pkgbase = package.PackageBase # We shouldn't have flagged the package yet; assert so. assert pkgbase.OutOfDateTS is None cookies = {"AURSID": user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{pkgbase.Name}/flag" # Get the flag page. with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) # Now, let's check the /pkgbase/{name}/flag-comment route. flag_comment_endpoint = f"/pkgbase/{pkgbase.Name}/flag-comment" with client as request: request.cookies = cookies resp = request.get(flag_comment_endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}" # Try to flag it without a comment. with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) # Flag it with a valid comment. with client as request: request.cookies = cookies resp = request.post(endpoint, data={"comments": "Test"}) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert pkgbase.Flagger == user assert pkgbase.FlaggerComment == "Test" # Should've gotten a FlagNotification. assert Email.count() == 1 # Now, let's check the /pkgbase/{name}/flag-comment route. flag_comment_endpoint = f"/pkgbase/{pkgbase.Name}/flag-comment" with client as request: request.cookies = cookies resp = request.get(flag_comment_endpoint) assert resp.status_code == int(HTTPStatus.OK) # Now try to perform a get; we should be redirected because # it's already flagged. with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) with db.begin(): user2 = db.create( User, Username="test2", Email="test2@example.org", Passwd="testPassword", AccountType=user.AccountType, ) # Now, test that the 'user2' user can't unflag it, because they # didn't flag it to begin with. user2_cookies = {"AURSID": user2.login(Request(), "testPassword")} endpoint = f"/pkgbase/{pkgbase.Name}/unflag" with client as request: request.cookies = user2_cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert pkgbase.Flagger == user # Now, test that the 'maintainer' user can. maint_cookies = {"AURSID": maintainer.login(Request(), "testPassword")} with client as request: request.cookies = maint_cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert pkgbase.Flagger is None # Flag it again. with client as request: request.cookies = cookies resp = request.post(f"/pkgbase/{pkgbase.Name}/flag", data={"comments": "Test"}) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Now, unflag it for real. with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert pkgbase.Flagger is None def test_pkgbase_flag_vcs(client: TestClient, user: User, package: Package): # Morph our package fixture into a VCS package (-git). with db.begin(): package.PackageBase.Name += "-git" package.Name += "-git" cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.get(f"/pkgbase/{package.PackageBase.Name}/flag") assert resp.status_code == int(HTTPStatus.OK) expected = ( "This seems to be a VCS package. Please do " "not flag it out-of-date if the package " "version in the AUR does not match the most recent commit. " "Flagging this package should only be done if the sources " "moved or changes in the PKGBUILD are required because of " "recent upstream changes." ) assert expected in resp.text def test_pkgbase_notify(client: TestClient, user: User, package: Package): pkgbase = package.PackageBase # We have no notif record yet; assert that. notif = pkgbase.notifications.filter(PackageNotification.UserID == user.ID).first() assert notif is None # Enable notifications. cookies = {"AURSID": user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{pkgbase.Name}/notify" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) notif = pkgbase.notifications.filter(PackageNotification.UserID == user.ID).first() assert notif is not None # Disable notifications. endpoint = f"/pkgbase/{pkgbase.Name}/unnotify" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) notif = pkgbase.notifications.filter(PackageNotification.UserID == user.ID).first() assert notif is None def test_pkgbase_vote(client: TestClient, user: User, package: Package): pkgbase = package.PackageBase # We haven't voted yet. vote = pkgbase.package_votes.filter(PackageVote.UsersID == user.ID).first() assert vote is None # Vote for the package. cookies = {"AURSID": user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{pkgbase.Name}/vote" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) vote = pkgbase.package_votes.filter(PackageVote.UsersID == user.ID).first() assert vote is not None assert pkgbase.NumVotes == 1 # Remove vote. endpoint = f"/pkgbase/{pkgbase.Name}/unvote" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) vote = pkgbase.package_votes.filter(PackageVote.UsersID == user.ID).first() assert vote is None assert pkgbase.NumVotes == 0 def test_pkgbase_disown_as_sole_maintainer( client: TestClient, maintainer: User, package: Package ): cookies = {"AURSID": maintainer.login(Request(), "testPassword")} pkgbase = package.PackageBase endpoint = f"/pkgbase/{pkgbase.Name}/disown" # But we do here. with client as request: request.cookies = cookies resp = request.post(endpoint, data={"confirm": True}) assert resp.status_code == int(HTTPStatus.SEE_OTHER) def test_pkgbase_disown_as_maint_with_comaint( client: TestClient, user: User, maintainer: User, package: Package ): """When disowning as a maintainer, the lowest priority comaintainer is promoted to maintainer.""" pkgbase = package.PackageBase endp = f"/pkgbase/{pkgbase.Name}/disown" post_data = {"confirm": True} with db.begin(): db.create(PackageComaintainer, PackageBase=pkgbase, User=user, Priority=1) maint_cookies = {"AURSID": maintainer.login(Request(), "testPassword")} with client as request: request.cookies = maint_cookies resp = request.post(endp, data=post_data, follow_redirects=True) assert resp.status_code == int(HTTPStatus.OK) package = db.refresh(package) pkgbase = package.PackageBase assert pkgbase.Maintainer == user assert pkgbase.comaintainers.count() == 0 def test_pkgbase_disown( client: TestClient, user: User, maintainer: User, comaintainer: User, package: Package, ): maint_cookies = {"AURSID": maintainer.login(Request(), "testPassword")} comaint_cookies = {"AURSID": comaintainer.login(Request(), "testPassword")} user_cookies = {"AURSID": user.login(Request(), "testPassword")} pkgbase = package.PackageBase pkgbase_endp = f"/pkgbase/{pkgbase.Name}" endpoint = f"{pkgbase_endp}/disown" with db.begin(): db.create( PackageComaintainer, User=comaintainer, PackageBase=pkgbase, Priority=1 ) # GET as a normal user, which is rejected for lack of credentials. with client as request: request.cookies = user_cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # GET as a comaintainer. with client as request: request.cookies = comaint_cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) # Ensure that the comaintainer can see "Disown Package" link with client as request: request.cookies = comaint_cookies resp = request.get(pkgbase_endp, follow_redirects=True) assert "Disown Package" in resp.text # GET as the maintainer. with client as request: request.cookies = maint_cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) # Ensure that the maintainer can see "Disown Package" link with client as request: request.cookies = maint_cookies resp = request.get(pkgbase_endp, follow_redirects=True) assert "Disown Package" in resp.text # POST as a normal user, which is rejected for lack of credentials. with client as request: request.cookies = user_cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # POST as the comaintainer without "confirm". with client as request: request.cookies = comaint_cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) # POST as the maintainer without "confirm". with client as request: request.cookies = maint_cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) # POST as the comaintainer with "confirm". with client as request: request.cookies = comaint_cookies resp = request.post(endpoint, data={"confirm": True}) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # POST as the maintainer with "confirm". with client as request: request.cookies = maint_cookies resp = request.post(endpoint, data={"confirm": True}) assert resp.status_code == int(HTTPStatus.SEE_OTHER) def test_pkgbase_adopt( client: TestClient, user: User, tu_user: User, maintainer: User, package: Package ): # Unset the maintainer as if package is orphaned. with db.begin(): package.PackageBase.Maintainer = None pkgbasename = package.PackageBase.Name cookies = {"AURSID": maintainer.login(Request(), "testPassword")} endpoint = f"/pkgbase/{pkgbasename}/adopt" # Adopt the package base. with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert package.PackageBase.Maintainer == maintainer # Try to adopt it when it already has a maintainer; nothing changes. user_cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = user_cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert package.PackageBase.Maintainer == maintainer # Steal the package as a TU. tu_cookies = {"AURSID": tu_user.login(Request(), "testPassword")} with client as request: request.cookies = tu_cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert package.PackageBase.Maintainer == tu_user def test_pkgbase_delete_unauthorized(client: TestClient, user: User, package: Package): pkgbase = package.PackageBase cookies = {"AURSID": user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{pkgbase.Name}/delete" # Test GET. with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}" # Test POST. with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}" def test_pkgbase_delete(client: TestClient, tu_user: User, package: Package): pkgbase = package.PackageBase # Test that the GET request works. cookies = {"AURSID": tu_user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{pkgbase.Name}/delete" with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) # Test that POST works and denies us because we haven't confirmed. with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) # Test that we can actually delete the pkgbase. with client as request: request.cookies = cookies resp = request.post(endpoint, data={"confirm": True}) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Let's assert that the package base record got removed. record = db.query(PackageBase).filter(PackageBase.Name == pkgbase.Name).first() assert record is None # Two emails should've been sent out; an autogenerated # request's accepted notification and a deletion notification. assert Email.count() == 1 req_close = Email(1).parse() expr = r"^\[PRQ#\d+\] Deletion Request for [^ ]+ Accepted$" subject = req_close.headers.get("Subject") assert re.match(expr, subject) def test_pkgbase_delete_with_request( client: TestClient, tu_user: User, pkgbase: PackageBase, pkgreq: PackageRequest ): # TODO: Test that a previously existing request gets Accepted when # a TU deleted the package. # Delete the package as `tu_user` via POST request. cookies = {"AURSID": tu_user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{pkgbase.Name}/delete" with client as request: request.cookies = cookies resp = request.post(endpoint, data={"confirm": True}) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == "/packages" # We should've just sent one closure email since `pkgreq` exists. assert Email.count() == 1 # Make sure it was a closure for the deletion request. email = Email(1).parse() expr = r"^\[PRQ#\d+\] Deletion Request for [^ ]+ Accepted$" assert re.match(expr, email.headers.get("Subject")) def test_packages_post_unknown_action(client: TestClient, user: User, package: Package): cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post( "/packages", data={"action": "unknown"}, ) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) def test_packages_post_error(client: TestClient, user: User, package: Package): async def stub_action(request: Request, **kwargs): return False, ["Some error."] actions = {"stub": stub_action} with mock.patch.dict("aurweb.routers.packages.PACKAGE_ACTIONS", actions): cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post( "/packages", data={"action": "stub"}, ) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) errors = get_errors(resp.text) expected = "Some error." assert errors[0].text.strip() == expected def test_packages_post(client: TestClient, user: User, package: Package): async def stub_action(request: Request, **kwargs): return True, ["Some success."] actions = {"stub": stub_action} with mock.patch.dict("aurweb.routers.packages.PACKAGE_ACTIONS", actions): cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies resp = request.post( "/packages", data={"action": "stub"}, ) assert resp.status_code == int(HTTPStatus.OK) errors = get_successes(resp.text) expected = "Some success." assert errors[0].text.strip() == expected def test_pkgbase_merge_unauthorized(client: TestClient, user: User, package: Package): cookies = {"AURSID": user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{package.PackageBase.Name}/merge" with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.UNAUTHORIZED) def test_pkgbase_merge(client: TestClient, tu_user: User, package: Package): cookies = {"AURSID": tu_user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{package.PackageBase.Name}/merge" with client as request: request.cookies = cookies resp = request.get(endpoint) assert resp.status_code == int(HTTPStatus.OK) assert not get_errors(resp.text) def test_pkgbase_merge_post_unauthorized( client: TestClient, user: User, package: Package ): cookies = {"AURSID": user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{package.PackageBase.Name}/merge" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.UNAUTHORIZED) def test_pkgbase_merge_post_unconfirmed( client: TestClient, tu_user: User, package: Package ): cookies = {"AURSID": tu_user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{package.PackageBase.Name}/merge" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) errors = get_errors(resp.text) expected = ( "The selected packages have not been deleted, " "check the confirmation checkbox." ) assert errors[0].text.strip() == expected def test_pkgbase_merge_post_invalid_into( client: TestClient, tu_user: User, package: Package ): cookies = {"AURSID": tu_user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{package.PackageBase.Name}/merge" with client as request: request.cookies = cookies resp = request.post(endpoint, data={"into": "not_real", "confirm": True}) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) errors = get_errors(resp.text) expected = "Cannot find package to merge votes and comments into." assert errors[0].text.strip() == expected def test_pkgbase_merge_post_self_invalid( client: TestClient, tu_user: User, package: Package ): cookies = {"AURSID": tu_user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{package.PackageBase.Name}/merge" with client as request: request.cookies = cookies resp = request.post( endpoint, data={"into": package.PackageBase.Name, "confirm": True}, ) assert resp.status_code == int(HTTPStatus.BAD_REQUEST) errors = get_errors(resp.text) expected = "Cannot merge a package base with itself." assert errors[0].text.strip() == expected def test_pkgbase_merge_post( client: TestClient, tu_user: User, package: Package, pkgbase: PackageBase, target: PackageBase, pkgreq: PackageRequest, ): pkgname = package.Name pkgbasename = pkgbase.Name # Create a merge request destined for another target. # This will allow our test code to exercise closing # such a request after merging the pkgbase in question. with db.begin(): pkgreq.ReqTypeID = MERGE_ID pkgreq.MergeBaseName = target.Name # Vote for the package. cookies = {"AURSID": tu_user.login(Request(), "testPassword")} endpoint = f"/pkgbase/{package.PackageBase.Name}/vote" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Enable notifications. endpoint = f"/pkgbase/{package.PackageBase.Name}/notify" with client as request: request.cookies = cookies resp = request.post(endpoint) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Comment on the package. endpoint = f"/pkgbase/{package.PackageBase.Name}/comments" with client as request: request.cookies = cookies resp = request.post( endpoint, data={"comment": "Test comment."}, ) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Save these relationships for later comparison. comments = package.PackageBase.comments.all() notifs = package.PackageBase.notifications.all() votes = package.PackageBase.package_votes.all() # Merge the package into target. endpoint = f"/pkgbase/{package.PackageBase.Name}/merge" with client as request: request.cookies = cookies resp = request.post(endpoint, data={"into": target.Name, "confirm": True}) assert resp.status_code == int(HTTPStatus.SEE_OTHER) loc = resp.headers.get("location") assert loc == f"/pkgbase/{target.Name}" # Two emails should've been sent out. assert Email.count() == 1 email_body = Email(1).parse().glue() assert f"Merge Request for {pkgbasename} Accepted" in email_body # Assert that the original comments, notifs and votes we setup # got migrated to target as intended. assert comments == target.comments.all() assert notifs == target.notifications.all() assert votes == target.package_votes.all() # ...and that the package got deleted. package = db.query(Package).filter(Package.Name == pkgname).first() assert package is None # Our previously-made request should have gotten accepted. assert pkgreq.Status == ACCEPTED_ID assert pkgreq.Closer is not None # A PackageRequest is always created when merging this way. pkgreq = ( db.query(PackageRequest) .filter( and_( PackageRequest.ReqTypeID == MERGE_ID, PackageRequest.PackageBaseName == pkgbasename, PackageRequest.MergeBaseName == target.Name, ) ) .first() ) assert pkgreq is not None def test_pkgbase_keywords(client: TestClient, user: User, package: Package): endpoint = f"/pkgbase/{package.PackageBase.Name}" with client as request: resp = request.get(endpoint, follow_redirects=True) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) keywords = root.xpath('//a[@class="keyword"]') assert len(keywords) == 0 maint = package.PackageBase.Maintainer cookies = {"AURSID": maint.login(Request(), "testPassword")} post_endpoint = f"{endpoint}/keywords" with client as request: request.cookies = cookies resp = request.post( post_endpoint, data={"keywords": "abc test"}, ) assert resp.status_code == int(HTTPStatus.SEE_OTHER) with client as request: request.cookies = {} resp = request.get(resp.headers.get("location"), follow_redirects=True) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) keywords = root.xpath('//a[@class="keyword"]') assert len(keywords) == 2 expected = ["abc", "test"] for i, keyword in enumerate(keywords): assert keyword.text.strip() == expected[i] def test_pkgbase_empty_keywords(client: TestClient, user: User, package: Package): endpoint = f"/pkgbase/{package.PackageBase.Name}" with client as request: request.cookies = {} resp = request.get(endpoint, follow_redirects=True) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) keywords = root.xpath('//a[@class="keyword"]') assert len(keywords) == 0 maint = package.PackageBase.Maintainer cookies = {"AURSID": maint.login(Request(), "testPassword")} post_endpoint = f"{endpoint}/keywords" with client as request: request.cookies = cookies resp = request.post( post_endpoint, data={"keywords": "abc test foo bar "}, ) assert resp.status_code == int(HTTPStatus.SEE_OTHER) with client as request: request.cookies = {} resp = request.get(resp.headers.get("location"), follow_redirects=True) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) keywords = root.xpath('//a[@class="keyword"]') assert len(keywords) == 4 expected = ["abc", "bar", "foo", "test"] for i, keyword in enumerate(keywords): assert keyword.text.strip() == expected[i] def test_unauthorized_pkgbase_keywords(client: TestClient, package: Package): with db.begin(): user = db.create( User, Username="random_user", Email="random_user", Passwd="testPassword" ) cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: request.cookies = cookies pkgbase = package.PackageBase endp = f"/pkgbase/{pkgbase.Name}/keywords" response = request.post(endp) assert response.status_code == HTTPStatus.UNAUTHORIZED def test_independent_user_unflag(client: TestClient, user: User, package: Package): with db.begin(): flagger = db.create( User, Username="test_flagger", Email="test_flagger@example.com", Passwd="testPassword", ) pkgbase = package.PackageBase cookies = {"AURSID": flagger.login(Request(), "testPassword")} with client as request: request.cookies = cookies endp = f"/pkgbase/{pkgbase.Name}/flag" response = request.post( endp, data={"comments": "This thing needs a flag!"}, follow_redirects=True, ) assert response.status_code == HTTPStatus.OK # At this point, we've flagged it as `flagger`. # Now, we should be able to view the "Unflag package" link on the package # page when browsing as that `flagger` user. with client as request: endp = f"/pkgbase/{pkgbase.Name}" request.cookies = cookies response = request.get(endp, follow_redirects=True) assert response.status_code == HTTPStatus.OK # Assert that the "Unflag package" link appears in the DOM. root = parse_root(response.text) elems = root.xpath('//input[@name="do_UnFlag"]') assert len(elems) == 1 # Now, unflag the package by "clicking" the "Unflag package" link. with client as request: endp = f"/pkgbase/{pkgbase.Name}/unflag" request.cookies = cookies response = request.post(endp, follow_redirects=True) assert response.status_code == HTTPStatus.OK # For the last time, let's check the GET response. The package should # not show as flagged anymore, and thus the "Unflag package" link # should be missing. with client as request: endp = f"/pkgbase/{pkgbase.Name}" request.cookies = cookies response = request.get(endp, follow_redirects=True) assert response.status_code == HTTPStatus.OK # Assert that the "Unflag package" link does not appear in the DOM. root = parse_root(response.text) elems = root.xpath('//input[@name="do_UnFlag"]') assert len(elems) == 0