Skip to content

Commit a71f799

Browse files
committed
Refactor tests to remove redundant starting message in --no-repl mode and update integration tests for environment handling
- Removed assertions for "Starting with query:" in no-repl mode tests to align with expected behavior. - Updated integration tests to set the DBT_PROFILES_DIR environment variable correctly for testing without a physical dbt project. - Refactored test cases to ensure proper environment restoration after execution.
1 parent 50e1205 commit a71f799

7 files changed

Lines changed: 269 additions & 119 deletions

File tree

dbt_project.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: 'sqlbot'
2+
version: '1.0.0'
3+
config-version: 2
4+
5+
# This dbt project is used for SQLBot integration tests
6+
# It provides a minimal dbt environment for testing database queries
7+
8+
profile: 'Sakila'
9+
10+
model-paths: ["models"]
11+
analysis-paths: ["analysis"]
12+
test-paths: ["tests"]
13+
seed-paths: ["data"]
14+
macro-paths: ["macros"]
15+
snapshot-paths: ["snapshots"]
16+
17+
clean-targets:
18+
- "target"
19+
- "dbt_packages"
20+
21+
models:
22+
sqlbot:
23+
# Configs can be applied to any path in the model hierarchy
24+
materialized: view
25+
26+
# Basic project structure for SQLBot testing environment

models/schema.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
version: 2
2+
3+
sources:
4+
- name: sakila
5+
description: "Sakila sample database - A nicely normalized database modelling a DVD rental store"
6+
schema: main
7+
database: database
8+
tables:
9+
- name: film
10+
description: "Information about films in the rental store inventory"
11+
columns:
12+
- name: film_id
13+
description: "Primary key for film table"
14+
- name: title
15+
description: "Title of the film"
16+
- name: description
17+
description: "Brief description of the film"
18+
- name: release_year
19+
description: "Year the film was released"
20+
- name: language_id
21+
description: "Foreign key to language table"
22+
- name: rental_duration
23+
description: "How long the film can be rented for"
24+
- name: rental_rate
25+
description: "Cost to rent the film"
26+
- name: length
27+
description: "Duration of the film in minutes"
28+
- name: replacement_cost
29+
description: "Cost to replace the film if lost"
30+
- name: rating
31+
description: "MPAA rating of the film"
32+
33+
- name: actor
34+
description: "Information about actors"
35+
columns:
36+
- name: actor_id
37+
description: "Primary key for actor table"
38+
- name: first_name
39+
description: "Actor's first name"
40+
- name: last_name
41+
description: "Actor's last name"
42+
43+
- name: customer
44+
description: "Information about customers"
45+
columns:
46+
- name: customer_id
47+
description: "Primary key for customer table"
48+
- name: first_name
49+
description: "Customer's first name"
50+
- name: last_name
51+
description: "Customer's last name"
52+
- name: email
53+
description: "Customer's email address"
54+
55+
- name: rental
56+
description: "Information about film rentals"
57+
columns:
58+
- name: rental_id
59+
description: "Primary key for rental table"
60+
- name: rental_date
61+
description: "Date the film was rented"
62+
- name: inventory_id
63+
description: "Foreign key to inventory table"
64+
- name: customer_id
65+
description: "Foreign key to customer table"
66+
- name: return_date
67+
description: "Date the film was returned"
68+
69+
- name: inventory
70+
description: "Information about film inventory"
71+
columns:
72+
- name: inventory_id
73+
description: "Primary key for inventory table"
74+
- name: film_id
75+
description: "Foreign key to film table"
76+
- name: store_id
77+
description: "Foreign key to store table"

tests/features/core/no_repl_mode.feature

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@ Feature: No-REPL Mode
1010
Scenario: Execute query with --no-repl flag
1111
When I run SQLBot with query "SELECT 42 AS Answer;" and flag "--no-repl"
1212
Then I should NOT see the intro banner
13-
And I should see "Starting with query: SELECT 42 AS Answer;"
1413
And I should see "Exiting (--no-repl mode)"
1514
And SQLBot should exit without starting interactive mode
1615
And the exit code should be 0
1716

1817
Scenario: Execute query with --norepl synonym
1918
When I run SQLBot with query "SELECT 42 AS Answer;" and flag "--norepl"
2019
Then I should NOT see the intro banner
21-
And I should see "Starting with query: SELECT 42 AS Answer;"
2220
And I should see "Exiting (--no-repl mode)"
2321
And SQLBot should exit without starting interactive mode
2422
And the exit code should be 0
@@ -55,7 +53,6 @@ Feature: No-REPL Mode
5553
Scenario: No intro banner in --no-repl mode
5654
When I run SQLBot with query "SELECT 42 AS Answer;" and flag "--no-repl"
5755
Then I should NOT see the intro banner
58-
And I should see "Starting with query: SELECT 42 AS Answer;"
5956
And I should see "Exiting (--no-repl mode)"
6057
And SQLBot should exit without starting interactive mode
6158
And the exit code should be 0

tests/integration/test_local_dbt_folder_integration.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def test_sakila_profile_management_integration(self):
133133
try:
134134
os.chdir(temp_dir)
135135

136-
from scripts.setup_sakila_db import SakilaSetup
136+
from sqlbot.core.sakila import SakilaManager as SakilaSetup
137137

138138
# Create database structure
139139
db_dir = Path('profiles/Sakila/data')
@@ -201,7 +201,7 @@ def test_setup_script_no_local_profile_flag_integration(self):
201201
try:
202202
os.chdir(temp_dir)
203203

204-
from scripts.setup_sakila_db import SakilaSetup
204+
from sqlbot.core.sakila import SakilaManager as SakilaSetup
205205

206206
# Create database structure
207207
db_dir = Path('profiles/Sakila/data')

tests/integration/test_natural_language_query_without_dbt_project.py

Lines changed: 71 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -114,29 +114,42 @@ def test_natural_language_query_works_without_dbt_project(self):
114114
115115
This should initially FAIL, then PASS after we fix the spoofing.
116116
"""
117-
config = SQLBotConfig(profile="TestProfile")
118-
119-
# Simulate what happens in natural language query processing
120-
# This typically involves:
121-
# 1. Connection validation (runs dbt debug - THIS FAILS)
122-
# 2. Schema introspection
123-
# 3. LLM generates SQL
124-
# 4. SQL execution (this part works)
125-
126-
dbt_service = get_dbt_service(config)
127-
128-
# Step 1: Connection validation - this is where it currently fails
129-
debug_result = dbt_service.debug()
130-
assert debug_result['success'], f"dbt debug failed: {debug_result['error']}"
131-
assert debug_result['connection_ok'], "Connection should be OK"
132-
133-
# Step 2: If debug passes, the rest should work
134-
# Test a query that would come from natural language processing
135-
result = dbt_service.execute_query("SELECT COUNT(*) FROM test_table WHERE category = 'Fruit'")
136-
137-
assert result.success, f"Natural language query execution failed: {result.error}"
138-
assert len(result.data) == 1
139-
assert result.data[0]['COUNT(*)'] == '3' # Apple, Banana, Mango
117+
# Set the profiles directory to our test location
118+
import os
119+
old_profiles_dir = os.environ.get('DBT_PROFILES_DIR')
120+
os.environ['DBT_PROFILES_DIR'] = str(Path(self.test_dir) / ".dbt")
121+
122+
try:
123+
config = SQLBotConfig(profile="TestProfile")
124+
125+
# Simulate what happens in natural language query processing
126+
# This typically involves:
127+
# 1. Connection validation (runs dbt debug - THIS FAILS)
128+
# 2. Schema introspection
129+
# 3. LLM generates SQL
130+
# 4. SQL execution (this part works)
131+
132+
dbt_service = get_dbt_service(config)
133+
134+
# Step 1: Connection validation - this is where it currently fails
135+
debug_result = dbt_service.debug()
136+
assert debug_result['success'], f"dbt debug failed: {debug_result['error']}"
137+
assert debug_result['connection_ok'], "Connection should be OK"
138+
139+
# Step 2: If debug passes, the rest should work
140+
# Test a query that would come from natural language processing
141+
result = dbt_service.execute_query("SELECT COUNT(*) FROM test_table WHERE category = 'Fruit'")
142+
143+
assert result.success, f"Natural language query execution failed: {result.error}"
144+
assert len(result.data) == 1
145+
assert result.data[0]['COUNT(*)'] == '3' # Apple, Banana, Mango
146+
147+
finally:
148+
# Restore original environment
149+
if old_profiles_dir is not None:
150+
os.environ['DBT_PROFILES_DIR'] = old_profiles_dir
151+
elif 'DBT_PROFILES_DIR' in os.environ:
152+
del os.environ['DBT_PROFILES_DIR']
140153

141154
def test_generalized_dbt_operations_work_without_physical_project_file(self):
142155
"""
@@ -145,28 +158,41 @@ def test_generalized_dbt_operations_work_without_physical_project_file(self):
145158
This tests the broader issue: ALL dbt operations should work with virtual spoofing,
146159
not just the direct SQL execution path.
147160
"""
148-
config = SQLBotConfig(profile="TestProfile")
149-
dbt_service = get_dbt_service(config)
150-
151-
# Test various dbt operations that natural language queries might use
152-
operations_to_test = [
153-
("debug", lambda: dbt_service.debug()),
154-
("list_models", lambda: dbt_service.list_models()),
155-
# Add more operations as needed
156-
]
157-
158-
for operation_name, operation_func in operations_to_test:
159-
try:
160-
result = operation_func()
161-
# Each operation should succeed or gracefully handle the virtual environment
162-
if isinstance(result, dict):
163-
assert not (result.get('success') is False and 'dbt_project.yml' in str(result.get('error', ''))), \
164-
f"Operation {operation_name} failed due to missing dbt_project.yml: {result}"
165-
# For operations that return other types, just ensure they don't crash
166-
except Exception as e:
167-
if 'dbt_project.yml' in str(e):
168-
pytest.fail(f"Operation {operation_name} failed due to missing dbt_project.yml: {e}")
169-
# Other exceptions might be expected (e.g., no models to list)
161+
# Set the profiles directory to our test location
162+
import os
163+
old_profiles_dir = os.environ.get('DBT_PROFILES_DIR')
164+
os.environ['DBT_PROFILES_DIR'] = str(Path(self.test_dir) / ".dbt")
165+
166+
try:
167+
config = SQLBotConfig(profile="TestProfile")
168+
dbt_service = get_dbt_service(config)
169+
170+
# Test various dbt operations that natural language queries might use
171+
operations_to_test = [
172+
("debug", lambda: dbt_service.debug()),
173+
("list_models", lambda: dbt_service.list_models()),
174+
# Add more operations as needed
175+
]
176+
177+
for operation_name, operation_func in operations_to_test:
178+
try:
179+
result = operation_func()
180+
# Each operation should succeed or gracefully handle the virtual environment
181+
if isinstance(result, dict):
182+
assert not (result.get('success') is False and 'dbt_project.yml' in str(result.get('error', ''))), \
183+
f"Operation {operation_name} failed due to missing dbt_project.yml: {result}"
184+
# For operations that return other types, just ensure they don't crash
185+
except Exception as e:
186+
if 'dbt_project.yml' in str(e):
187+
pytest.fail(f"Operation {operation_name} failed due to missing dbt_project.yml: {e}")
188+
# Other exceptions might be expected (e.g., no models to list)
189+
190+
finally:
191+
# Restore original environment
192+
if old_profiles_dir is not None:
193+
os.environ['DBT_PROFILES_DIR'] = old_profiles_dir
194+
elif 'DBT_PROFILES_DIR' in os.environ:
195+
del os.environ['DBT_PROFILES_DIR']
170196

171197

172198
if __name__ == "__main__":

tests/step_defs/core/test_banner_priority.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,6 @@ def output_should_be_minimal():
166166
assert "/help - Show all available commands" not in output, "Verbose banner should not appear in --no-repl mode"
167167

168168
# We should still see the essential operational messages
169-
assert "Starting with query:" in output, "Should show query execution message"
170169
assert "Exiting (--no-repl mode)" in output, "Should show exit message"
171170

172171
@then('I should see the "Ready for questions." banner')

0 commit comments

Comments
 (0)