Skip to content

Commit b6298ff

Browse files
jackwildmanclaude
andcommitted
Run integration tests in a single shared session
Add a session-scoped pytest fixture that creates one everyrow session for all integration tests to share, reducing the number of sessions created during test runs. Each test now receives and uses this shared session instead of creating its own. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 4f5e5dd commit b6298ff

File tree

8 files changed

+66
-26
lines changed

8 files changed

+66
-26
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,4 @@ markers = [
7979
"integration: marks tests as integration tests (require EVERYROW_API_KEY)",
8080
]
8181
asyncio_mode = "auto"
82+
asyncio_default_fixture_loop_scope = "session"

tests/integration/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
"""Shared fixtures and configuration for integration tests."""
22

33
import os
4+
from collections.abc import AsyncGenerator
5+
from datetime import datetime
46

57
import pandas as pd
68
import pytest
9+
import pytest_asyncio
710
from pydantic import BaseModel, Field
811

12+
from everyrow.session import Session, create_session
13+
914

1015
@pytest.fixture(scope="session", autouse=True)
1116
def require_api_key():
@@ -14,6 +19,14 @@ def require_api_key():
1419
pytest.fail("EVERYROW_API_KEY environment variable not set")
1520

1621

22+
@pytest_asyncio.fixture(scope="session")
23+
async def session() -> AsyncGenerator[Session, None]:
24+
"""Create a single shared session for all integration tests."""
25+
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
26+
async with create_session(name=f"integration-tests-{timestamp}") as sess:
27+
yield sess
28+
29+
1730
# ============================================================================
1831
# Common Test Data - Small datasets to minimize cost/time
1932
# ============================================================================

tests/integration/test_agent_map.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
pytestmark = [pytest.mark.integration, pytest.mark.asyncio]
1111

1212

13-
async def test_agent_map_returns_table_result():
13+
async def test_agent_map_returns_table_result(session):
1414
"""Test that agent_map returns a TableResult."""
1515
input_df = pd.DataFrame(
1616
[
@@ -22,6 +22,7 @@ async def test_agent_map_returns_table_result():
2222
result = await agent_map(
2323
task="What year was this company founded?",
2424
input=input_df,
25+
session=session,
2526
)
2627

2728
assert isinstance(result, TableResult)
@@ -30,7 +31,7 @@ async def test_agent_map_returns_table_result():
3031
assert "answer" in result.data.columns
3132

3233

33-
async def test_agent_map_with_custom_response_model():
34+
async def test_agent_map_with_custom_response_model(session):
3435
"""Test agent_map with a custom response model."""
3536

3637
class FoundedYear(BaseModel):
@@ -47,6 +48,7 @@ class FoundedYear(BaseModel):
4748
task="When was this company founded?",
4849
input=input_df,
4950
response_model=FoundedYear,
51+
session=session,
5052
)
5153

5254
assert isinstance(result, TableResult)
@@ -60,7 +62,7 @@ class FoundedYear(BaseModel):
6062
assert msft_row["founded_year"].iloc[0] == 1975 # pyright: ignore[reportAttributeAccessIssue]
6163

6264

63-
async def test_agent_map_preserves_input_columns():
65+
async def test_agent_map_preserves_input_columns(session):
6466
"""Test that agent_map joins results with input columns."""
6567
input_df = pd.DataFrame(
6668
[
@@ -72,6 +74,7 @@ async def test_agent_map_preserves_input_columns():
7274
result = await agent_map(
7375
task="What city is the headquarters of this company located in?",
7476
input=input_df,
77+
session=session,
7578
)
7679

7780
assert isinstance(result, TableResult)

tests/integration/test_dedupe.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
pytestmark = [pytest.mark.integration, pytest.mark.asyncio]
1010

1111

12-
async def test_dedupe_returns_table_with_equivalence_fields(papers_df):
12+
async def test_dedupe_returns_table_with_equivalence_fields(papers_df, session):
1313
"""Test that dedupe returns a TableResult with equivalence class fields."""
1414
result = await dedupe(
1515
equivalence_relation="""
@@ -18,6 +18,7 @@ async def test_dedupe_returns_table_with_equivalence_fields(papers_df):
1818
are considered duplicates.
1919
""",
2020
input=papers_df,
21+
session=session,
2122
)
2223

2324
assert isinstance(result, TableResult)
@@ -26,7 +27,7 @@ async def test_dedupe_returns_table_with_equivalence_fields(papers_df):
2627
assert "selected" in result.data.columns
2728

2829

29-
async def test_dedupe_identifies_duplicates(papers_df):
30+
async def test_dedupe_identifies_duplicates(papers_df, session):
3031
"""Test that dedupe correctly identifies duplicate papers."""
3132
result = await dedupe(
3233
equivalence_relation="""
@@ -35,6 +36,7 @@ async def test_dedupe_identifies_duplicates(papers_df):
3536
"Attention Is All You Need" appears twice - once as NeurIPS and once as arXiv.
3637
""",
3738
input=papers_df,
39+
session=session,
3840
)
3941

4042
assert isinstance(result, TableResult)
@@ -53,7 +55,7 @@ async def test_dedupe_identifies_duplicates(papers_df):
5355
assert attention_class != bert_class
5456

5557

56-
async def test_dedupe_selects_one_per_class():
58+
async def test_dedupe_selects_one_per_class(session):
5759
"""Test that dedupe marks exactly one entry as selected per equivalence class."""
5860
input_df = pd.DataFrame(
5961
[
@@ -69,6 +71,7 @@ async def test_dedupe_selects_one_per_class():
6971
"Paper A - Preprint" and "Paper A - Published" are the same paper.
7072
""",
7173
input=input_df,
74+
session=session,
7275
)
7376

7477
assert isinstance(result, TableResult)
@@ -82,7 +85,7 @@ async def test_dedupe_selects_one_per_class():
8285
)
8386

8487

85-
async def test_dedupe_unique_items_all_selected():
88+
async def test_dedupe_unique_items_all_selected(session):
8689
"""Test that unique (non-duplicate) items each get their own class and are selected."""
8790
input_df = pd.DataFrame(
8891
[
@@ -95,6 +98,7 @@ async def test_dedupe_unique_items_all_selected():
9598
result = await dedupe(
9699
equivalence_relation="Items are duplicates only if they are the exact same fruit name.",
97100
input=input_df,
101+
session=session,
98102
)
99103

100104
assert isinstance(result, TableResult)
@@ -104,7 +108,7 @@ async def test_dedupe_unique_items_all_selected():
104108
assert result.data["selected"].all() # pyright: ignore[reportGeneralTypeIssues]
105109

106110

107-
async def test_dedupe_identify_strategy_no_selection():
111+
async def test_dedupe_identify_strategy_no_selection(session):
108112
"""Test that identify strategy clusters but does not add a 'selected' column."""
109113
input_df = pd.DataFrame(
110114
[
@@ -121,6 +125,7 @@ async def test_dedupe_identify_strategy_no_selection():
121125
""",
122126
input=input_df,
123127
strategy="identify",
128+
session=session,
124129
)
125130

126131
assert isinstance(result, TableResult)
@@ -129,7 +134,7 @@ async def test_dedupe_identify_strategy_no_selection():
129134
assert "selected" not in result.data.columns
130135

131136

132-
async def test_dedupe_combine_strategy_creates_combined_rows():
137+
async def test_dedupe_combine_strategy_creates_combined_rows(session):
133138
"""Test that combine strategy produces combined rows marked as selected."""
134139
input_df = pd.DataFrame(
135140
[
@@ -146,6 +151,7 @@ async def test_dedupe_combine_strategy_creates_combined_rows():
146151
""",
147152
input=input_df,
148153
strategy="combine",
154+
session=session,
149155
)
150156

151157
assert isinstance(result, TableResult)
@@ -157,7 +163,7 @@ async def test_dedupe_combine_strategy_creates_combined_rows():
157163
assert len(selected_rows) >= 1
158164

159165

160-
async def test_dedupe_select_strategy_explicit():
166+
async def test_dedupe_select_strategy_explicit(session):
161167
"""Test that explicitly passing strategy='select' works the same as the default."""
162168
input_df = pd.DataFrame(
163169
[
@@ -170,6 +176,7 @@ async def test_dedupe_select_strategy_explicit():
170176
equivalence_relation="Items are duplicates only if they are the exact same fruit name.",
171177
input=input_df,
172178
strategy="select",
179+
session=session,
173180
)
174181

175182
assert isinstance(result, TableResult)
@@ -179,7 +186,7 @@ async def test_dedupe_select_strategy_explicit():
179186
assert result.data["selected"].all() # pyright: ignore[reportGeneralTypeIssues]
180187

181188

182-
async def test_dedupe_with_strategy_prompt():
189+
async def test_dedupe_with_strategy_prompt(session):
183190
"""Test that strategy_prompt parameter is accepted."""
184191
input_df = pd.DataFrame(
185192
[
@@ -197,6 +204,7 @@ async def test_dedupe_with_strategy_prompt():
197204
input=input_df,
198205
strategy="select",
199206
strategy_prompt="Always prefer the published version over the preprint.",
207+
session=session,
200208
)
201209

202210
assert isinstance(result, TableResult)

tests/integration/test_merge.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
pytestmark = [pytest.mark.integration, pytest.mark.asyncio]
1010

1111

12-
async def test_merge_returns_joined_table(trials_df, pharma_df):
12+
async def test_merge_returns_joined_table(trials_df, pharma_df, session):
1313
"""Test that merge returns a MergeResult with joined data and breakdown."""
1414
result = await merge(
1515
task="""
@@ -21,6 +21,7 @@ async def test_merge_returns_joined_table(trials_df, pharma_df):
2121
right_table=pharma_df,
2222
merge_on_left="sponsor",
2323
merge_on_right="company",
24+
session=session,
2425
)
2526

2627
assert isinstance(result, MergeResult)
@@ -33,7 +34,7 @@ async def test_merge_returns_joined_table(trials_df, pharma_df):
3334
assert result.breakdown is not None
3435

3536

36-
async def test_merge_subsidiary_to_parent():
37+
async def test_merge_subsidiary_to_parent(session):
3738
"""Test merge matching subsidiaries to parent companies."""
3839
subsidiaries = pd.DataFrame(
3940
[
@@ -61,6 +62,7 @@ async def test_merge_subsidiary_to_parent():
6162
right_table=parents,
6263
merge_on_left="subsidiary",
6364
merge_on_right="parent_company",
65+
session=session,
6466
)
6567

6668
assert isinstance(result, MergeResult)
@@ -76,7 +78,7 @@ async def test_merge_subsidiary_to_parent():
7678
assert "Microsoft" in linkedin_row["parent_company"].iloc[0] # pyright: ignore[reportAttributeAccessIssue]
7779

7880

79-
async def test_merge_fuzzy_matches_abbreviations():
81+
async def test_merge_fuzzy_matches_abbreviations(session):
8082
"""Test that merge correctly matches abbreviated names."""
8183
employees = pd.DataFrame(
8284
[
@@ -102,6 +104,7 @@ async def test_merge_fuzzy_matches_abbreviations():
102104
right_table=departments,
103105
merge_on_left="dept",
104106
merge_on_right="department",
107+
session=session,
105108
)
106109

107110
assert isinstance(result, MergeResult)
@@ -110,7 +113,7 @@ async def test_merge_fuzzy_matches_abbreviations():
110113
assert "budget" in result.data.columns
111114

112115

113-
async def test_merge_breakdown_structure():
116+
async def test_merge_breakdown_structure(session):
114117
"""Test that merge returns a proper breakdown structure."""
115118
# Create small tables for a simple merge
116119
left = pd.DataFrame([{"id": "A", "value": 1}, {"id": "B", "value": 2}])
@@ -122,6 +125,7 @@ async def test_merge_breakdown_structure():
122125
right_table=right,
123126
merge_on_left="id",
124127
merge_on_right="id",
128+
session=session,
125129
)
126130

127131
assert isinstance(result, MergeResult)

tests/integration/test_rank.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
pytestmark = [pytest.mark.integration, pytest.mark.asyncio]
1111

1212

13-
async def test_rank_returns_sorted_table_ascending():
13+
async def test_rank_returns_sorted_table_ascending(session):
1414
"""Test that rank returns a TableResult sorted ascending."""
1515
input_df = pd.DataFrame(
1616
[
@@ -26,6 +26,7 @@ async def test_rank_returns_sorted_table_ascending():
2626
field_name="population",
2727
field_type="int",
2828
ascending_order=True,
29+
session=session,
2930
)
3031

3132
assert isinstance(result, TableResult)
@@ -38,7 +39,7 @@ async def test_rank_returns_sorted_table_ascending():
3839
assert result.data.iloc[0]["country"] == "Vatican City"
3940

4041

41-
async def test_rank_descending_order():
42+
async def test_rank_descending_order(session):
4243
"""Test rank with descending order."""
4344
input_df = pd.DataFrame(
4445
[
@@ -54,6 +55,7 @@ async def test_rank_descending_order():
5455
field_name="population",
5556
field_type="int",
5657
ascending_order=False,
58+
session=session,
5759
)
5860

5961
assert isinstance(result, TableResult)
@@ -64,7 +66,7 @@ async def test_rank_descending_order():
6466
assert result.data.iloc[0]["country"] == "India"
6567

6668

67-
async def test_rank_with_custom_response_model():
69+
async def test_rank_with_custom_response_model(session):
6870
"""Test rank with a custom response model."""
6971

7072
class CountryMetrics(BaseModel):
@@ -85,6 +87,7 @@ class CountryMetrics(BaseModel):
8587
field_name="population_millions",
8688
response_model=CountryMetrics,
8789
ascending_order=False,
90+
session=session,
8891
)
8992

9093
assert isinstance(result, TableResult)
@@ -94,7 +97,7 @@ class CountryMetrics(BaseModel):
9497
assert result.data.iloc[0]["country"] == "Brazil"
9598

9699

97-
async def test_rank_validates_field_in_response_model():
100+
async def test_rank_validates_field_in_response_model(session):
98101
"""Test that rank validates field_name exists in response_model."""
99102

100103
class WrongModel(BaseModel):
@@ -108,4 +111,5 @@ class WrongModel(BaseModel):
108111
input=input_df,
109112
field_name="population",
110113
response_model=WrongModel,
114+
session=session,
111115
)

0 commit comments

Comments
 (0)