Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check mismatched quotes, test for uppercase #19

Merged
merged 2 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions semantic_model_generator/sqlgen/generate_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ def _return_col_or_expr(
raise ValueError(
f"Column names should not have spaces in them. Passed = {col.name}"
)
if col.name.count('"') % 2 != 0: # Odd number of quotes indicates an issue
raise ValueError(
f"Invalid column name '{col.name}'. Mismatched quotes detected."
)
expr = (
f"{col.expr} as {col.name}"
if col.expr.lower() != col.name.lower()
Expand All @@ -63,8 +67,8 @@ def _return_col_or_expr(
columns = []
for dim_col in table.dimensions:
columns.append(_return_col_or_expr(dim_col))
for time_col in table.measures:
columns.append(_return_col_or_expr(time_col))
for measure_col in table.measures:
columns.append(_return_col_or_expr(measure_col))
for time_dim_col in table.time_dimensions:
columns.append(_return_col_or_expr(time_dim_col))

Expand Down
146 changes: 146 additions & 0 deletions semantic_model_generator/tests/validate_model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,96 @@ def mock_snowflake_connection():
"""


_INVALID_YAML_UPPERCASE_DEFAULT_AGG = """name: my test semantic model
tables:
- name: ALIAS
base_table:
database: AUTOSQL_DATASET_BIRD_V2
schema: ADDRESS
table: ALIAS
dimensions:
- name: ALIAS
synonyms:
- 'an alias for something'
expr: ALIAS
data_type: TEXT
sample_values:
- Holtsville
- Adjuntas
- Boqueron
measures:
- name: ZIP_CODE
synonyms:
- 'another synonym'
expr: ZIP_CODE
data_type: NUMBER
sample_values:
- '501'
default_aggregation: AVG
- name: AREA_CODE
base_table:
database: AUTOSQL_DATASET_BIRD_V2
schema: ADDRESS
table: AREA_CODE
measures:
- name: ZIP_CODE
expr: ZIP_CODE
data_type: NUMBER
sample_values:
- '501'
- '544'
- name: AREA_CODE
expr: AREA_CODE
data_type: NUMBER
sample_values:
- '631'
"""

_INVALID_YAML_UNMATCHED_QUOTE = """name: my test semantic model
tables:
- name: ALIAS
base_table:
database: AUTOSQL_DATASET_BIRD_V2
schema: ADDRESS
table: ALIAS
dimensions:
- name: ALIAS
synonyms:
- 'an alias for something'
expr: ALIAS
data_type: TEXT
sample_values:
- Holtsville
- Adjuntas
- Boqueron
measures:
- name: ZIP_CODE
synonyms:
- 'another synonym'
expr: ZIP_CODE
data_type: NUMBER
sample_values:
- '501'
- name: AREA_CODE
base_table:
database: AUTOSQL_DATASET_BIRD_V2
schema: ADDRESS
table: AREA_CODE
measures:
- name: ZIP_CODE"
expr: ZIP_CODE
data_type: NUMBER
sample_values:
- '501'
- '544'
- name: AREA_CODE
expr: AREA_CODE
data_type: NUMBER
sample_values:
- '631'
"""


@pytest.fixture
def temp_valid_yaml_file():
"""Create a temporary YAML file with the test data."""
Expand All @@ -122,6 +212,24 @@ def temp_invalid_yaml_formatting_file():
yield tmp.name


@pytest.fixture
def temp_invalid_yaml_uppercase_file():
"""Create a temporary YAML file with the test data."""
with tempfile.NamedTemporaryFile(mode="w", delete=True) as tmp:
tmp.write(_INVALID_YAML_UPPERCASE_DEFAULT_AGG)
tmp.flush()
yield tmp.name


@pytest.fixture
def temp_invalid_yaml_unmatched_quote_file():
"""Create a temporary YAML file with the test data."""
with tempfile.NamedTemporaryFile(mode="w", delete=True) as tmp:
tmp.write(_INVALID_YAML_UNMATCHED_QUOTE)
tmp.flush()
yield tmp.name


@mock.patch("semantic_model_generator.validate_model.logger")
def test_valid_yaml(mock_logger, temp_valid_yaml_file, mock_snowflake_connection):
account_name = "snowflake test"
Expand Down Expand Up @@ -176,3 +284,41 @@ def test_invalid_yaml_formatting(mock_logger, temp_invalid_yaml_formatting_file)
assert (
expected_log_call not in mock_logger.mock_calls
), "Unexpected log message found in logger calls"


@mock.patch("semantic_model_generator.validate_model.logger")
def test_invalid_yaml_uppercase(mock_logger, temp_invalid_yaml_uppercase_file):
account_name = "snowflake test"
with pytest.raises(ValueError) as exc_info:
validate(temp_invalid_yaml_uppercase_file, account_name)

expected_error_fragment = "Unable to parse yaml to protobuf. Error: Failed to parse tables field: Failed to parse measures field: Failed to parse default_aggregation field: Invalid enum value AVG for enum type semantic_model_generator.AggregationType at SemanticModel.tables[0].measures[0].default_aggregation..."
assert expected_error_fragment in str(exc_info.value), "Unexpected error message"

expected_log_call = mock.call.info(
f"Successfully validated {temp_invalid_yaml_uppercase_file}"
)
assert (
expected_log_call not in mock_logger.mock_calls
), "Unexpected log message found in logger calls"


@mock.patch("semantic_model_generator.validate_model.logger")
def test_invalid_yaml_missing_quote(
mock_logger, temp_invalid_yaml_unmatched_quote_file, mock_snowflake_connection
):
account_name = "snowflake test"
with pytest.raises(ValueError) as exc_info:
validate(temp_invalid_yaml_unmatched_quote_file, account_name)

expected_error_fragment = "Unable to execute query with your logical table against physical tables on Snowflake. Query = SELECT ALIAS, ZIP_CODE FROM AUTOSQL_DATASET_BIRD_V2.ADDRESS.ALIAS LIMIT 100. Error = Invalid column name 'ZIP_CODE\"'. Mismatched quotes detected."

assert expected_error_fragment in str(exc_info.value), "Unexpected error message"

expected_log_call = mock.call.info(
f"Successfully validated {temp_invalid_yaml_unmatched_quote_file}"
)

assert (
expected_log_call not in mock_logger.mock_calls
), "Unexpected log message found in logger calls"
Loading