forked from subquery/cosmos-subql-starter
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2046443
commit 6379b3e
Showing
4 changed files
with
258 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
import json | ||
import sys | ||
import time | ||
import unittest | ||
from dataclasses import dataclass | ||
from pathlib import Path | ||
from typing import List, Dict | ||
|
||
import graphql | ||
from cosmpy.aerial.tx_helpers import SubmittedTx | ||
from gql import gql | ||
|
||
repo_root_path = Path(__file__).parent.parent.parent.parent.absolute() | ||
sys.path.insert(0, str(repo_root_path)) | ||
|
||
from src.genesis.helpers.field_enums import AlmanacRegistrations, AlmanacRecords | ||
from tests.helpers.contracts import AlmanacContract, DefaultAlmanacContractConfig | ||
from tests.helpers.entity_test import EntityTest | ||
from tests.helpers.graphql import test_filtered_query | ||
from tests.helpers.regexes import msg_id_regex, tx_id_regex, block_id_regex | ||
|
||
|
||
def by_expiry_height(registration_node: Dict): | ||
return int(registration_node["expiryHeight"]) | ||
|
||
|
||
@dataclass | ||
class Scenario: | ||
name: str | ||
query: graphql.DocumentNode | ||
expected: any | ||
|
||
|
||
class TestAlmanac(EntityTest): | ||
test_registrations_endpoints = [ | ||
"127.0.0.1:9999", | ||
"127.0.0.1:8888", | ||
"127.0.0.1:7777", | ||
"127.0.0.1:6666" | ||
] | ||
submitted_txs: List[SubmittedTx] = [] | ||
expected_registrations: List[Dict] = [] | ||
expected_records: List[Dict] = [ | ||
{ | ||
"service": { | ||
"protocols": ["grpc"], | ||
"endpoints": [{ | ||
"url": endpoint, | ||
# NB: not "proper" usage of weight; for testing only | ||
"weight": i | ||
}] | ||
} | ||
} for (i, endpoint) in enumerate(test_registrations_endpoints) | ||
] | ||
|
||
@classmethod | ||
def setUpClass(cls): | ||
super().setUpClass() | ||
cls.clean_db({"almanac_registrations", "almanac_resolutions"}) | ||
cls._contract = AlmanacContract(cls.ledger_client, cls.validator_wallet) | ||
|
||
# NB: broadcast multiple registrations | ||
for (i, expected_record) in enumerate(cls.expected_records): | ||
tx = cls._contract.execute({ | ||
"register": { | ||
"record": expected_record, | ||
# "sequence": , | ||
# "signature": , | ||
} | ||
}, cls.validator_wallet, funds=DefaultAlmanacContractConfig.register_stake_funds) | ||
tx.wait_to_complete() | ||
cls.submitted_txs.append(tx) | ||
cls.expected_registrations.append({ | ||
"accountId": cls.validator_address, | ||
"expiryHeight": tx.response.height + DefaultAlmanacContractConfig.expiry_height, | ||
"record": expected_record | ||
}) | ||
# NB: wait for the indexer | ||
time.sleep(7) | ||
|
||
def test_registrations_sql(self): | ||
registrations = self.db_cursor.execute(AlmanacRegistrations.select_query()).fetchall() | ||
actual_reg_length = len(registrations) | ||
|
||
expected_registrations_count = len(self.expected_registrations) | ||
self.assertEqual(expected_registrations_count, | ||
actual_reg_length, | ||
f"expected {expected_registrations_count} registrations; got {actual_reg_length}") | ||
for (i, registration) in enumerate(registrations): | ||
self.assertEqual(self.validator_address, registration[AlmanacRegistrations.account_id.value]) | ||
self.assertLess(self.submitted_txs[i].response.height, | ||
registration[AlmanacRegistrations.expiry_height.value]) | ||
self.assertRegex(registration[AlmanacRegistrations.id.value], msg_id_regex) | ||
self.assertRegex(registration[AlmanacRegistrations.transaction_id.value], tx_id_regex) | ||
self.assertRegex(registration[AlmanacRegistrations.block_id.value], block_id_regex) | ||
|
||
def matches_expected_record(_record: Dict) -> bool: | ||
return _record["service"]["endpoints"][0]["weight"] == i | ||
|
||
# Lookup related record | ||
record = self.db_cursor.execute( | ||
AlmanacRecords.select_where( | ||
f"almanac_records.id = '{registration[AlmanacRegistrations.record_id.value]}'", | ||
[AlmanacRecords.table, AlmanacRegistrations.table])).fetchone() | ||
expected_record = next(r for r in self.expected_records if matches_expected_record(r)) | ||
self.assertIsNotNone(record) | ||
self.assertIsNotNone(expected_record) | ||
self.assertDictEqual(expected_record["service"], record[AlmanacRecords.service.value]) | ||
self.assertRegex(record[AlmanacRecords.id.value], msg_id_regex) | ||
self.assertRegex(record[AlmanacRecords.transaction_id.value], tx_id_regex) | ||
self.assertRegex(record[AlmanacRecords.block_id.value], block_id_regex) | ||
|
||
def test_registrations_gql(self): | ||
registrations_nodes = """ | ||
{ | ||
id | ||
expiryHeight | ||
accountId | ||
record { | ||
id | ||
service | ||
# registrationId | ||
# eventId | ||
transactionId | ||
blockId | ||
} | ||
transactionId | ||
blockId | ||
} | ||
""" | ||
|
||
last_tx_height = self.submitted_txs[-1].response.height | ||
expired_registrations_query = test_filtered_query("almanacRegistrations", { | ||
"expiryHeight": { | ||
"lessThanOrEqualTo": str(last_tx_height) | ||
} | ||
}, registrations_nodes) | ||
|
||
active_registrations_query = test_filtered_query("almanacRegistrations", { | ||
"expiryHeight": { | ||
"greaterThan": str(last_tx_height) | ||
} | ||
}, registrations_nodes) | ||
|
||
all_registrations_query = gql("query {almanacRegistrations {nodes " + registrations_nodes + "}}") | ||
|
||
last_expired_height = last_tx_height - DefaultAlmanacContractConfig.expiry_height | ||
last_expired = next(r for r in self.submitted_txs if r.response.height == last_expired_height) | ||
last_expired_index = self.submitted_txs.index(last_expired) | ||
scenarios = [ | ||
Scenario( | ||
name="expired registrations", | ||
query=expired_registrations_query, | ||
expected=self.expected_registrations[0: last_expired_index + 1] | ||
), | ||
Scenario( | ||
name="active registrations", | ||
query=active_registrations_query, | ||
expected=self.expected_registrations[last_expired_index + 1:] | ||
), | ||
Scenario( | ||
name="all registrations", | ||
query=all_registrations_query, | ||
expected=self.expected_registrations | ||
), | ||
] | ||
|
||
for scenario in scenarios: | ||
with self.subTest(scenario.name): | ||
gql_result = self.gql_client.execute(scenario.query) | ||
registrations = gql_result["almanacRegistrations"]["nodes"] | ||
self.assertEqual(len(scenario.expected), len(registrations)) | ||
|
||
# TODO: use respective gql order by when available | ||
# NB: sort by expiry height so that indexes match | ||
# their respective scenario.expected index | ||
list.sort(registrations, key=by_expiry_height) | ||
self.assertEqual(len(scenario.expected), len(registrations)) | ||
|
||
for (i, registration) in enumerate(registrations): | ||
self.assertRegex(registration["id"], msg_id_regex) | ||
self.assertEqual(self.validator_address, registration["accountId"], ) | ||
self.assertEqual(str(scenario.expected[i]["expiryHeight"]), registration["expiryHeight"]) | ||
self.assertRegex(registration["transactionId"], tx_id_regex) | ||
self.assertRegex(registration["blockId"], block_id_regex) | ||
# TODO: assert record equality | ||
|
||
if __name__ == "__main__": | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters