remove databases, use SQLAlechemy 2.0 instead

Among the many changes we switch to using SQLAlchemy's connection pool,
which means we are no longer required to guard against multiple threads
working on the database.
All db funcs now receive a connection to use as their first argument,
this allows the caller to control transaction & rollback behavior.
This commit is contained in:
ducklet 2023-11-27 23:24:35 +01:00
parent c63bee072f
commit 4981de4a04
12 changed files with 876 additions and 765 deletions

View file

@ -34,7 +34,7 @@ def admin_client() -> TestClient:
@pytest.mark.asyncio
async def test_get_ratings_for_group(
shared_conn: db.Database, unauthorized_client: TestClient
conn: db.Connection, unauthorized_client: TestClient
):
user = models.User(
imdb_id="ur12345678",
@ -48,201 +48,196 @@ async def test_get_ratings_for_group(
)
user.groups = [models.UserGroup(id=str(group.id), access="r")]
path = app.url_path_for("get_ratings_for_group", group_id=str(group.id))
async with shared_conn.transaction(force_rollback=True):
resp = unauthorized_client.get(path)
assert resp.status_code == 404, "Group does not exist (yet)"
await db.add(user)
await db.add(group)
resp = unauthorized_client.get(path)
assert resp.status_code == 404, "Group does not exist (yet)"
resp = unauthorized_client.get(path)
assert resp.status_code == 200
assert resp.json() == []
await db.add(conn, user)
await db.add(conn, group)
movie = models.Movie(
title="test movie",
release_year=2013,
media_type="Movie",
imdb_id="tt12345678",
genres={"genre-1"},
)
await db.add(movie)
resp = unauthorized_client.get(path)
assert resp.status_code == 200
assert resp.json() == []
rating = models.Rating(
movie_id=movie.id, user_id=user.id, score=66, rating_date=datetime.now()
)
await db.add(rating)
movie = models.Movie(
title="test movie",
release_year=2013,
media_type="Movie",
imdb_id="tt12345678",
genres={"genre-1"},
)
await db.add(conn, movie)
rating_aggregate = {
"canonical_title": movie.title,
"imdb_score": movie.imdb_score,
"imdb_votes": movie.imdb_votes,
"link": imdb.movie_url(movie.imdb_id),
"media_type": movie.media_type,
"original_title": movie.original_title,
"user_scores": [rating.score],
"year": movie.release_year,
}
rating = models.Rating(
movie_id=movie.id, user_id=user.id, score=66, rating_date=datetime.now()
)
await db.add(conn, rating)
resp = unauthorized_client.get(path)
rating_aggregate = {
"canonical_title": movie.title,
"imdb_score": movie.imdb_score,
"imdb_votes": movie.imdb_votes,
"link": imdb.movie_url(movie.imdb_id),
"media_type": movie.media_type,
"original_title": movie.original_title,
"user_scores": [rating.score],
"year": movie.release_year,
}
resp = unauthorized_client.get(path)
assert resp.status_code == 200
assert resp.json() == [rating_aggregate]
filters = {
"imdb_id": movie.imdb_id,
"unwind_id": str(movie.id),
"title": movie.title,
"media_type": movie.media_type,
"year": movie.release_year,
}
for k, v in filters.items():
resp = unauthorized_client.get(path, params={k: v})
assert resp.status_code == 200
assert resp.json() == [rating_aggregate]
filters = {
"imdb_id": movie.imdb_id,
"unwind_id": str(movie.id),
"title": movie.title,
"media_type": movie.media_type,
"year": movie.release_year,
}
for k, v in filters.items():
resp = unauthorized_client.get(path, params={k: v})
assert resp.status_code == 200
assert resp.json() == [rating_aggregate]
resp = unauthorized_client.get(path, params={"title": "no such thing"})
assert resp.status_code == 200
assert resp.json() == []
resp = unauthorized_client.get(path, params={"title": "no such thing"})
assert resp.status_code == 200
assert resp.json() == []
# Test "exact" query param.
resp = unauthorized_client.get(
path, params={"title": "test movie", "exact": "true"}
)
assert resp.status_code == 200
assert resp.json() == [rating_aggregate]
resp = unauthorized_client.get(path, params={"title": "te mo", "exact": "false"})
assert resp.status_code == 200
assert resp.json() == [rating_aggregate]
resp = unauthorized_client.get(path, params={"title": "te mo", "exact": "true"})
assert resp.status_code == 200
assert resp.json() == []
# Test "exact" query param.
resp = unauthorized_client.get(
path, params={"title": "test movie", "exact": "true"}
)
assert resp.status_code == 200
assert resp.json() == [rating_aggregate]
resp = unauthorized_client.get(
path, params={"title": "te mo", "exact": "false"}
)
assert resp.status_code == 200
assert resp.json() == [rating_aggregate]
resp = unauthorized_client.get(path, params={"title": "te mo", "exact": "true"})
assert resp.status_code == 200
assert resp.json() == []
# XXX Test "ignore_tv_episodes" query param.
# XXX Test "include_unrated" query param.
# XXX Test "per_page" query param.
# XXX Test "ignore_tv_episodes" query param.
# XXX Test "include_unrated" query param.
# XXX Test "per_page" query param.
@pytest.mark.asyncio
async def test_list_movies(
shared_conn: db.Database,
conn: db.Connection,
unauthorized_client: TestClient,
authorized_client: TestClient,
):
path = app.url_path_for("list_movies")
async with shared_conn.transaction(force_rollback=True):
response = unauthorized_client.get(path)
assert response.status_code == 403
response = unauthorized_client.get(path)
assert response.status_code == 403
response = authorized_client.get(path)
assert response.status_code == 200
assert response.json() == []
response = authorized_client.get(path)
assert response.status_code == 200
assert response.json() == []
m = models.Movie(
title="test movie",
release_year=2013,
media_type="Movie",
imdb_id="tt12345678",
genres={"genre-1"},
)
await db.add(m)
m = models.Movie(
title="test movie",
release_year=2013,
media_type="Movie",
imdb_id="tt12345678",
genres={"genre-1"},
)
await db.add(conn, m)
response = authorized_client.get(path, params={"include_unrated": 1})
assert response.status_code == 200
assert response.json() == [{**models.asplain(m), "user_scores": []}]
response = authorized_client.get(path, params={"include_unrated": 1})
assert response.status_code == 200
assert response.json() == [{**models.asplain(m), "user_scores": []}]
m_plain = {
"canonical_title": m.title,
"imdb_score": m.imdb_score,
"imdb_votes": m.imdb_votes,
"link": imdb.movie_url(m.imdb_id),
"media_type": m.media_type,
"original_title": m.original_title,
"user_scores": [],
"year": m.release_year,
}
m_plain = {
"canonical_title": m.title,
"imdb_score": m.imdb_score,
"imdb_votes": m.imdb_votes,
"link": imdb.movie_url(m.imdb_id),
"media_type": m.media_type,
"original_title": m.original_title,
"user_scores": [],
"year": m.release_year,
}
response = authorized_client.get(path, params={"imdb_id": m.imdb_id})
assert response.status_code == 200
assert response.json() == [m_plain]
response = authorized_client.get(path, params={"imdb_id": m.imdb_id})
assert response.status_code == 200
assert response.json() == [m_plain]
response = authorized_client.get(path, params={"unwind_id": str(m.id)})
assert response.status_code == 200
assert response.json() == [m_plain]
response = authorized_client.get(path, params={"unwind_id": str(m.id)})
assert response.status_code == 200
assert response.json() == [m_plain]
@pytest.mark.asyncio
async def test_list_users(
shared_conn: db.Database,
conn: db.Connection,
unauthorized_client: TestClient,
authorized_client: TestClient,
admin_client: TestClient,
):
path = app.url_path_for("list_users")
async with shared_conn.transaction(force_rollback=True):
response = unauthorized_client.get(path)
assert response.status_code == 403
response = unauthorized_client.get(path)
assert response.status_code == 403
response = authorized_client.get(path)
assert response.status_code == 403
response = authorized_client.get(path)
assert response.status_code == 403
response = admin_client.get(path)
assert response.status_code == 200
assert response.json() == []
response = admin_client.get(path)
assert response.status_code == 200
assert response.json() == []
m = models.User(
imdb_id="ur12345678",
name="user-1",
secret="secret-1",
groups=[],
)
await db.add(m)
m = models.User(
imdb_id="ur12345678",
name="user-1",
secret="secret-1",
groups=[],
)
await db.add(conn, m)
m_plain = {
"groups": m.groups,
"id": m.id,
"imdb_id": m.imdb_id,
"name": m.name,
"secret": m.secret,
}
m_plain = {
"groups": m.groups,
"id": m.id,
"imdb_id": m.imdb_id,
"name": m.name,
"secret": m.secret,
}
response = admin_client.get(path)
assert response.status_code == 200
assert response.json() == [m_plain]
response = admin_client.get(path)
assert response.status_code == 200
assert response.json() == [m_plain]
@pytest.mark.asyncio
async def test_list_groups(
shared_conn: db.Database,
conn: db.Connection,
unauthorized_client: TestClient,
authorized_client: TestClient,
admin_client: TestClient,
):
path = app.url_path_for("list_groups")
async with shared_conn.transaction(force_rollback=True):
response = unauthorized_client.get(path)
assert response.status_code == 403
response = unauthorized_client.get(path)
assert response.status_code == 403
response = authorized_client.get(path)
assert response.status_code == 403
response = authorized_client.get(path)
assert response.status_code == 403
response = admin_client.get(path)
assert response.status_code == 200
assert response.json() == []
response = admin_client.get(path)
assert response.status_code == 200
assert response.json() == []
m = models.Group(
name="group-1",
users=[models.GroupUser(id="123", name="itsa-me")],
)
await db.add(m)
m = models.Group(
name="group-1",
users=[models.GroupUser(id="123", name="itsa-me")],
)
await db.add(conn, m)
m_plain = {
"users": m.users,
"id": m.id,
"name": m.name,
}
m_plain = {
"users": m.users,
"id": m.id,
"name": m.name,
}
response = admin_client.get(path)
assert response.status_code == 200
assert response.json() == [m_plain]
response = admin_client.get(path)
assert response.status_code == 200
assert response.json() == [m_plain]