diff --git a/src/aleph/model/messages.py b/src/aleph/model/messages.py index 01f214fb3..71dbe6256 100644 --- a/src/aleph/model/messages.py +++ b/src/aleph/model/messages.py @@ -228,7 +228,7 @@ async def get_merged_posts(filters, sort=None, limit=100, skip=0, amend_limit=1) } } }, - {"$project": {"amends": 0}}, + {"$project": {"_id": 0, "amends": 0}}, {"$replaceRoot": {"newRoot": {"$mergeObjects": ["$$ROOT", "$content"]}}}, ] diff --git a/src/aleph/web/__init__.py b/src/aleph/web/__init__.py index c3d4288ae..b4f8082fa 100644 --- a/src/aleph/web/__init__.py +++ b/src/aleph/web/__init__.py @@ -38,8 +38,8 @@ def init_sio(app: web.Application) -> socketio.AsyncServer: return sio -def create_app() -> web.Application: - app = web.Application(client_max_size=1024 ** 2 * 64) +def create_app(debug: bool = False) -> web.Application: + app = web.Application(client_max_size=1024**2 * 64, debug=debug) tpl_path = pkg_resources.resource_filename("aleph.web", "templates") jinja_loader = jinja2.ChoiceLoader( diff --git a/src/aleph/web/controllers/posts.py b/src/aleph/web/controllers/posts.py index 126da1b31..916f036e4 100644 --- a/src/aleph/web/controllers/posts.py +++ b/src/aleph/web/controllers/posts.py @@ -85,10 +85,7 @@ async def view_posts_list(request): context = {"posts": posts} if pagination_per_page is not None: - total_msgs = await Message.collection.count_documents( - filter=find_filters, - projection={"_id": 0}, - ) + total_msgs = await Message.collection.count_documents(filter=find_filters) pagination = Pagination( pagination_page, diff --git a/tests/api/__init__.py b/tests/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/api/conftest.py b/tests/api/conftest.py new file mode 100644 index 000000000..7dd3c87cf --- /dev/null +++ b/tests/api/conftest.py @@ -0,0 +1,16 @@ +import json +from pathlib import Path +import pytest_asyncio +from aleph.model.messages import Message + + +@pytest_asyncio.fixture +async def fixture_messages(test_db): + fixtures_dir = Path(__file__).parent / "fixtures" + fixtures_file = fixtures_dir / "fixture_messages.json" + + with fixtures_file.open() as f: + messages = json.load(f) + + await Message.collection.insert_many(messages) + return messages diff --git a/tests/api/fixtures/fixture_posts.json b/tests/api/fixtures/fixture_posts.json new file mode 100644 index 000000000..e69de29bb diff --git a/tests/api/test_messages.py b/tests/api/test_messages.py index 54d4e527d..3fd9337c9 100644 --- a/tests/api/test_messages.py +++ b/tests/api/test_messages.py @@ -1,20 +1,13 @@ import itertools -import json -from pathlib import Path -from typing import Dict, Iterable, List +from typing import Dict, Iterable import pytest -import pytest_asyncio -from aleph.model.messages import Message +from .utils import get_messages_by_keys MESSAGES_URI = "/api/v0/messages.json" -def get_messages_by_keys(messages: Iterable[Dict], **keys) -> List[Dict]: - return [msg for msg in messages if all(msg[k] == v for k, v in keys.items())] - - def check_message_fields(messages: Iterable[Dict]): """ Basic checks on fields. For example, check that we do not expose internal data @@ -38,18 +31,6 @@ def assert_messages_equal(messages: Iterable[Dict], expected_messages: Iterable[ assert message["signature"] == expected_message["signature"] -@pytest_asyncio.fixture -async def fixture_messages(test_db): - fixtures_dir = Path(__file__).parent / "fixtures" - fixtures_file = fixtures_dir / "fixture_messages.json" - - with fixtures_file.open() as f: - messages = json.load(f) - - await Message.collection.insert_many(messages) - return messages - - @pytest.mark.asyncio async def test_get_messages(fixture_messages, ccn_api_client): response = await ccn_api_client.get(MESSAGES_URI) diff --git a/tests/api/test_posts.py b/tests/api/test_posts.py new file mode 100644 index 000000000..436f6d932 --- /dev/null +++ b/tests/api/test_posts.py @@ -0,0 +1,59 @@ +from typing import Dict, Iterable + +import aiohttp +import pytest +from aleph_message.models import MessageType + +from .utils import get_messages_by_keys + +POSTS_URI = "/api/v0/posts.json" + + +def assert_posts_equal(posts: Iterable[Dict], expected_messages: Iterable[Dict]): + posts_by_hash = {post["item_hash"]: post for post in posts} + + for expected_message in expected_messages: + post = posts_by_hash[expected_message["item_hash"]] + assert "_id" not in post + + assert post["chain"] == expected_message["chain"] + assert post["channel"] == expected_message["channel"] + assert post["sender"] == expected_message["sender"] + assert post["signature"] == expected_message["signature"] + + if expected_message.get("forgotten_by", []): + assert post["content"] is None + continue + + if "content" not in expected_message["content"]: + # TODO: there is a problem with the spec of posts: they can be specified + # without an internal "content" field, which does not break the + # endpoint but returns the content of message["content"] instead. + # We skip the issue for now. + continue + + assert post["content"] == expected_message["content"]["content"] + + +async def get_posts(api_client, **params) -> aiohttp.ClientResponse: + return await api_client.get(POSTS_URI, params=params) + + +async def get_posts_expect_success(api_client, **params): + response = await get_posts(api_client, **params) + assert response.status == 200, await response.text() + data = await response.json() + return data["posts"] + + +@pytest.mark.asyncio +async def test_get_posts(fixture_messages, ccn_api_client): + # The POST messages in the fixtures file do not amend one another, so we should have + # 1 POST = 1 message. + post_messages = get_messages_by_keys( + fixture_messages, + type=MessageType.post, + ) + posts = await get_posts_expect_success(ccn_api_client) + + assert_posts_equal(posts, post_messages) diff --git a/tests/api/utils/__init__.py b/tests/api/utils/__init__.py new file mode 100644 index 000000000..182366ba5 --- /dev/null +++ b/tests/api/utils/__init__.py @@ -0,0 +1,27 @@ +from typing import Dict, Iterable, List, Callable + + +def get_messages_by_predicate( + messages: Iterable[Dict], predicate: Callable[[Dict], bool] +) -> List[Dict]: + """ + Filters messages based on a user-provided predicate + (=a function/lambda operating on a single message). + """ + + return [msg for msg in messages if predicate(msg)] + + +def get_messages_by_keys(messages: Iterable[Dict], **keys) -> List[Dict]: + """ + Filters messages based on user-provided keys. + + Example: + >>> filtered_messages = get_messages_by_keys( + >>> message_list, item_hash="some-hash", channel="MY-CHANNEL" + >>> ) + + """ + return get_messages_by_predicate( + messages, lambda msg: all(msg[k] == v for k, v in keys.items()) + ) diff --git a/tests/conftest.py b/tests/conftest.py index 5f73a7158..d4d92281f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -62,7 +62,7 @@ async def ccn_api_client(aiohttp_client, mock_config): event_loop = asyncio.get_event_loop() event_loop.set_debug(True) - app = create_app() + app = create_app(debug=True) app["config"] = mock_config client = await aiohttp_client(app)