Skip to content

Commit

Permalink
Add all missing tests 🚀
Browse files Browse the repository at this point in the history
  • Loading branch information
bkis committed Jan 2, 2024
1 parent 0edfcd5 commit ab48e73
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 75 deletions.
5 changes: 5 additions & 0 deletions Tekst-API/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -6136,6 +6136,11 @@
},
"type": "array",
"title": "Infosegments"
},
"settingsCacheTtl": {
"type": "integer",
"title": "Settingscachettl",
"default": 60
}
},
"type": "object",
Expand Down
10 changes: 4 additions & 6 deletions Tekst-API/tekst/routers/texts.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ async def download_structure_template(
# validate template
try:
TextStructureDefinition.model_validate(structure_def)
except Exception:
except Exception: # pragma: no cover
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error creating template",
Expand Down Expand Up @@ -192,7 +192,7 @@ async def upload_structure_definition(
structure_def = TextStructureDefinition.model_validate_json(await file.read())
except Exception as e:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid structure definition ({str(e)})",
)
# import nodes depth-first
Expand All @@ -204,7 +204,7 @@ async def upload_structure_definition(
# process nodes level by level
for level in range(len(text.levels)):
if len(nodes) == 0:
break
break # pragma: no cover
# create NodeDocument instances for each node definition
node_docs = [
NodeDocument(
Expand Down Expand Up @@ -246,7 +246,6 @@ async def insert_level(
) -> TextRead:
text_doc: TextDocument = await TextDocument.get(text_id)

# text exists?
if not text_doc:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
Expand Down Expand Up @@ -348,7 +347,6 @@ async def delete_level(
) -> TextRead:
text_doc: TextDocument = await TextDocument.get(text_id)

# text exists?
if not text_doc:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
Expand Down Expand Up @@ -473,7 +471,7 @@ async def delete_text(
# delete text itself
await text.delete()
# check if deleted text was default text, correct if necessary
pf_settings_doc = await get_settings()
pf_settings_doc = await get_settings(nocache=True)
if pf_settings_doc.default_text_id == text_id:
pf_settings_doc.default_text_id = (await TextDocument.find_one()).id
await pf_settings_doc.replace()
Expand Down
35 changes: 16 additions & 19 deletions Tekst-API/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import contextlib

from collections.abc import Callable
from pathlib import Path
from typing import Any
Expand Down Expand Up @@ -150,10 +148,10 @@ async def _insert_sample_data(*collections: str) -> dict[str, list[str]]:

@pytest.fixture
def get_fake_user() -> Callable:
def _get_fake_user(alternative: bool = False):
def _get_fake_user(suffix: str = ""):
return dict(
email="foo@bar.de" if not alternative else "bar@foo.de",
username="test_user" if not alternative else "test_user2",
email=f"foo{suffix}@bar.de",
username=f"test_user{suffix}",
password="poiPOI098",
name="Foo Bar",
affiliation="Some Institution",
Expand All @@ -169,9 +167,9 @@ async def _register_test_user(
is_active: bool = True,
is_verified: bool = True,
is_superuser: bool = False,
alternative: bool = False,
suffix: str = "",
) -> dict:
user_data = get_fake_user(alternative=alternative)
user_data = get_fake_user(suffix=suffix)
user = UserCreate(**user_data)
user.is_active = is_active
user.is_verified = is_verified
Expand All @@ -183,28 +181,30 @@ async def _register_test_user(


@pytest.fixture
async def logout(test_client) -> Callable:
async def logout(config, test_client: AsyncClient) -> Callable:
async def _logout() -> None:
await test_client.post("/auth/cookie/logout")
test_client.cookies.delete(name=config.security_auth_cookie_name)

return _logout


@pytest.fixture
async def login(
config, test_client, status_fail_msg, logout, register_test_user
) -> Callable:
async def login(config, test_client, logout, register_test_user) -> Callable:
async def _login(*, user: dict | None = None, **kwargs) -> dict:
await logout()
if not user:
user = await register_test_user(**kwargs)
payload = {"username": user["email"], "password": user["password"]}
resp = await test_client.post(
"/auth/cookie/login",
data=payload,
data={"username": user["email"], "password": user["password"]},
)
assert resp.status_code == 204, status_fail_msg(204, resp)
assert resp.cookies.get(config.security_auth_cookie_name)
if resp.status_code != 204:
raise Exception(
f"Failed to login (got status {resp.status_code}: {resp.text})"
)
if not resp.cookies.get(config.security_auth_cookie_name):
raise Exception("No cookies retrieved after login")
return user

return _login
Expand All @@ -213,12 +213,9 @@ async def _login(*, user: dict | None = None, **kwargs) -> dict:
@pytest.fixture(scope="session")
def status_fail_msg() -> Callable:
def _status_fail_msg(expected_status: int, response: Response) -> tuple[bool, str]:
resp_json = "No JSON response data."
with contextlib.suppress(Exception):
resp_json = response.json()
return (
f"HTTP {response.status_code} (expected: {expected_status})"
f" -- {resp_json}"
f" -- {response.text}"
)

return _status_fail_msg
Expand Down
2 changes: 1 addition & 1 deletion Tekst-API/tests/test_api_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ async def test_update_user(
status_fail_msg,
):
user = await register_test_user(is_active=False)
await login(is_superuser=True, alternative=True)
await login(is_superuser=True, suffix="normalo")
# update user
resp = await test_client.patch(
f"/users/{user['id']}",
Expand Down
1 change: 1 addition & 0 deletions Tekst-API/tests/test_api_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ async def test_update_platform_settings(
assert resp.status_code == 200, status_fail_msg(200, resp)
assert isinstance(resp.json(), dict)
assert "availableLocales" in resp.json()
assert len(resp.json()["availableLocales"]) == 1
assert resp.json()["availableLocales"][0] == "enUS"


Expand Down
107 changes: 84 additions & 23 deletions Tekst-API/tests/test_api_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ async def test_update_resource(
):
text_id = (await insert_sample_data("texts", "nodes", "resources"))["texts"][0]
user = await login()
other_user = await register_test_user(alternative=True)
other_user = await register_test_user(suffix="other")

# create new resource (because only owner can update(write))
payload = {
Expand Down Expand Up @@ -224,7 +224,7 @@ async def test_set_shares_for_public_resource(
"resources"
][0]
await login(is_superuser=True)
other_user = await register_test_user(alternative=True)
other_user = await register_test_user(suffix="other")

# set resource public
res = await ResourceBaseDocument.get(resource_id, with_children=True)
Expand Down Expand Up @@ -351,21 +351,18 @@ async def test_get_resources(

@pytest.mark.anyio
async def test_propose_unpropose_publish_unpublish_resource(
test_client: AsyncClient,
insert_sample_data,
status_fail_msg,
login,
test_client: AsyncClient, insert_sample_data, status_fail_msg, login, wrong_id
):
text_id = (await insert_sample_data("texts", "nodes", "resources"))["texts"][0]
user = await login()
owner = await login(suffix="super", is_superuser=True)

# create new resource (because only owner can update(write))
payload = {
"title": "Foo Bar Baz",
"textId": text_id,
"level": 0,
"resourceType": "plaintext",
"ownerId": user.get("id"),
"ownerId": owner.get("id"),
}
resp = await test_client.post(
"/resources",
Expand All @@ -375,67 +372,115 @@ async def test_propose_unpropose_publish_unpublish_resource(
resource_data = resp.json()
assert "id" in resource_data
assert "ownerId" in resource_data

# become superuser
user = await login(is_superuser=True, alternative=True)
await login(user=user)
resource_id = resource_data["id"]

# publish unproposed resource
resp = await test_client.post(
f"/resources/{resource_data['id']}/publish",
f"/resources/{resource_id}/publish",
)
assert resp.status_code == 400, status_fail_msg(400, resp)

# propose resource
resp = await test_client.post(
f"/resources/{resource_data['id']}/propose",
f"/resources/{resource_id}/propose",
)
assert resp.status_code == 200, status_fail_msg(200, resp)

# propose resource w/ wrong ID
resp = await test_client.post(
f"/resources/{wrong_id}/propose",
)
assert resp.status_code == 404, status_fail_msg(404, resp)

# get all accessible resources, check if ours is proposed
resp = await test_client.get("/resources", params={"textId": text_id})
assert resp.status_code == 200, status_fail_msg(200, resp)
assert isinstance(resp.json(), list)
for resource in resp.json():
if resource["id"] == resource_data["id"]:
if resource["id"] == resource_id:
assert resource["proposed"]

# propose resource again (should just go through)
resp = await test_client.post(
f"/resources/{resource_data['id']}/propose",
f"/resources/{resource_id}/propose",
)
assert resp.status_code == 200, status_fail_msg(200, resp)

# publish resource w/ wrong ID
resp = await test_client.post(
f"/resources/{wrong_id}/publish",
)
assert resp.status_code == 404, status_fail_msg(404, resp)

# publish resource
resp = await test_client.post(
f"/resources/{resource_data['id']}/publish",
f"/resources/{resource_id}/publish",
)
assert resp.status_code == 200, status_fail_msg(200, resp)

# propose public resource
resp = await test_client.post(
f"/resources/{resource_id}/propose",
)
assert resp.status_code == 400, status_fail_msg(400, resp)

# unpublish resource w/ wrong ID
resp = await test_client.post(
f"/resources/{wrong_id}/unpublish",
)
assert resp.status_code == 404, status_fail_msg(404, resp)

# unpublish resource
resp = await test_client.post(
f"/resources/{resource_data['id']}/unpublish",
f"/resources/{resource_id}/unpublish",
)
assert resp.status_code == 200, status_fail_msg(200, resp)

# unpublish resource again (should just go through)
resp = await test_client.post(
f"/resources/{resource_data['id']}/unpublish",
f"/resources/{resource_id}/unpublish",
)
assert resp.status_code == 200, status_fail_msg(200, resp)

# propose resource again
resp = await test_client.post(
f"/resources/{resource_data['id']}/propose",
f"/resources/{resource_id}/propose",
)
assert resp.status_code == 200, status_fail_msg(200, resp)

# unpropose resource w/ wrong ID
resp = await test_client.post(
f"/resources/{wrong_id}/unpropose",
)
assert resp.status_code == 404, status_fail_msg(404, resp)

# unpropose resource
resp = await test_client.post(
f"/resources/{resource_data['id']}/unpropose",
f"/resources/{resource_id}/unpropose",
)
assert resp.status_code == 200, status_fail_msg(200, resp)

# propose resource unauthorized
other_user = await login(suffix="other")
resp = await test_client.post(
f"/resources/{resource_id}/propose",
)
assert resp.status_code == 403, status_fail_msg(403, resp)

# propose resource again
await login(user=owner)
resp = await test_client.post(
f"/resources/{resource_id}/propose",
)
assert resp.status_code == 200, status_fail_msg(200, resp)

# unpropose resource unauthorized
await login(user=other_user)
resp = await test_client.post(
f"/resources/{resource_id}/unpropose",
)
assert resp.status_code == 403, status_fail_msg(403, resp)


@pytest.mark.anyio
async def test_delete_resource(
Expand Down Expand Up @@ -470,7 +515,7 @@ async def test_delete_resource(
assert resp.status_code == 404, status_fail_msg(404, resp)

# become non-owner/non-superuser
await login(alternative=True)
await login(suffix="normalo")

# try to delete resource as non-owner/non-superuser
resp = await test_client.delete(
Expand Down Expand Up @@ -564,7 +609,7 @@ async def test_transfer_resource(
# register regular test user
user = await register_test_user(is_superuser=False)
# register test superuser
superuser = await login(alternative=True, is_superuser=True)
superuser = await login(suffix="super", is_superuser=True)

# transfer resource that is still public to test user
resp = await test_client.post(
Expand All @@ -586,6 +631,13 @@ async def test_transfer_resource(
)
assert resp.status_code == 404, status_fail_msg(404, resp)

# transfer resource to user w/ wrong ID
resp = await test_client.post(
f"/resources/{resource_id}/transfer",
json=wrong_id,
)
assert resp.status_code == 400, status_fail_msg(400, resp)

# transfer resource without permission
await login(user=user)
resp = await test_client.post(
Expand All @@ -604,6 +656,15 @@ async def test_transfer_resource(
assert isinstance(resp.json(), dict)
assert resp.json()["ownerId"] == user["id"]

# ....and do that again
resp = await test_client.post(
f"/resources/{resource_id}/transfer",
json=user["id"],
)
assert resp.status_code == 200, status_fail_msg(200, resp)
assert isinstance(resp.json(), dict)
assert resp.json()["ownerId"] == user["id"]


@pytest.mark.anyio
async def test_get_resource_template(
Expand Down
Loading

0 comments on commit ab48e73

Please sign in to comment.