mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
Merge branch 'postgres-sqlalchemy2' into 'master'
Draft: temp: Sandbox with postgres and sqlalchemy2 See merge request archlinux/aurweb!778
This commit is contained in:
commit
a25faaa87e
71 changed files with 807 additions and 743 deletions
|
@ -60,7 +60,7 @@ other user:
|
|||
|
||||
GRANT ALL ON *.* TO 'user'@'localhost' WITH GRANT OPTION
|
||||
|
||||
The aurweb platform is intended to use the `mysql` backend, but
|
||||
The aurweb platform is intended to use the `postgresql` backend, but
|
||||
the `sqlite` backend is still used for sharness tests. These tests
|
||||
will soon be replaced with pytest suites and `sqlite` removed.
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ from multiprocessing import Lock
|
|||
import py
|
||||
import pytest
|
||||
from prometheus_client import values
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy import create_engine, text
|
||||
from sqlalchemy.engine import URL
|
||||
from sqlalchemy.engine.base import Engine
|
||||
from sqlalchemy.exc import ProgrammingError
|
||||
|
@ -52,6 +52,7 @@ from sqlalchemy.orm import scoped_session
|
|||
|
||||
import aurweb.config
|
||||
import aurweb.db
|
||||
import aurweb.schema
|
||||
from aurweb import aur_logging, initdb, testing
|
||||
from aurweb.testing.email import Email
|
||||
from aurweb.testing.git import GitRepository
|
||||
|
@ -68,25 +69,28 @@ values.ValueClass = values.MutexValue
|
|||
|
||||
def test_engine() -> Engine:
|
||||
"""
|
||||
Return a privileged SQLAlchemy engine with no database.
|
||||
Return a privileged SQLAlchemy engine with default database.
|
||||
|
||||
This method is particularly useful for providing an engine that
|
||||
can be used to create and drop databases from an SQL server.
|
||||
|
||||
:return: SQLAlchemy Engine instance (not connected to a database)
|
||||
:return: SQLAlchemy Engine instance (connected to a default)
|
||||
"""
|
||||
unix_socket = aurweb.config.get_with_fallback("database", "socket", None)
|
||||
socket = aurweb.config.get_with_fallback("database", "socket", None)
|
||||
host = aurweb.config.get_with_fallback("database", "host", None)
|
||||
port = aurweb.config.get_with_fallback("database", "port", None)
|
||||
|
||||
kwargs = {
|
||||
"database": aurweb.config.get("database", "name"),
|
||||
"username": aurweb.config.get("database", "user"),
|
||||
"password": aurweb.config.get_with_fallback("database", "password", None),
|
||||
"host": aurweb.config.get("database", "host"),
|
||||
"port": aurweb.config.get_with_fallback("database", "port", None),
|
||||
"query": {"unix_socket": unix_socket},
|
||||
"host": socket if socket else host,
|
||||
"port": port if not socket else None,
|
||||
}
|
||||
|
||||
backend = aurweb.config.get("database", "backend")
|
||||
driver = aurweb.db.DRIVERS.get(backend)
|
||||
return create_engine(URL.create(driver, **kwargs))
|
||||
return create_engine(URL.create(driver, **kwargs), isolation_level="AUTOCOMMIT")
|
||||
|
||||
|
||||
class AlembicArgs:
|
||||
|
@ -109,15 +113,16 @@ def _create_database(engine: Engine, dbname: str) -> None:
|
|||
:param dbname: Database name to create
|
||||
"""
|
||||
conn = engine.connect()
|
||||
try:
|
||||
conn.execute(f"CREATE DATABASE {dbname}")
|
||||
except ProgrammingError: # pragma: no cover
|
||||
# The database most likely already existed if we hit
|
||||
# a ProgrammingError. Just drop the database and try
|
||||
# again. If at that point things still fail, any
|
||||
# exception will be propogated up to the caller.
|
||||
conn.execute(f"DROP DATABASE {dbname}")
|
||||
conn.execute(f"CREATE DATABASE {dbname}")
|
||||
with conn.begin():
|
||||
try:
|
||||
conn.execute(text(f"CREATE DATABASE {dbname}"))
|
||||
except ProgrammingError: # pragma: no cover
|
||||
# The database most likely already existed if we hit
|
||||
# a ProgrammingError. Just drop the database and try
|
||||
# again. If at that point things still fail, any
|
||||
# exception will be propogated up to the caller.
|
||||
conn.execute(text(f"DROP DATABASE {dbname} WITH (FORCE)"))
|
||||
conn.execute(text(f"CREATE DATABASE {dbname}"))
|
||||
conn.close()
|
||||
initdb.run(AlembicArgs)
|
||||
|
||||
|
@ -131,7 +136,7 @@ def _drop_database(engine: Engine, dbname: str) -> None:
|
|||
"""
|
||||
aurweb.schema.metadata.drop_all(bind=engine)
|
||||
conn = engine.connect()
|
||||
conn.execute(f"DROP DATABASE {dbname}")
|
||||
conn.execute(text(f"DROP DATABASE {dbname}"))
|
||||
conn.close()
|
||||
|
||||
|
||||
|
@ -178,6 +183,10 @@ def db_session(setup_database: None) -> scoped_session:
|
|||
session.close()
|
||||
aurweb.db.pop_session(dbname)
|
||||
|
||||
# Dispose engine and close connections
|
||||
aurweb.db.get_engine(dbname).dispose()
|
||||
aurweb.db.pop_engine(dbname)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db_test(db_session: scoped_session) -> None:
|
||||
|
|
|
@ -830,6 +830,7 @@ def test_post_account_edit_type_as_dev(client: TestClient, pm_user: User):
|
|||
request.cookies = cookies
|
||||
resp = request.post(endpoint, data=data)
|
||||
assert resp.status_code == int(HTTPStatus.OK)
|
||||
db.refresh(user2)
|
||||
assert user2.AccountTypeID == at.DEVELOPER_ID
|
||||
|
||||
|
||||
|
@ -850,6 +851,7 @@ def test_post_account_edit_invalid_type_as_pm(client: TestClient, pm_user: User)
|
|||
request.cookies = cookies
|
||||
resp = request.post(endpoint, data=data)
|
||||
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
|
||||
db.refresh(user2)
|
||||
assert user2.AccountTypeID == at.USER_ID
|
||||
|
||||
errors = get_errors(resp.text)
|
||||
|
@ -1020,6 +1022,7 @@ def test_post_account_edit_inactivity(client: TestClient, user: User):
|
|||
assert resp.status_code == int(HTTPStatus.OK)
|
||||
|
||||
# Make sure the user record got updated correctly.
|
||||
db.refresh(user)
|
||||
assert user.InactivityTS > 0
|
||||
|
||||
post_data.update({"J": False})
|
||||
|
@ -1028,6 +1031,7 @@ def test_post_account_edit_inactivity(client: TestClient, user: User):
|
|||
resp = request.post(f"/account/{user.Username}/edit", data=post_data)
|
||||
assert resp.status_code == int(HTTPStatus.OK)
|
||||
|
||||
db.refresh(user)
|
||||
assert user.InactivityTS == 0
|
||||
|
||||
|
||||
|
@ -1050,6 +1054,7 @@ def test_post_account_edit_suspended(client: TestClient, user: User):
|
|||
assert resp.status_code == int(HTTPStatus.OK)
|
||||
|
||||
# Make sure the user record got updated correctly.
|
||||
db.refresh(user)
|
||||
assert user.Suspended
|
||||
# Let's make sure the DB got updated properly.
|
||||
assert user.session is None
|
||||
|
@ -1207,6 +1212,7 @@ def test_post_account_edit_password(client: TestClient, user: User):
|
|||
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
db.refresh(user)
|
||||
assert user.valid_password("newPassword")
|
||||
|
||||
|
||||
|
@ -1273,6 +1279,7 @@ def test_post_account_edit_self_type_as_pm(client: TestClient, pm_user: User):
|
|||
resp = request.post(endpoint, data=data)
|
||||
assert resp.status_code == int(HTTPStatus.OK)
|
||||
|
||||
db.refresh(pm_user)
|
||||
assert pm_user.AccountTypeID == USER_ID
|
||||
|
||||
|
||||
|
@ -1308,6 +1315,7 @@ def test_post_account_edit_other_user_type_as_pm(
|
|||
assert resp.status_code == int(HTTPStatus.OK)
|
||||
|
||||
# Let's make sure the DB got updated properly.
|
||||
db.refresh(user2)
|
||||
assert user2.AccountTypeID == PACKAGE_MAINTAINER_ID
|
||||
|
||||
# and also that this got logged out at DEBUG level.
|
||||
|
|
|
@ -14,7 +14,7 @@ from aurweb.models.user import User
|
|||
from aurweb.testing.html import get_errors
|
||||
|
||||
# Some test global constants.
|
||||
TEST_USERNAME = "test"
|
||||
TEST_USERNAME = "Test"
|
||||
TEST_EMAIL = "test@example.org"
|
||||
TEST_REFERER = {
|
||||
"referer": aurweb.config.get("options", "aur_location") + "/login",
|
||||
|
@ -54,36 +54,37 @@ def user() -> User:
|
|||
|
||||
|
||||
def test_login_logout(client: TestClient, user: User):
|
||||
post_data = {"user": "test", "passwd": "testPassword", "next": "/"}
|
||||
for username in ["test", "TEst"]:
|
||||
post_data = {"user": username, "passwd": "testPassword", "next": "/"}
|
||||
|
||||
with client as request:
|
||||
# First, let's test get /login.
|
||||
response = request.get("/login")
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
with client as request:
|
||||
# First, let's test get /login.
|
||||
response = request.get("/login")
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
response = request.post("/login", data=post_data)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
response = request.post("/login", data=post_data)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
# Simulate following the redirect location from above's response.
|
||||
response = request.get(response.headers.get("location"))
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
# Simulate following the redirect location from above's response.
|
||||
response = request.get(response.headers.get("location"))
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
response = request.post("/logout", data=post_data)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
response = request.post("/logout", data=post_data)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
request.cookies = {"AURSID": response.cookies.get("AURSID")}
|
||||
response = request.post(
|
||||
"/logout",
|
||||
data=post_data,
|
||||
)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
request.cookies = {"AURSID": response.cookies.get("AURSID")}
|
||||
response = request.post(
|
||||
"/logout",
|
||||
data=post_data,
|
||||
)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
assert "AURSID" not in response.cookies
|
||||
assert "AURSID" not in response.cookies
|
||||
|
||||
|
||||
def test_login_suspended(client: TestClient, user: User):
|
||||
with db.begin():
|
||||
user.Suspended = 1
|
||||
user.Suspended = True
|
||||
|
||||
data = {"user": user.Username, "passwd": "testPassword", "next": "/"}
|
||||
with client as request:
|
||||
|
@ -184,23 +185,23 @@ def test_secure_login(getboolean: mock.Mock, client: TestClient, user: User):
|
|||
|
||||
|
||||
def test_authenticated_login(client: TestClient, user: User):
|
||||
post_data = {"user": user.Username, "passwd": "testPassword", "next": "/"}
|
||||
for username in [user.Username.lower(), user.Username.upper()]:
|
||||
post_data = {"user": username, "passwd": "testPassword", "next": "/"}
|
||||
|
||||
with client as request:
|
||||
# Try to login.
|
||||
response = request.post("/login", data=post_data)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
assert response.headers.get("location") == "/"
|
||||
with client as request:
|
||||
# Try to login.
|
||||
request.cookies = {}
|
||||
response = request.post("/login", data=post_data)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
assert response.headers.get("location") == "/"
|
||||
|
||||
# Now, let's verify that we get the logged in rendering
|
||||
# when requesting GET /login as an authenticated user.
|
||||
# Now, let's verify that we receive 403 Forbidden when we
|
||||
# try to get /login as an authenticated user.
|
||||
request.cookies = response.cookies
|
||||
response = request.get("/login")
|
||||
# Now, let's verify that we get the logged in rendering
|
||||
# when requesting GET /login as an authenticated user.
|
||||
request.cookies = response.cookies
|
||||
response = request.get("/login")
|
||||
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
assert "Logged-in as: <strong>test</strong>" in response.text
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
assert f"Logged-in as: <strong>{user.Username}</strong>" in response.text
|
||||
|
||||
|
||||
def test_unauthenticated_logout_unauthorized(client: TestClient):
|
||||
|
@ -370,5 +371,4 @@ def test_generate_unique_sid_exhausted(
|
|||
assert re.search(expr, caplog.text)
|
||||
assert "IntegrityError" in caplog.text
|
||||
|
||||
expr = r"Duplicate entry .+ for key .+SessionID.+"
|
||||
assert re.search(expr, response.text)
|
||||
assert "duplicate key value" in response.text
|
||||
|
|
|
@ -93,9 +93,9 @@ def make_temp_sqlite_config():
|
|||
)
|
||||
|
||||
|
||||
def make_temp_mysql_config():
|
||||
def make_temp_postgres_config():
|
||||
return make_temp_config(
|
||||
(r"backend = .*", "backend = mysql"), (r"name = .*", "name = aurweb_test")
|
||||
(r"backend = .*", "backend = postgres"), (r"name = .*", "name = aurweb_test")
|
||||
)
|
||||
|
||||
|
||||
|
@ -114,8 +114,8 @@ def test_sqlalchemy_sqlite_url():
|
|||
aurweb.config.rehash()
|
||||
|
||||
|
||||
def test_sqlalchemy_mysql_url():
|
||||
tmpctx, tmp = make_temp_mysql_config()
|
||||
def test_sqlalchemy_postgres_url():
|
||||
tmpctx, tmp = make_temp_postgres_config()
|
||||
with tmpctx:
|
||||
with mock.patch.dict(os.environ, {"AUR_CONFIG": tmp}):
|
||||
aurweb.config.rehash()
|
||||
|
@ -123,8 +123,8 @@ def test_sqlalchemy_mysql_url():
|
|||
aurweb.config.rehash()
|
||||
|
||||
|
||||
def test_sqlalchemy_mysql_port_url():
|
||||
tmpctx, tmp = make_temp_config((r";port = 3306", "port = 3306"))
|
||||
def test_sqlalchemy_postgres_port_url():
|
||||
tmpctx, tmp = make_temp_config((r";port = 5432", "port = 5432"))
|
||||
|
||||
with tmpctx:
|
||||
with mock.patch.dict(os.environ, {"AUR_CONFIG": tmp}):
|
||||
|
@ -133,7 +133,7 @@ def test_sqlalchemy_mysql_port_url():
|
|||
aurweb.config.rehash()
|
||||
|
||||
|
||||
def test_sqlalchemy_mysql_socket_url():
|
||||
def test_sqlalchemy_postgres_socket_url():
|
||||
tmpctx, tmp = make_temp_config()
|
||||
|
||||
with tmpctx:
|
||||
|
@ -170,16 +170,6 @@ def test_connection_class_unsupported_backend():
|
|||
aurweb.config.rehash()
|
||||
|
||||
|
||||
@mock.patch("MySQLdb.connect", mock.MagicMock(return_value=True))
|
||||
def test_connection_mysql():
|
||||
tmpctx, tmp = make_temp_mysql_config()
|
||||
with tmpctx:
|
||||
with mock.patch.dict(os.environ, {"AUR_CONFIG": tmp}):
|
||||
aurweb.config.rehash()
|
||||
db.Connection()
|
||||
aurweb.config.rehash()
|
||||
|
||||
|
||||
def test_create_delete():
|
||||
with db.begin():
|
||||
account_type = db.create(AccountType, AccountType="test")
|
||||
|
@ -212,8 +202,8 @@ def test_add_commit():
|
|||
db.delete(account_type)
|
||||
|
||||
|
||||
def test_connection_executor_mysql_paramstyle():
|
||||
executor = db.ConnectionExecutor(None, backend="mysql")
|
||||
def test_connection_executor_postgres_paramstyle():
|
||||
executor = db.ConnectionExecutor(None, backend="postgres")
|
||||
assert executor.paramstyle() == "format"
|
||||
|
||||
|
||||
|
|
|
@ -20,9 +20,10 @@ def test_run():
|
|||
from aurweb.schema import metadata
|
||||
|
||||
aurweb.db.kill_engine()
|
||||
metadata.drop_all(aurweb.db.get_engine())
|
||||
metadata.drop_all(aurweb.db.get_engine(), checkfirst=False)
|
||||
aurweb.initdb.run(Args())
|
||||
|
||||
# Check that constant table rows got added via initdb.
|
||||
record = aurweb.db.query(AccountType, AccountType.AccountType == "User").first()
|
||||
with aurweb.db.begin():
|
||||
record = aurweb.db.query(AccountType, AccountType.AccountType == "User").first()
|
||||
assert record is not None
|
||||
|
|
|
@ -227,7 +227,7 @@ please go to the package page [2] and select "Disable notifications".
|
|||
def test_update(user: User, user2: User, pkgbases: list[PackageBase]):
|
||||
pkgbase = pkgbases[0]
|
||||
with db.begin():
|
||||
user.UpdateNotify = 1
|
||||
user.UpdateNotify = True
|
||||
|
||||
notif = notify.UpdateNotification(user2.ID, pkgbase.ID)
|
||||
notif.send()
|
||||
|
@ -331,7 +331,7 @@ You were removed from the co-maintainer list of {pkgbase.Name} [1].
|
|||
|
||||
def test_suspended_ownership_change(user: User, pkgbases: list[PackageBase]):
|
||||
with db.begin():
|
||||
user.Suspended = 1
|
||||
user.Suspended = True
|
||||
|
||||
pkgbase = pkgbases[0]
|
||||
notif = notify.ComaintainerAddNotification(user.ID, pkgbase.ID)
|
||||
|
@ -491,7 +491,7 @@ def test_open_close_request_hidden_email(
|
|||
|
||||
# Enable the "HideEmail" option for our requester
|
||||
with db.begin():
|
||||
user2.HideEmail = 1
|
||||
user2.HideEmail = True
|
||||
|
||||
# Send an open request notification.
|
||||
notif = notify.RequestOpenNotification(
|
||||
|
|
|
@ -350,7 +350,7 @@ def test_pm_index_table_paging(client, pm_user):
|
|||
VoteInfo,
|
||||
Agenda=f"Agenda #{i}",
|
||||
User=pm_user.Username,
|
||||
Submitted=(ts - 5),
|
||||
Submitted=(ts - 5 - i),
|
||||
End=(ts + 1000),
|
||||
Quorum=0.0,
|
||||
Submitter=pm_user,
|
||||
|
@ -362,7 +362,7 @@ def test_pm_index_table_paging(client, pm_user):
|
|||
VoteInfo,
|
||||
Agenda=f"Agenda #{25 + i}",
|
||||
User=pm_user.Username,
|
||||
Submitted=(ts - 1000),
|
||||
Submitted=(ts - 1000 - i),
|
||||
End=(ts - 5),
|
||||
Quorum=0.0,
|
||||
Submitter=pm_user,
|
||||
|
@ -768,6 +768,7 @@ def test_pm_proposal_vote(client, proposal):
|
|||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
# Check that the proposal record got updated.
|
||||
db.refresh(voteinfo)
|
||||
assert voteinfo.Yes == yes + 1
|
||||
|
||||
# Check that the new PMVote exists.
|
||||
|
|
|
@ -742,14 +742,15 @@ def test_packages_empty(client: TestClient):
|
|||
|
||||
|
||||
def test_packages_search_by_name(client: TestClient, packages: list[Package]):
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "n", "K": "pkg_"})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
for keyword in ["pkg_", "PkG_"]:
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "n", "K": keyword})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
root = parse_root(response.text)
|
||||
root = parse_root(response.text)
|
||||
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 50 # Default per-page
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 50 # Default per-page
|
||||
|
||||
|
||||
def test_packages_search_by_exact_name(client: TestClient, packages: list[Package]):
|
||||
|
@ -763,26 +764,28 @@ def test_packages_search_by_exact_name(client: TestClient, packages: list[Packag
|
|||
# There is no package named exactly 'pkg_', we get 0 results.
|
||||
assert len(rows) == 0
|
||||
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "N", "K": "pkg_1"})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
for keyword in ["pkg_1", "PkG_1"]:
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "N", "K": keyword})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
|
||||
# There's just one package named 'pkg_1', we get 1 result.
|
||||
assert len(rows) == 1
|
||||
# There's just one package named 'pkg_1', we get 1 result.
|
||||
assert len(rows) == 1
|
||||
|
||||
|
||||
def test_packages_search_by_pkgbase(client: TestClient, packages: list[Package]):
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "b", "K": "pkg_"})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
for keyword in ["pkg_", "PkG_"]:
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "b", "K": "pkg_"})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
root = parse_root(response.text)
|
||||
root = parse_root(response.text)
|
||||
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 50
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 50
|
||||
|
||||
|
||||
def test_packages_search_by_exact_pkgbase(client: TestClient, packages: list[Package]):
|
||||
|
@ -794,13 +797,14 @@ def test_packages_search_by_exact_pkgbase(client: TestClient, packages: list[Pac
|
|||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 0
|
||||
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "B", "K": "pkg_1"})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
for keyword in ["pkg_1", "PkG_1"]:
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "B", "K": "pkg_1"})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
|
||||
|
||||
def test_packages_search_by_keywords(client: TestClient, packages: list[Package]):
|
||||
|
@ -821,15 +825,16 @@ def test_packages_search_by_keywords(client: TestClient, packages: list[Package]
|
|||
)
|
||||
|
||||
# And request packages with that keyword, we should get 1 result.
|
||||
with client as request:
|
||||
# clear fakeredis cache
|
||||
cache._redis.flushall()
|
||||
response = request.get("/packages", params={"SeB": "k", "K": "testKeyword"})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
for keyword in ["testkeyword", "TestKeyWord"]:
|
||||
with client as request:
|
||||
# clear fakeredis cache
|
||||
cache._redis.flushall()
|
||||
response = request.get("/packages", params={"SeB": "k", "K": keyword})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
|
||||
# Now let's add another keyword to the same package
|
||||
with db.begin():
|
||||
|
@ -854,14 +859,13 @@ def test_packages_search_by_maintainer(
|
|||
):
|
||||
# We should expect that searching by `package`'s maintainer
|
||||
# returns `package` in the results.
|
||||
with client as request:
|
||||
response = request.get(
|
||||
"/packages", params={"SeB": "m", "K": maintainer.Username}
|
||||
)
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
for keyword in [maintainer.Username, maintainer.Username.upper()]:
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "m", "K": keyword})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
|
||||
# Search again by maintainer with no keywords given.
|
||||
# This kind of search returns all orphans instead.
|
||||
|
@ -912,17 +916,16 @@ def test_packages_search_by_comaintainer(
|
|||
)
|
||||
|
||||
# Then test that it's returned by our search.
|
||||
with client as request:
|
||||
# clear fakeredis cache
|
||||
cache._redis.flushall()
|
||||
response = request.get(
|
||||
"/packages", params={"SeB": "c", "K": maintainer.Username}
|
||||
)
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
for keyword in [maintainer.Username, maintainer.Username.upper()]:
|
||||
with client as request:
|
||||
# clear fakeredis cache
|
||||
cache._redis.flushall()
|
||||
response = request.get("/packages", params={"SeB": "c", "K": keyword})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
|
||||
|
||||
def test_packages_search_by_co_or_maintainer(
|
||||
|
@ -954,27 +957,27 @@ def test_packages_search_by_co_or_maintainer(
|
|||
PackageComaintainer, PackageBase=package.PackageBase, User=user, Priority=1
|
||||
)
|
||||
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "M", "K": user.Username})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
for keyword in [user.Username, user.Username.upper()]:
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "M", "K": keyword})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
|
||||
|
||||
def test_packages_search_by_submitter(
|
||||
client: TestClient, maintainer: User, package: Package
|
||||
):
|
||||
with client as request:
|
||||
response = request.get(
|
||||
"/packages", params={"SeB": "s", "K": maintainer.Username}
|
||||
)
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
for keyword in [maintainer.Username, maintainer.Username.upper()]:
|
||||
with client as request:
|
||||
response = request.get("/packages", params={"SeB": "s", "K": keyword})
|
||||
assert response.status_code == int(HTTPStatus.OK)
|
||||
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
root = parse_root(response.text)
|
||||
rows = root.xpath('//table[@class="results"]/tbody/tr')
|
||||
assert len(rows) == 1
|
||||
|
||||
|
||||
def test_packages_sort_by_name(client: TestClient, packages: list[Package]):
|
||||
|
@ -1528,6 +1531,7 @@ def test_packages_post_disown_as_maintainer(
|
|||
errors = get_errors(resp.text)
|
||||
expected = "You did not select any packages to disown."
|
||||
assert errors[0].text.strip() == expected
|
||||
db.refresh(package)
|
||||
assert package.PackageBase.Maintainer is not None
|
||||
|
||||
# Try to disown `package` without giving the confirm argument.
|
||||
|
@ -1552,6 +1556,7 @@ def test_packages_post_disown_as_maintainer(
|
|||
data={"action": "disown", "IDs": [package.ID], "confirm": True},
|
||||
)
|
||||
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
|
||||
db.refresh(package)
|
||||
assert package.PackageBase.Maintainer is not None
|
||||
errors = get_errors(resp.text)
|
||||
expected = "You are not allowed to disown one of the packages you selected."
|
||||
|
@ -1565,6 +1570,7 @@ def test_packages_post_disown_as_maintainer(
|
|||
data={"action": "disown", "IDs": [package.ID], "confirm": True},
|
||||
)
|
||||
|
||||
db.get_session().expire_all()
|
||||
assert package.PackageBase.Maintainer is None
|
||||
successes = get_successes(resp.text)
|
||||
expected = "The selected packages have been disowned."
|
||||
|
@ -1649,6 +1655,7 @@ def test_packages_post_delete(
|
|||
|
||||
# Whoo. Now, let's finally make a valid request as `pm_user`
|
||||
# to delete `package`.
|
||||
pkgname = package.PackageBase.Name
|
||||
with client as request:
|
||||
request.cookies = pm_cookies
|
||||
resp = request.post(
|
||||
|
@ -1661,7 +1668,7 @@ def test_packages_post_delete(
|
|||
assert successes[0].text.strip() == expected
|
||||
|
||||
# Expect that the package deletion was logged.
|
||||
pkgbases = [package.PackageBase.Name]
|
||||
pkgbases = [pkgname]
|
||||
expected = (
|
||||
f"Privileged user '{pm_user.Username}' deleted the "
|
||||
f"following package bases: {str(pkgbases)}."
|
||||
|
|
|
@ -153,7 +153,7 @@ def test_pkg_required(package: Package):
|
|||
# We want to make sure "Package" data is included
|
||||
# to avoid lazy-loading the information for each dependency
|
||||
qry = util.pkg_required("test", list())
|
||||
assert "Packages_ID" in str(qry)
|
||||
assert "packages_id" in str(qry).lower()
|
||||
|
||||
# We should have 1 record
|
||||
assert qry.count() == 1
|
||||
|
|
|
@ -428,7 +428,7 @@ def test_pkgbase_comments(
|
|||
|
||||
# create notification
|
||||
with db.begin():
|
||||
user.CommentNotify = 1
|
||||
user.CommentNotify = True
|
||||
db.create(PackageNotification, PackageBase=package.PackageBase, User=user)
|
||||
|
||||
# post a comment
|
||||
|
@ -688,6 +688,7 @@ def test_pkgbase_comment_pin_as_co(
|
|||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
# Assert that PinnedTS got set.
|
||||
db.refresh(comment)
|
||||
assert comment.PinnedTS > 0
|
||||
|
||||
# Unpin the comment we just pinned.
|
||||
|
@ -698,6 +699,7 @@ def test_pkgbase_comment_pin_as_co(
|
|||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
# Let's assert that PinnedTS was unset.
|
||||
db.refresh(comment)
|
||||
assert comment.PinnedTS == 0
|
||||
|
||||
|
||||
|
@ -716,6 +718,7 @@ def test_pkgbase_comment_pin(
|
|||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
# Assert that PinnedTS got set.
|
||||
db.refresh(comment)
|
||||
assert comment.PinnedTS > 0
|
||||
|
||||
# Unpin the comment we just pinned.
|
||||
|
@ -726,6 +729,7 @@ def test_pkgbase_comment_pin(
|
|||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
# Let's assert that PinnedTS was unset.
|
||||
db.refresh(comment)
|
||||
assert comment.PinnedTS == 0
|
||||
|
||||
|
||||
|
@ -1040,6 +1044,7 @@ def test_pkgbase_flag(
|
|||
request.cookies = cookies
|
||||
resp = request.post(endpoint, data={"comments": "Test"})
|
||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
db.refresh(pkgbase)
|
||||
assert pkgbase.Flagger == user
|
||||
assert pkgbase.FlaggerComment == "Test"
|
||||
|
||||
|
@ -1077,6 +1082,7 @@ def test_pkgbase_flag(
|
|||
request.cookies = user2_cookies
|
||||
resp = request.post(endpoint)
|
||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
db.refresh(pkgbase)
|
||||
assert pkgbase.Flagger == user
|
||||
|
||||
# Now, test that the 'maintainer' user can.
|
||||
|
@ -1085,6 +1091,7 @@ def test_pkgbase_flag(
|
|||
request.cookies = maint_cookies
|
||||
resp = request.post(endpoint)
|
||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
db.refresh(pkgbase)
|
||||
assert pkgbase.Flagger is None
|
||||
|
||||
# Flag it again.
|
||||
|
@ -1098,6 +1105,7 @@ def test_pkgbase_flag(
|
|||
request.cookies = cookies
|
||||
resp = request.post(endpoint)
|
||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
db.refresh(pkgbase)
|
||||
assert pkgbase.Flagger is None
|
||||
|
||||
|
||||
|
@ -1170,6 +1178,7 @@ def test_pkgbase_vote(client: TestClient, user: User, package: Package):
|
|||
|
||||
vote = pkgbase.package_votes.filter(PackageVote.UsersID == user.ID).first()
|
||||
assert vote is not None
|
||||
db.refresh(pkgbase)
|
||||
assert pkgbase.NumVotes == 1
|
||||
|
||||
# Remove vote.
|
||||
|
@ -1181,6 +1190,7 @@ def test_pkgbase_vote(client: TestClient, user: User, package: Package):
|
|||
|
||||
vote = pkgbase.package_votes.filter(PackageVote.UsersID == user.ID).first()
|
||||
assert vote is None
|
||||
db.refresh(pkgbase)
|
||||
assert pkgbase.NumVotes == 0
|
||||
|
||||
|
||||
|
@ -1592,9 +1602,9 @@ def test_pkgbase_merge_post(
|
|||
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()
|
||||
comments = [row.__dict__ for row in package.PackageBase.comments.all()]
|
||||
notifs = [row.__dict__ for row in package.PackageBase.notifications.all()]
|
||||
votes = [row.__dict__ for row in package.PackageBase.package_votes.all()]
|
||||
|
||||
# Merge the package into target.
|
||||
endpoint = f"/pkgbase/{package.PackageBase.Name}/merge"
|
||||
|
@ -1612,9 +1622,13 @@ def test_pkgbase_merge_post(
|
|||
|
||||
# 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()
|
||||
db.get_session().refresh(target)
|
||||
assert len(comments) == target.comments.count()
|
||||
assert comments[0]["PackageBaseID"] != target.ID
|
||||
assert len(notifs) == target.notifications.count()
|
||||
assert notifs[0]["PackageBaseID"] != target.ID
|
||||
assert len(votes) == target.package_votes.count()
|
||||
assert votes[0]["PackageBaseID"] != target.ID
|
||||
|
||||
# ...and that the package got deleted.
|
||||
package = db.query(Package).filter(Package.Name == pkgname).first()
|
||||
|
|
|
@ -649,6 +649,7 @@ def test_orphan_request(
|
|||
assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}"
|
||||
|
||||
# We should have unset the maintainer.
|
||||
db.refresh(pkgbase)
|
||||
assert pkgbase.Maintainer is None
|
||||
|
||||
# We should have removed the comaintainers.
|
||||
|
@ -748,6 +749,7 @@ def test_orphan_as_maintainer(client: TestClient, auser: User, pkgbase: PackageB
|
|||
# As the pkgbase maintainer, disowning the package just ends up
|
||||
# either promoting the lowest priority comaintainer or removing
|
||||
# the associated maintainer relationship altogether.
|
||||
db.refresh(pkgbase)
|
||||
assert pkgbase.Maintainer is None
|
||||
|
||||
|
||||
|
@ -1044,6 +1046,7 @@ def test_requests_close_post(client: TestClient, user: User, pkgreq: PackageRequ
|
|||
resp = request.post(f"/requests/{pkgreq.ID}/close")
|
||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
db.refresh(pkgreq)
|
||||
assert pkgreq.Status == REJECTED_ID
|
||||
assert pkgreq.Closer == user
|
||||
assert pkgreq.ClosureComment == str()
|
||||
|
@ -1060,6 +1063,7 @@ def test_requests_close_post_rejected(
|
|||
)
|
||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
|
||||
db.refresh(pkgreq)
|
||||
assert pkgreq.Status == REJECTED_ID
|
||||
assert pkgreq.Closer == user
|
||||
assert pkgreq.ClosureComment == str()
|
||||
|
|
|
@ -102,6 +102,7 @@ def test_user_language(client: TestClient, user: User):
|
|||
req.cookies = {"AURSID": sid}
|
||||
response = req.post("/language", data=post_data)
|
||||
assert response.status_code == int(HTTPStatus.SEE_OTHER)
|
||||
db.refresh(user)
|
||||
assert user.LangPreference == "de"
|
||||
|
||||
|
||||
|
|
|
@ -149,15 +149,15 @@ def assert_multiple_keys(pks):
|
|||
def test_hash_query():
|
||||
# No conditions
|
||||
query = db.query(User)
|
||||
assert util.hash_query(query) == "75e76026b7d576536e745ec22892cf8f5d7b5d62"
|
||||
assert util.hash_query(query) == "ebbf077df70d97a1584f91d0dd6ec61e43aa101f"
|
||||
|
||||
# With where clause
|
||||
query = db.query(User).filter(User.Username == "bla")
|
||||
assert util.hash_query(query) == "4dca710f33b1344c27ec6a3c266970f4fa6a8a00"
|
||||
assert util.hash_query(query) == "b51f2bfda67051f381a5c05b2946a1aa4d91e56d"
|
||||
|
||||
# With where clause and sorting
|
||||
query = db.query(User).filter(User.Username == "bla").order_by(User.Username)
|
||||
assert util.hash_query(query) == "ee2c7846fede430776e140f8dfe1d83cd21d2eed"
|
||||
assert util.hash_query(query) == "8d458bfe1edfe8f78929fab590612e9e5d9db3a5"
|
||||
|
||||
# With where clause, sorting and specific columns
|
||||
query = (
|
||||
|
@ -166,4 +166,4 @@ def test_hash_query():
|
|||
.order_by(User.Username)
|
||||
.with_entities(User.Username)
|
||||
)
|
||||
assert util.hash_query(query) == "c1db751be61443d266cf643005eee7a884dac103"
|
||||
assert util.hash_query(query) == "006811a386789f25d40a37496f6ac6651413c245"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import pytest
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.exc import IntegrityError, SAWarning
|
||||
|
||||
from aurweb import db, time
|
||||
from aurweb.db import create, rollback
|
||||
|
@ -109,7 +109,7 @@ def test_voteinfo_null_submitter_raises(user: User):
|
|||
|
||||
|
||||
def test_voteinfo_null_agenda_raises(user: User):
|
||||
with pytest.raises(IntegrityError):
|
||||
with pytest.raises(IntegrityError), pytest.warns(SAWarning):
|
||||
with db.begin():
|
||||
create(
|
||||
VoteInfo,
|
||||
|
@ -123,7 +123,7 @@ def test_voteinfo_null_agenda_raises(user: User):
|
|||
|
||||
|
||||
def test_voteinfo_null_user_raises(user: User):
|
||||
with pytest.raises(IntegrityError):
|
||||
with pytest.raises(IntegrityError), pytest.warns(SAWarning):
|
||||
with db.begin():
|
||||
create(
|
||||
VoteInfo,
|
||||
|
@ -137,7 +137,7 @@ def test_voteinfo_null_user_raises(user: User):
|
|||
|
||||
|
||||
def test_voteinfo_null_submitted_raises(user: User):
|
||||
with pytest.raises(IntegrityError):
|
||||
with pytest.raises(IntegrityError), pytest.warns(SAWarning):
|
||||
with db.begin():
|
||||
create(
|
||||
VoteInfo,
|
||||
|
@ -151,7 +151,7 @@ def test_voteinfo_null_submitted_raises(user: User):
|
|||
|
||||
|
||||
def test_voteinfo_null_end_raises(user: User):
|
||||
with pytest.raises(IntegrityError):
|
||||
with pytest.raises(IntegrityError), pytest.warns(SAWarning):
|
||||
with db.begin():
|
||||
create(
|
||||
VoteInfo,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue