Skip to content

Commit 30e0bf3

Browse files
authored
chore: Make contrib test pluggable (#2654)
* make contrib test plugable Signed-off-by: Oleksii Moskalenko <moskalenko.alexey@gmail.com> * no autouse Signed-off-by: Oleksii Moskalenko <moskalenko.alexey@gmail.com> * pass request Signed-off-by: Oleksii Moskalenko <moskalenko.alexey@gmail.com> * fix fixture name Signed-off-by: Oleksii Moskalenko <moskalenko.alexey@gmail.com> * publish trino fixture on package level Signed-off-by: Oleksii Moskalenko <moskalenko.alexey@gmail.com> * format Signed-off-by: Oleksii Moskalenko <moskalenko.alexey@gmail.com> * disable some tests for contrib tests run Signed-off-by: Oleksii Moskalenko <moskalenko.alexey@gmail.com> * address comments Signed-off-by: Oleksii Moskalenko <moskalenko.alexey@gmail.com>
1 parent 0e809c2 commit 30e0bf3

File tree

23 files changed

+255
-397
lines changed

23 files changed

+255
-397
lines changed

Makefile

+15-1
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,25 @@ test-python-integration-container:
7272
FEAST_USAGE=False IS_TEST=True FEAST_LOCAL_ONLINE_CONTAINER=True python -m pytest -n 8 --integration sdk/python/tests
7373

7474
test-python-universal-contrib:
75-
PYTHONPATH='.' FULL_REPO_CONFIGS_MODULE=sdk.python.feast.infra.offline_stores.contrib.contrib_repo_configuration FEAST_USAGE=False IS_TEST=True python -m pytest -n 8 --integration --universal sdk/python/tests
75+
PYTHONPATH='.' \
76+
FULL_REPO_CONFIGS_MODULE=sdk.python.feast.infra.offline_stores.contrib.contrib_repo_configuration \
77+
PYTEST_PLUGINS=feast.infra.offline_stores.contrib.trino_offline_store.tests \
78+
FEAST_USAGE=False IS_TEST=True \
79+
python -m pytest -n 8 --integration --universal \
80+
-k "not test_historical_retrieval_fails_on_validation and \
81+
not test_historical_retrieval_with_validation and \
82+
not test_historical_features_persisting and \
83+
not test_historical_retrieval_fails_on_validation and \
84+
not test_universal_cli and \
85+
not test_go_feature_server and \
86+
not test_feature_logging and \
87+
not test_universal_types" \
88+
sdk/python/tests
7689

7790
test-python-universal-postgres:
7891
PYTHONPATH='.' \
7992
FULL_REPO_CONFIGS_MODULE=sdk.python.feast.infra.offline_stores.contrib.postgres_repo_configuration \
93+
PYTEST_PLUGINS=sdk.python.feast.infra.offline_stores.contrib.postgres_offline_store.tests \
8094
FEAST_USAGE=False \
8195
IS_TEST=True \
8296
python -m pytest -x --integration --universal \

sdk/python/feast/infra/offline_stores/contrib/contrib_repo_configuration.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
from tests.integration.feature_repos.integration_test_repo_config import (
2-
IntegrationTestRepoConfig,
3-
)
4-
from tests.integration.feature_repos.universal.data_sources.spark_data_source_creator import (
1+
from feast.infra.offline_stores.contrib.spark_offline_store.tests.data_source import (
52
SparkDataSourceCreator,
63
)
7-
from tests.integration.feature_repos.universal.data_sources.trino import (
4+
from feast.infra.offline_stores.contrib.trino_offline_store.tests.data_source import (
85
TrinoSourceCreator,
96
)
7+
from tests.integration.feature_repos.integration_test_repo_config import (
8+
IntegrationTestRepoConfig,
9+
)
1010

1111
FULL_REPO_CONFIGS = [
1212
IntegrationTestRepoConfig(offline_store_creator=SparkDataSourceCreator),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .data_source import postgres_container # noqa
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import logging
2+
from typing import Dict, Optional
3+
4+
import pandas as pd
5+
import pytest
6+
from testcontainers.core.container import DockerContainer
7+
from testcontainers.core.waiting_utils import wait_for_logs
8+
9+
from feast.data_source import DataSource
10+
from feast.infra.offline_stores.contrib.postgres_offline_store.postgres import (
11+
PostgreSQLOfflineStoreConfig,
12+
PostgreSQLSource,
13+
)
14+
from feast.infra.utils.postgres.connection_utils import df_to_postgres_table
15+
from tests.integration.feature_repos.universal.data_source_creator import (
16+
DataSourceCreator,
17+
)
18+
from tests.integration.feature_repos.universal.online_store_creator import (
19+
OnlineStoreCreator,
20+
)
21+
22+
logger = logging.getLogger(__name__)
23+
24+
POSTGRES_USER = "test"
25+
POSTGRES_PASSWORD = "test"
26+
POSTGRES_DB = "test"
27+
28+
29+
@pytest.fixture(scope="session")
30+
def postgres_container():
31+
container = (
32+
DockerContainer("postgres:latest")
33+
.with_exposed_ports(5432)
34+
.with_env("POSTGRES_USER", POSTGRES_USER)
35+
.with_env("POSTGRES_PASSWORD", POSTGRES_PASSWORD)
36+
.with_env("POSTGRES_DB", POSTGRES_DB)
37+
)
38+
39+
container.start()
40+
41+
log_string_to_wait_for = "database system is ready to accept connections"
42+
waited = wait_for_logs(
43+
container=container, predicate=log_string_to_wait_for, timeout=30, interval=10,
44+
)
45+
logger.info("Waited for %s seconds until postgres container was up", waited)
46+
47+
yield container
48+
container.stop()
49+
50+
51+
class PostgreSQLDataSourceCreator(DataSourceCreator, OnlineStoreCreator):
52+
def __init__(
53+
self, project_name: str, fixture_request: pytest.FixtureRequest, **kwargs
54+
):
55+
super().__init__(project_name,)
56+
57+
self.project_name = project_name
58+
self.container = fixture_request.getfixturevalue("postgres_container")
59+
if not self.container:
60+
raise RuntimeError(
61+
"In order to use this data source "
62+
"'feast.infra.offline_stores.contrib.postgres_offline_store.tests' "
63+
"must be include into pytest plugins"
64+
)
65+
66+
self.offline_store_config = PostgreSQLOfflineStoreConfig(
67+
type="postgres",
68+
host="localhost",
69+
port=self.container.get_exposed_port(5432),
70+
database=self.container.env["POSTGRES_DB"],
71+
db_schema="public",
72+
user=self.container.env["POSTGRES_USER"],
73+
password=self.container.env["POSTGRES_PASSWORD"],
74+
)
75+
76+
def create_data_source(
77+
self,
78+
df: pd.DataFrame,
79+
destination_name: str,
80+
suffix: Optional[str] = None,
81+
timestamp_field="ts",
82+
created_timestamp_column="created_ts",
83+
field_mapping: Dict[str, str] = None,
84+
) -> DataSource:
85+
destination_name = self.get_prefixed_table_name(destination_name)
86+
87+
if self.offline_store_config:
88+
df_to_postgres_table(self.offline_store_config, df, destination_name)
89+
90+
return PostgreSQLSource(
91+
name=destination_name,
92+
query=f"SELECT * FROM {destination_name}",
93+
timestamp_field=timestamp_field,
94+
created_timestamp_column=created_timestamp_column,
95+
field_mapping=field_mapping or {"ts_1": "ts"},
96+
)
97+
98+
def create_offline_store_config(self) -> PostgreSQLOfflineStoreConfig:
99+
assert self.offline_store_config
100+
return self.offline_store_config
101+
102+
def get_prefixed_table_name(self, suffix: str) -> str:
103+
return f"{self.project_name}_{suffix}"
104+
105+
def create_online_store(self) -> Dict[str, str]:
106+
assert self.container
107+
return {
108+
"type": "postgres",
109+
"host": "localhost",
110+
"port": self.container.get_exposed_port(5432),
111+
"database": POSTGRES_DB,
112+
"db_schema": "feature_store",
113+
"user": POSTGRES_USER,
114+
"password": POSTGRES_PASSWORD,
115+
}
116+
117+
def create_saved_dataset_destination(self):
118+
# FIXME: ...
119+
return None
120+
121+
def teardown(self):
122+
pass

sdk/python/feast/infra/offline_stores/contrib/postgres_repo_configuration.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
from feast.infra.offline_stores.contrib.postgres_offline_store.tests.data_source import (
2+
PostgreSQLDataSourceCreator,
3+
)
14
from tests.integration.feature_repos.integration_test_repo_config import (
25
IntegrationTestRepoConfig,
36
)
4-
from tests.integration.feature_repos.universal.data_sources.postgres import (
5-
PostgreSQLDataSourceCreator,
6-
)
77

88
FULL_REPO_CONFIGS = [
99
IntegrationTestRepoConfig(

sdk/python/feast/infra/offline_stores/contrib/spark_offline_store/tests/__init__.py

Whitespace-only changes.

sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/test_config/manual_tests.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
from feast.infra.offline_stores.contrib.trino_offline_store.tests.data_source import (
2+
TrinoSourceCreator,
3+
)
14
from tests.integration.feature_repos.integration_test_repo_config import (
25
IntegrationTestRepoConfig,
36
)
4-
from tests.integration.feature_repos.universal.data_sources.trino import (
5-
TrinoSourceCreator,
6-
)
77

88
FULL_REPO_CONFIGS = [
99
IntegrationTestRepoConfig(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .data_source import trino_container # noqa

sdk/python/tests/integration/feature_repos/universal/data_sources/trino.py sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/tests/data_source.py

+31-27
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Dict, List, Optional
44

55
import pandas as pd
6+
import pytest
67
from testcontainers.core.container import DockerContainer
78
from testcontainers.core.waiting_utils import wait_for_logs
89

@@ -24,46 +25,49 @@
2425
)
2526

2627

28+
@pytest.fixture(scope="session")
29+
def trino_container():
30+
current_file = pathlib.Path(__file__).parent.resolve()
31+
catalog_dir = current_file.parent.joinpath("catalog")
32+
container = (
33+
DockerContainer("trinodb/trino:376")
34+
.with_volume_mapping(catalog_dir, "/etc/catalog/")
35+
.with_exposed_ports("8080")
36+
)
37+
38+
container.start()
39+
40+
log_string_to_wait_for = "SERVER STARTED"
41+
wait_for_logs(container=container, predicate=log_string_to_wait_for, timeout=30)
42+
43+
yield container
44+
45+
container.stop()
46+
47+
2748
class TrinoSourceCreator(DataSourceCreator):
2849

2950
tables: List[str] = []
3051

31-
def __init__(self, project_name: str, **kwargs):
52+
def __init__(
53+
self, project_name: str, fixture_request: pytest.FixtureRequest, **kwargs
54+
):
3255
super().__init__(project_name)
3356
self.tables_created: List[str] = []
34-
35-
if "offline_container" not in kwargs or not kwargs.get(
36-
"offline_container", None
37-
):
38-
# If we don't get an offline container provided, we try to create it on the fly.
39-
# the problem here is that each test creates its own conatiner, which basically
40-
# browns out developer laptops.
41-
current_file = pathlib.Path(__file__).parent.resolve()
42-
catalog_dir = current_file.parent.joinpath("catalog")
43-
self.container = (
44-
DockerContainer("trinodb/trino:376")
45-
.with_volume_mapping(catalog_dir, "/etc/catalog/")
46-
.with_exposed_ports("8080")
57+
self.container = fixture_request.getfixturevalue("trino_container")
58+
if not self.container:
59+
raise RuntimeError(
60+
"In order to use this data source "
61+
"'feast.infra.offline_stores.contrib.trino_offline_store.tests' "
62+
"must be include into pytest plugins"
4763
)
48-
49-
self.container.start()
50-
self.provided_container = False
51-
log_string_to_wait_for = "SERVER STARTED"
52-
wait_for_logs(
53-
container=self.container, predicate=log_string_to_wait_for, timeout=30
54-
)
55-
else:
56-
self.provided_container = True
57-
self.container = kwargs["offline_container"]
58-
5964
self.exposed_port = self.container.get_exposed_port("8080")
6065
self.client = Trino(
6166
user="user", catalog="memory", host="localhost", port=self.exposed_port,
6267
)
6368

6469
def teardown(self):
65-
if not self.provided_container:
66-
self.container.stop()
70+
pass
6771

6872
def create_data_source(
6973
self,

sdk/python/feast/infra/online_stores/contrib/postgres.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def online_read(
131131
res = {}
132132
for feature_name, value_bin, event_ts in value:
133133
val = ValueProto()
134-
val.ParseFromString(value_bin)
134+
val.ParseFromString(bytes(value_bin))
135135
res[feature_name] = val
136136
result.append((event_ts, res))
137137
else:

0 commit comments

Comments
 (0)