Skip to content

Commit bed0a2b

Browse files
authored
Merge pull request #2975 from IntersectMBO/artur/db-sync-config-test
Add db-sync config tests
2 parents c044066 + 2c5f9d7 commit bed0a2b

File tree

8 files changed

+942
-1
lines changed

8 files changed

+942
-1
lines changed

.github/env_dbsync_config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CLUSTERS_COUNT=1
2+
MARKEXPR=(dbsync and smoke) or dbsync_config
3+
DBSYNC_SKIP_INDEXES=true

.github/workflows/dbsync_config.yaml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: db-sync config tests
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
node_rev:
7+
description: "cardano-node revision"
8+
default: master
9+
cli_rev:
10+
description: "cardano-cli revision (optional)"
11+
default: ""
12+
dbsync_rev:
13+
description: "db-sync revision"
14+
default: master
15+
cluster_era:
16+
type: choice
17+
description: "Cluster era"
18+
options:
19+
- conway 10
20+
- conway 9
21+
default: conway 10
22+
topology:
23+
type: choice
24+
description: "Network topology"
25+
options:
26+
- p2p
27+
- legacy
28+
- mixed
29+
default: p2p
30+
utxo_backend:
31+
type: choice
32+
description: "UTxO backend"
33+
options:
34+
- ""
35+
- mem
36+
- disk
37+
default: ""
38+
byron_cluster:
39+
type: boolean
40+
default: false
41+
description: "Start cluster in Byron era"
42+
testrun_name:
43+
required: false
44+
description: "Test run name (internal)"
45+
skip_passed:
46+
type: boolean
47+
default: false
48+
description: "Skip tests that already passed (internal)"
49+
50+
run-name: ${{ inputs.testrun_name && 'Run:' || ''}} ${{ inputs.testrun_name }} ${{ (inputs.testrun_name && inputs.skip_passed) && ':repeat:' || '' }}
51+
52+
jobs:
53+
regression_tests:
54+
# reusable workflow from local repo and same branch as this config
55+
uses: ./.github/workflows/regression_reusable.yaml
56+
with:
57+
node_rev: ${{ inputs.node_rev }}
58+
cli_rev: ${{ inputs.cli_rev }}
59+
dbsync_rev: ${{ inputs.dbsync_rev }}
60+
cluster_era: ${{ inputs.cluster_era }}
61+
topology: ${{ inputs.topology }}
62+
utxo_backend: ${{ inputs.utxo_backend }}
63+
byron_cluster: ${{ inputs.byron_cluster }}
64+
testrun_name: ${{ inputs.testrun_name }}
65+
skip_passed: ${{ inputs.skip_passed }}
66+
env-path: .github/env_dbsync_config
67+
secrets:
68+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69+
TCACHE_BASIC_AUTH: ${{ secrets.TCACHE_BASIC_AUTH }}
70+
TCACHE_URL: ${{ secrets.TCACHE_URL }}

cardano_node_tests/cluster_scripts/conway_fast/postgres-setup.sh

100644100755
File mode changed.
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
"""Tests for basic DB-Sync configuration options."""
2+
3+
import enum
4+
import logging
5+
import shutil
6+
import typing as tp
7+
8+
import allure
9+
import pytest
10+
import pytest_subtests
11+
from cardano_clusterlib import clusterlib
12+
13+
from cardano_node_tests.tests import common
14+
from cardano_node_tests.utils import cluster_nodes
15+
from cardano_node_tests.utils import configuration
16+
from cardano_node_tests.utils import dbsync_service_manager as db_sync
17+
from cardano_node_tests.utils import dbsync_utils
18+
from cardano_node_tests.utils import helpers
19+
20+
LOGGER = logging.getLogger(__name__)
21+
22+
pytestmark = [
23+
pytest.mark.skipif(
24+
configuration.CLUSTERS_COUNT != 1,
25+
reason="db-sync config tests can run on a single cluster only",
26+
),
27+
pytest.mark.dbsync_config,
28+
pytest.mark.needs_dbsync,
29+
]
30+
31+
32+
class TableCondition(str, enum.Enum):
33+
"""Enum for table-level db-sync state conditions."""
34+
35+
EMPTY = "empty"
36+
NOT_EMPTY = "not_empty"
37+
EXISTS = "exists"
38+
NOT_EXISTS = "not_exists"
39+
40+
41+
class ColumnCondition(str, enum.Enum):
42+
"""Enum for column-level db-sync condition checks."""
43+
44+
ZERO = "column_condition:=0"
45+
IS_NULL = "column_condition:IS NULL"
46+
47+
48+
def check_dbsync_state(
49+
expected_state: dict[tp.Union[str, db_sync.Table], TableCondition | ColumnCondition],
50+
) -> None:
51+
"""Check the state of db-sync tables and columns against expected conditions.
52+
53+
Args:
54+
expected_state: Dictionary specifying conditions to verify where:
55+
- Key format:
56+
* "table" for table-level checks
57+
* "table.column" for column-level checks
58+
- Value format:
59+
* TableCondition enum values for table-level checks
60+
* ColumnCondition enum values for column-level checks
61+
62+
Returns:
63+
bool: True if all conditions match, False otherwise
64+
"""
65+
for key, condition in expected_state.items():
66+
if "." in key: # Column-level check
67+
table, column = key.split(".", 1)
68+
assert isinstance(condition, ColumnCondition), (
69+
f"Invalid column condition format: {condition}"
70+
)
71+
column_condition = condition.value.split(":", 1)[1]
72+
dbsync_utils.check_column_condition(table, column, column_condition)
73+
else: # Table-level check
74+
assert isinstance(condition, TableCondition), (
75+
f"Invalid table condition format: {condition}"
76+
)
77+
match condition:
78+
case TableCondition.EMPTY:
79+
assert dbsync_utils.table_empty(key), (
80+
f"Expected {key} to be empty, but it is not."
81+
)
82+
case TableCondition.NOT_EMPTY:
83+
assert not dbsync_utils.table_empty(key), (
84+
f"Expected {key} to have data, but it is empty."
85+
)
86+
case TableCondition.EXISTS:
87+
assert dbsync_utils.table_exists(key), (
88+
f"Expected {key} to exist, but it does not."
89+
)
90+
case TableCondition.NOT_EXISTS:
91+
assert not dbsync_utils.table_exists(key), (
92+
f"Expected {key} to NOT exist, but it does."
93+
)
94+
case _:
95+
error_msg = f"Unknown table condition '{condition}' for table '{key}'"
96+
raise ValueError(error_msg)
97+
98+
99+
@pytest.fixture
100+
def db_sync_manager(
101+
cluster_singleton: clusterlib.ClusterLib, # noqa: ARG001
102+
) -> tp.Generator[db_sync.DBSyncManager, None, None]:
103+
"""Provide db-sync manager on a singleton cluster.
104+
105+
Creates and returns a DBSyncManager instance with locked cluster resources
106+
to ensure exclusive access during testing.
107+
108+
Returns db-sync configuration to its original state after testing is finished.
109+
"""
110+
cluster_dir = cluster_nodes.get_cluster_env().state_dir
111+
config_file = cluster_dir / "dbsync-config.yaml"
112+
113+
# Backup the config file
114+
orig_config_file = cluster_dir / "dbsync-config.yaml.original"
115+
if not orig_config_file.exists():
116+
shutil.copy(config_file, orig_config_file)
117+
118+
manager = db_sync.DBSyncManager()
119+
yield manager
120+
121+
# Restore the original config file
122+
shutil.copy(orig_config_file, config_file)
123+
manager.restart_with_config()
124+
125+
126+
@pytest.mark.order(-1)
127+
class TestDBSyncConfig:
128+
"""Tests for DB-Sync Config."""
129+
130+
def get_subtests(self) -> tp.Generator[tp.Callable, None, None]:
131+
"""Get the DB-Sync Config scenarios.
132+
133+
The scenarios are executed as subtests in the `test_dbsync_config` test.
134+
"""
135+
136+
def basic_tx_out(
137+
db_sync_manager: db_sync.DBSyncManager,
138+
):
139+
"""Test `tx_out` option."""
140+
db_config = db_sync_manager.get_config_builder()
141+
142+
# Test tx_out : enable
143+
db_sync_manager.restart_with_config(
144+
custom_config=db_config.with_tx_out(
145+
value=db_sync.TxOutMode.ENABLE, force_tx_in=False, use_address_table=False
146+
)
147+
)
148+
check_dbsync_state(
149+
expected_state={
150+
db_sync.Table.ADDRESS: TableCondition.NOT_EXISTS,
151+
db_sync.Table.TX_IN: TableCondition.NOT_EMPTY,
152+
db_sync.Table.TX_OUT: TableCondition.NOT_EMPTY,
153+
db_sync.Table.MA_TX_OUT: TableCondition.NOT_EMPTY,
154+
}
155+
)
156+
157+
# Test tx_out : disable
158+
db_sync_manager.restart_with_config(
159+
custom_config=db_config.with_tx_out(
160+
value=db_sync.TxOutMode.DISABLE, force_tx_in=True, use_address_table=True
161+
)
162+
)
163+
check_dbsync_state(
164+
expected_state={
165+
db_sync.Table.ADDRESS: TableCondition.NOT_EXISTS,
166+
db_sync.Table.TX_IN: TableCondition.EMPTY,
167+
db_sync.Table.TX_OUT: TableCondition.EMPTY,
168+
db_sync.Table.MA_TX_OUT: TableCondition.EMPTY,
169+
db_sync.Column.Tx.FEE: ColumnCondition.ZERO,
170+
db_sync.Column.Redeemer.SCRIPT_HASH: ColumnCondition.IS_NULL,
171+
}
172+
)
173+
174+
yield basic_tx_out
175+
176+
def tx_cbor_value_enable(
177+
db_sync_manager: db_sync.DBSyncManager,
178+
):
179+
"""Test enabled `tx_cbor` option."""
180+
db_config = db_sync_manager.get_config_builder()
181+
182+
db_sync_manager.restart_with_config(
183+
custom_config=db_config.with_tx_cbor(value=db_sync.SettingState.ENABLE)
184+
)
185+
check_dbsync_state(expected_state={db_sync.Table.TX_CBOR: TableCondition.NOT_EMPTY})
186+
187+
yield tx_cbor_value_enable
188+
189+
def tx_cbor_value_disable(
190+
db_sync_manager: db_sync.DBSyncManager,
191+
):
192+
"""Test disabled `tx_cbor` option."""
193+
db_config = db_sync_manager.get_config_builder()
194+
195+
db_sync_manager.restart_with_config(
196+
custom_config=db_config.with_tx_cbor(value=db_sync.SettingState.DISABLE)
197+
)
198+
check_dbsync_state(expected_state={db_sync.Table.TX_CBOR: TableCondition.EMPTY})
199+
200+
yield tx_cbor_value_disable
201+
202+
def multi_asset_enable(
203+
db_sync_manager: db_sync.DBSyncManager,
204+
):
205+
"""Test elabled `multi_asset` option."""
206+
db_config = db_sync_manager.get_config_builder()
207+
208+
db_sync_manager.restart_with_config(
209+
custom_config=db_config.with_multi_asset(enable=True)
210+
)
211+
check_dbsync_state({db_sync.Table.MULTI_ASSET: TableCondition.NOT_EMPTY})
212+
213+
yield multi_asset_enable
214+
215+
def multi_asset_disable(
216+
db_sync_manager: db_sync.DBSyncManager,
217+
):
218+
"""Test disabled `multi_asset` option."""
219+
db_config = db_sync_manager.get_config_builder()
220+
221+
db_sync_manager.restart_with_config(
222+
custom_config=db_config.with_multi_asset(enable=False)
223+
)
224+
check_dbsync_state({db_sync.Table.MULTI_ASSET: TableCondition.EMPTY})
225+
226+
yield multi_asset_disable
227+
228+
@allure.link(helpers.get_vcs_link())
229+
def test_dbsync_config(
230+
self,
231+
cluster_singleton: clusterlib.ClusterLib,
232+
db_sync_manager: db_sync.DBSyncManager,
233+
subtests: pytest_subtests.SubTests,
234+
):
235+
"""Run db-sync config subtests."""
236+
cluster = cluster_singleton
237+
common.get_test_id(cluster)
238+
239+
for subt in self.get_subtests():
240+
with subtests.test(scenario=subt.__name__):
241+
subt(db_sync_manager)

0 commit comments

Comments
 (0)