Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions tests/api/conftest.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
import json
from pathlib import Path
from typing import Dict, List

import pytest_asyncio
from aleph.model.messages import Message


@pytest_asyncio.fixture
async def fixture_messages(test_db):
async def _load_fixtures(filename: str):
fixtures_dir = Path(__file__).parent / "fixtures"
fixtures_file = fixtures_dir / "fixture_messages.json"
fixtures_file = fixtures_dir / filename

with fixtures_file.open() as f:
messages = json.load(f)

await Message.collection.insert_many(messages)
return messages


@pytest_asyncio.fixture
async def fixture_messages(test_db) -> List[Dict]:
return await _load_fixtures("fixture_messages.json")


@pytest_asyncio.fixture
async def fixture_aggregate_messages(test_db) -> List[Dict]:
return await _load_fixtures("fixture_aggregates.json")


@pytest_asyncio.fixture
async def fixture_post_messages(test_db) -> List[Dict]:
return await _load_fixtures("fixture_posts.json")
123 changes: 123 additions & 0 deletions tests/api/fixtures/fixture_aggregates.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
[
{
"chain": "ETH",
"item_hash": "53c2b16aa84b10878982a2920844625546f5db32337ecd9dd15928095a30381c",
"sender": "0x720F319A9c3226dCDd7D8C49163D79EDa1084E98",
"type": "AGGREGATE",
"channel": "INTEGRATION_TESTS",
"content": {
"address": "0x720F319A9c3226dCDd7D8C49163D79EDa1084E98",
"time": 1644857371.391834,
"key": "test_reference",
"content": {
"a": 1,
"b": 2
}
},
"item_content": "{\"address\":\"0x720F319A9c3226dCDd7D8C49163D79EDa1084E98\",\"time\":1644857371.391834,\"key\":\"test_reference\",\"content\":{\"a\":1,\"b\":2}}",
"item_type": "inline",
"signature": "0x7eee4cfc03b963ec51f04f60f6f7d58b0f24e0309d209feecb55af9e411ed1c01cfb547bb13539e91308b044c3661d93ddf272426542bc1a47722614cb0cd3621c",
"size": 128,
"time": 1644859283.101
},
{
"chain": "ETH",
"item_hash": "0022ed09d16a1c3d6cbb3c7e2645657ebaa0382eba65be06264b106f528b85bf",
"sender": "0x720F319A9c3226dCDd7D8C49163D79EDa1084E98",
"type": "AGGREGATE",
"channel": "INTEGRATION_TESTS",
"content": {
"address": "0x720F319A9c3226dCDd7D8C49163D79EDa1084E98",
"time": 1644857704.6253593,
"key": "test_reference",
"content": {
"c": 3,
"d": 4
}
},
"item_content": "{\"address\":\"0x720F319A9c3226dCDd7D8C49163D79EDa1084E98\",\"time\":1644857704.6253593,\"key\":\"test_reference\",\"content\":{\"c\":3,\"d\":4}}",
"item_type": "inline",
"signature": "0xe6129196c36b066302692b53bcb78a9d8c996854b170238ebfe56924f0b6be604883c30a66d75250de489e1edb683c7da8ddb1ccb50a39d1bbbdad617e5c958f1b",
"size": 129,
"time": 1644859283.12
},
{
"chain": "ETH",
"item_hash": "a87004aa03f8ae63d2c4bbe84b93b9ce70ca6482ce36c82ab0b0f689fc273f34",
"sender": "0xaC033C1cA5C49Eff98A1D9a56BeDBC4840010BA4",
"type": "AGGREGATE",
"channel": "INTEGRATION_TESTS",
"content": {
"address": "0xaC033C1cA5C49Eff98A1D9a56BeDBC4840010BA4",
"time": 1648215802.3821976,
"key": "test_reference",
"content": {
"c": 3,
"d": 4
}
},
"item_content": "{\"address\":\"0xaC033C1cA5C49Eff98A1D9a56BeDBC4840010BA4\",\"time\":1648215802.3821976,\"key\":\"test_reference\",\"content\":{\"c\":3,\"d\":4}}",
"item_type": "inline",
"signature": "0xc0f6ce2e4e9561b3949d51a97b8746125e1f031bbc13813cc74f1f61eea654fe300ad5e9ec098d41374bc0e43f83f2d66b834672abb811ae6a2dcdbd09f2565f1c",
"size": 129,
"time": 1648467547.771
},
{
"chain": "ETH",
"item_hash": "f875631a6c4a70ce44143bdd9a64861a5ce6f68e2267a00979ff0ad399a6c780",
"sender": "0x720F319A9c3226dCDd7D8C49163D79EDa1084E98",
"type": "AGGREGATE",
"channel": "INTEGRATION_TESTS",
"confirmations": [
{
"chain": "ETH",
"height": 14205580,
"hash": "0x234b3cb25e893780c4cf50ec82c4ae9a61d61b766a367b559ae8192463e84a1b"
}
],
"confirmed": true,
"content": {
"address": "0x720F319A9c3226dCDd7D8C49163D79EDa1084E98",
"time": 1644857371.1748412,
"key": "test_target",
"content": {
"a": 1,
"b": 2
}
},
"item_content": "{\"address\":\"0x720F319A9c3226dCDd7D8C49163D79EDa1084E98\",\"time\":1644857371.1748412,\"key\":\"test_target\",\"content\":{\"a\":1,\"b\":2}}",
"item_type": "inline",
"signature": "0xaa28dafaecfd063bd30f65c877260bcdab37931fe7d8ef13173a952ae57a79e544d9fc9ae9131ba6ce6638bdbd62996467eb4a999416603ff2d1eaff372427bd1b",
"size": 126,
"time": 1644859283.1
},
{
"chain": "ETH",
"item_hash": "8c83e020b1f0661de3238ecaf2a41fd2f9dfe4a6c56453ccdf3ddd3fa4fae147",
"sender": "0x720F319A9c3226dCDd7D8C49163D79EDa1084E98",
"type": "AGGREGATE",
"channel": "INTEGRATION_TESTS",
"confirmations": [
{
"chain": "ETH",
"height": 14205311,
"hash": "0x805dcf51856c813d5524f5b64555145fce6487b81dc605e6657fd208bebb2e05"
}
],
"confirmed": true,
"content": {
"address": "0x720F319A9c3226dCDd7D8C49163D79EDa1084E98",
"time": 1644853185.0710306,
"key": "test_key",
"content": {
"a": 1,
"b": 2
}
},
"item_content": "{\"address\":\"0x720F319A9c3226dCDd7D8C49163D79EDa1084E98\",\"time\":1644853185.0710306,\"key\":\"test_key\",\"content\":{\"a\":1,\"b\":2}}",
"item_type": "inline",
"signature": "0x4e3060c596de77b19f2791fbc34eff3d6c89c63d29250c960e9c1b752898d22d0f7d7759dc0b0d935b93e29534e6861d7a8deeb75cd69836acf0ad0e6e8626601b",
"size": 123,
"time": 1644855661.089
}
]
141 changes: 141 additions & 0 deletions tests/api/test_aggregates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import itertools
from typing import Dict, Iterable, List

import aiohttp
import pytest

AGGREGATES_URI = "/api/v0/aggregates/{address}.json"

# Another address with three aggregates
ADDRESS_1 = "0x720F319A9c3226dCDd7D8C49163D79EDa1084E98"
# Another address with one aggregate
ADDRESS_2 = "0xaC033C1cA5C49Eff98A1D9a56BeDBC4840010BA4"

EXPECTED_AGGREGATES = {
ADDRESS_1: {
"test_key": {"a": 1, "b": 2},
"test_target": {"a": 1, "b": 2},
"test_reference": {"a": 1, "b": 2, "c": 3, "d": 4},
},
ADDRESS_2: {"test_reference": {"c": 3, "d": 4}},
}


def make_uri(address: str) -> str:
return AGGREGATES_URI.format(address=address)


def assert_aggregates_equal(expected: List[Dict], actual: Dict[str, Dict]):
for expected_aggregate in expected:
aggregate = actual[expected_aggregate["content"]["key"]]
assert "_id" not in aggregate

assert aggregate == expected_aggregate["content"]["content"]


def merge_aggregates(messages: Iterable[Dict]) -> List[Dict]:
def merge_content(_messages: List[Dict]) -> Dict:
original = _messages[0]
for update in _messages[1:]:
original["content"]["content"].update(update["content"]["content"])
return original

aggregates = []

for key, group in itertools.groupby(
sorted(messages, key=lambda msg: msg["content"]["key"]),
lambda msg: msg["content"]["key"],
):
sorted_messages = sorted(group, key=lambda msg: msg["time"])
aggregates.append(merge_content(sorted_messages))

return aggregates


async def get_aggregates(api_client, address: str, **params) -> aiohttp.ClientResponse:
return await api_client.get(make_uri(address), params=params)


async def get_aggregates_expect_success(api_client, address: str, **params):
response = await get_aggregates(api_client, address, **params)
assert response.status == 200, await response.text()
return await response.json()


@pytest.fixture()
def fixture_aggregates(fixture_aggregate_messages):
return merge_aggregates(fixture_aggregate_messages)


@pytest.mark.asyncio
async def test_get_aggregates_no_update(ccn_api_client, fixture_aggregates):
"""
Tests receiving an aggregate from an address which posted one aggregate and never
updated it.
"""

address = ADDRESS_2
aggregates = await get_aggregates_expect_success(ccn_api_client, address)

assert aggregates["address"] == address
assert aggregates["data"] == EXPECTED_AGGREGATES[address]


@pytest.mark.asyncio
async def test_get_aggregates(ccn_api_client, fixture_aggregates: List[Dict]):
"""
A more complex case with 3 aggregates, one of which was updated.
"""

address = ADDRESS_1
aggregates = await get_aggregates_expect_success(ccn_api_client, address)

assert address == aggregates["address"]
assert aggregates["data"]["test_key"] == {"a": 1, "b": 2}
assert aggregates["data"]["test_target"] == {"a": 1, "b": 2}
assert aggregates["data"]["test_reference"] == {"a": 1, "b": 2, "c": 3, "d": 4}

assert_aggregates_equal(fixture_aggregates, aggregates["data"])


@pytest.mark.asyncio
async def test_get_aggregates_filter_by_key(ccn_api_client, fixture_aggregates: List[Dict]):
"""
Tests the 'keys' query parameter.
"""

address, key = ADDRESS_1, "test_target"
aggregates = await get_aggregates_expect_success(ccn_api_client, address=address, keys=key)
assert aggregates["address"] == address
assert aggregates["data"][key] == EXPECTED_AGGREGATES[address][key]

# Multiple keys
address, keys = ADDRESS_1, ["test_target", "test_reference"]
aggregates = await get_aggregates_expect_success(ccn_api_client, address=address, keys=",".join(keys))
assert aggregates["address"] == address
for key in keys:
assert aggregates["data"][key] == EXPECTED_AGGREGATES[address][key], f"Key {key} does not match"


@pytest.mark.asyncio
async def test_get_aggregates_limit(ccn_api_client, fixture_aggregates: List[Dict]):
"""
Tests the 'limit' query parameter.
"""

address, key = ADDRESS_1, "test_reference"
aggregates = await get_aggregates_expect_success(ccn_api_client, address=address, keys=key, limit=1)
assert aggregates["address"] == address
assert aggregates["data"][key] == {"c": 3, "d": 4}


@pytest.mark.asyncio
async def test_get_aggregates_invalid_address(ccn_api_client, fixture_aggregates: List[Dict]):
"""
Pass an unknown address.
"""

invalid_address = "unknown"

response = await get_aggregates(ccn_api_client, invalid_address)
assert response.status == 404