-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconftest.py
More file actions
235 lines (182 loc) · 6.89 KB
/
conftest.py
File metadata and controls
235 lines (182 loc) · 6.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
"""
pytest configuration and fixtures for integration tests.
This module provides fixtures for managing the Altertable service connection,
either using an existing service (via environment variables) or starting one
using testcontainers.
"""
import os
from collections.abc import Generator
from dataclasses import dataclass
import pytest
from testcontainers.core.container import DockerContainer, LogMessageWaitStrategy
from altertable_flightsql import Client
@dataclass(frozen=True)
class SchemaInfo:
"""Information about a test schema."""
catalog: str
schema: str
full_name: str
@dataclass(frozen=True)
class TableInfo:
"""Information about a test table."""
catalog: str
schema: str
table: str
full_name: str
class AltertableContainer(DockerContainer):
"""Testcontainer for Altertable mock service."""
def __init__(
self,
image: str = "ghcr.io/altertable-ai/altertable-mock:latest",
port: int = 15002,
platform: str = "linux/amd64",
):
# Force the mock service to run on linux/amd64 so Apple Silicon hosts use the
# correct architecture (via emulation when needed).
super().__init__(image, platform=platform)
self.port = port
self.with_exposed_ports(port)
self.with_env("ALTERTABLE_MOCK_FLIGHT_PORT", str(port))
self.with_env("ALTERTABLE_MOCK_USERS", "altertable-test:lk_test")
self.waiting_for(LogMessageWaitStrategy("Starting Flight SQL server on"))
def get_connection_params(self) -> dict:
"""Get connection parameters for the container."""
return {
"host": self.get_container_host_ip(),
"port": int(self.get_exposed_port(self.port)),
"username": "altertable-test",
"password": "lk_test",
"tls": False,
"catalog": None,
"schema": None,
}
@pytest.fixture(scope="session")
def altertable_service() -> Generator[dict, None, None]:
"""
Provide Altertable service connection parameters.
This fixture checks for environment variables to determine if an existing
service is available. If not, it starts a testcontainer.
Environment variables:
ALTERTABLE_HOST: Hostname of existing service
ALTERTABLE_PORT: Port of existing service
ALTERTABLE_USERNAME: Username for authentication
ALTERTABLE_PASSWORD: Password for authentication
ALTERTABLE_CATALOG: Catalog for the connection
ALTERTABLE_SCHEMA: Schema for the connection
Yields:
dict: Connection parameters (host, port, username, password, tls)
"""
host = os.getenv("ALTERTABLE_HOST")
port = os.getenv("ALTERTABLE_PORT")
username = os.getenv("ALTERTABLE_USERNAME")
password = os.getenv("ALTERTABLE_PASSWORD")
catalog = os.getenv("ALTERTABLE_CATALOG")
schema = os.getenv("ALTERTABLE_SCHEMA")
tls = os.getenv("ALTERTABLE_TLS", "true").lower() == "true"
if host and port:
# Use existing service
yield {
"host": host,
"port": int(port),
"username": username,
"password": password,
"tls": tls,
"catalog": catalog,
"schema": schema,
}
else:
with AltertableContainer() as container:
print(container.get_logs())
yield container.get_connection_params()
@pytest.fixture
def altertable_client(altertable_service: dict) -> Generator[Client, None, None]:
"""
Provide an Altertable client connected to the test service.
This fixture creates a fresh client for each test and ensures proper cleanup.
Args:
altertable_service: Service connection parameters from fixture
Yields:
Client: Connected Altertable client
"""
with Client(**altertable_service) as client:
yield client
@pytest.fixture
def test_catalog(altertable_client: Client) -> Generator[str, None, None]:
"""
Create a test catalog via ATTACH and clean it up after the test.
Args:
altertable_client: Client fixture
Yields:
str: Catalog name to use in tests
"""
import uuid
catalog_name = f"test_catalog_{uuid.uuid4().hex[:8]}"
# Create catalog by attaching a memory database
altertable_client.execute(f"ATTACH ':memory:' AS {catalog_name}")
yield catalog_name
# Cleanup: detach the catalog
try:
altertable_client.execute(f"DETACH {catalog_name}")
except Exception as e:
# Ignore cleanup errors
print(f"Warning: Failed to detach catalog {catalog_name}: {e}")
@pytest.fixture
def test_schema(altertable_client: Client, test_catalog: str) -> Generator[SchemaInfo, None, None]:
"""
Create a test schema in the test catalog and clean it up after the test.
Args:
altertable_client: Client fixture
test_catalog: Test catalog fixture
Yields:
SchemaInfo: Schema information with catalog, schema, and full_name
"""
import uuid
schema_name = f"test_schema_{uuid.uuid4().hex[:8]}"
fully_qualified_schema = f"{test_catalog}.{schema_name}"
# Create schema in the test catalog
altertable_client.execute(f"CREATE SCHEMA {fully_qualified_schema}")
schema_info = SchemaInfo(
catalog=test_catalog, schema=schema_name, full_name=fully_qualified_schema
)
yield schema_info
# Cleanup: drop the schema
try:
altertable_client.execute(f"DROP SCHEMA IF EXISTS {fully_qualified_schema} CASCADE")
except Exception as e:
# Ignore cleanup errors
print(f"Warning: Failed to drop schema {fully_qualified_schema}: {e}")
@pytest.fixture
def test_table(
altertable_client: Client, test_schema: SchemaInfo
) -> Generator[TableInfo, None, None]:
"""
Create a test table in the test schema and clean it up after the test.
The table has columns: id (INT), name (VARCHAR), value (INT)
Args:
altertable_client: Client fixture
test_schema: Test schema fixture
Yields:
TableInfo: Table information with catalog, schema, table, and full_name
"""
import uuid
table_name = f"test_table_{uuid.uuid4().hex[:8]}"
fully_qualified_table = f"{test_schema.full_name}.{table_name}"
altertable_client.execute(
f"CREATE TABLE {fully_qualified_table} (id INT, name VARCHAR, value INT)"
)
altertable_client.execute(
f"INSERT INTO {fully_qualified_table} (id, name, value) VALUES (1, 'Alice', 100), (2, 'Bob', 200), (3, 'Charlie', 300)"
)
table_info = TableInfo(
catalog=test_schema.catalog,
schema=test_schema.schema,
table=table_name,
full_name=fully_qualified_table,
)
yield table_info
# Cleanup: drop the table
try:
altertable_client.execute(f"DROP TABLE IF EXISTS {fully_qualified_table}")
except Exception as e:
# Ignore cleanup errors
print(f"Warning: Failed to drop table {fully_qualified_table}: {e}")