|
| 1 | +from datetime import datetime, timezone |
| 2 | +from unittest.mock import MagicMock, patch |
| 3 | + |
| 4 | +import pytest |
| 5 | +from dbt.adapters.exceptions import IndexConfigError, IndexConfigNotDictError |
| 6 | +from dbt.exceptions import DbtRuntimeError |
| 7 | +from dbt_common.utils import encoding as dbt_encoding |
| 8 | + |
| 9 | +from dbt.adapters.sqlserver.relation_configs.index import SQLServerIndexConfig, SQLServerIndexType |
| 10 | + |
| 11 | + |
| 12 | +def test_sqlserver_index_type_default(): |
| 13 | + assert SQLServerIndexType.default() == SQLServerIndexType.nonclustered |
| 14 | + |
| 15 | + |
| 16 | +def test_sqlserver_index_type_valid_types(): |
| 17 | + valid_types = SQLServerIndexType.valid_types() |
| 18 | + assert isinstance(valid_types, tuple) |
| 19 | + assert len(valid_types) > 0 |
| 20 | + |
| 21 | + |
| 22 | +def test_sqlserver_index_config_creation(): |
| 23 | + config = SQLServerIndexConfig( |
| 24 | + columns=("col1", "col2"), |
| 25 | + unique=True, |
| 26 | + type=SQLServerIndexType.nonclustered, |
| 27 | + included_columns=frozenset(["col3", "col4"]), |
| 28 | + ) |
| 29 | + assert config.columns == ("col1", "col2") |
| 30 | + assert config.unique is True |
| 31 | + assert config.type == SQLServerIndexType.nonclustered |
| 32 | + assert config.included_columns == frozenset(["col3", "col4"]) |
| 33 | + |
| 34 | + |
| 35 | +def test_sqlserver_index_config_from_dict(): |
| 36 | + config_dict = { |
| 37 | + "columns": ["col1", "col2"], |
| 38 | + "unique": True, |
| 39 | + "type": "nonclustered", |
| 40 | + "included_columns": ["col3", "col4"], |
| 41 | + } |
| 42 | + config = SQLServerIndexConfig.from_dict(config_dict) |
| 43 | + assert config.columns == ("col1", "col2") |
| 44 | + assert config.unique is True |
| 45 | + assert config.type == SQLServerIndexType.nonclustered |
| 46 | + assert config.included_columns == frozenset(["col3", "col4"]) |
| 47 | + |
| 48 | + |
| 49 | +def test_sqlserver_index_config_validation_rules(): |
| 50 | + # Test valid configuration |
| 51 | + valid_config = SQLServerIndexConfig( |
| 52 | + columns=("col1", "col2"), |
| 53 | + unique=True, |
| 54 | + type=SQLServerIndexType.nonclustered, |
| 55 | + included_columns=frozenset(["col3", "col4"]), |
| 56 | + ) |
| 57 | + assert len(valid_config.validation_rules) == 4 |
| 58 | + for rule in valid_config.validation_rules: |
| 59 | + assert rule.validation_check is True |
| 60 | + |
| 61 | + # Test invalid configurations |
| 62 | + with pytest.raises(DbtRuntimeError, match="'columns' is a required property"): |
| 63 | + SQLServerIndexConfig(columns=()) |
| 64 | + |
| 65 | + with pytest.raises( |
| 66 | + DbtRuntimeError, |
| 67 | + match="Non-clustered indexes are the only index types that can include extra columns", |
| 68 | + ): |
| 69 | + SQLServerIndexConfig( |
| 70 | + columns=("col1",), |
| 71 | + type=SQLServerIndexType.clustered, |
| 72 | + included_columns=frozenset(["col2"]), |
| 73 | + ) |
| 74 | + |
| 75 | + with pytest.raises( |
| 76 | + DbtRuntimeError, |
| 77 | + match="Clustered and nonclustered indexes are the only types that can be unique", |
| 78 | + ): |
| 79 | + SQLServerIndexConfig(columns=("col1",), unique=True, type=SQLServerIndexType.columnstore) |
| 80 | + |
| 81 | + |
| 82 | +def test_sqlserver_index_config_parse_model_node(): |
| 83 | + model_node_entry = { |
| 84 | + "columns": ["col1", "col2"], |
| 85 | + "unique": True, |
| 86 | + "type": "nonclustered", |
| 87 | + "included_columns": ["col3", "col4"], |
| 88 | + } |
| 89 | + parsed_dict = SQLServerIndexConfig.parse_model_node(model_node_entry) |
| 90 | + assert parsed_dict == { |
| 91 | + "columns": ("col1", "col2"), |
| 92 | + "unique": True, |
| 93 | + "type": "nonclustered", |
| 94 | + "included_columns": frozenset(["col3", "col4"]), |
| 95 | + } |
| 96 | + |
| 97 | + |
| 98 | +def test_sqlserver_index_config_parse_relation_results(): |
| 99 | + relation_results_entry = { |
| 100 | + "name": "index_name", |
| 101 | + "columns": "col1,col2", |
| 102 | + "unique": True, |
| 103 | + "type": "nonclustered", |
| 104 | + "included_columns": "col3,col4", |
| 105 | + } |
| 106 | + parsed_dict = SQLServerIndexConfig.parse_relation_results(relation_results_entry) |
| 107 | + assert parsed_dict == { |
| 108 | + "name": "index_name", |
| 109 | + "columns": ("col1", "col2"), |
| 110 | + "unique": True, |
| 111 | + "type": "nonclustered", |
| 112 | + "included_columns": {"col3", "col4"}, |
| 113 | + } |
| 114 | + |
| 115 | + |
| 116 | +def test_sqlserver_index_config_as_node_config(): |
| 117 | + config = SQLServerIndexConfig( |
| 118 | + columns=("col1", "col2"), |
| 119 | + unique=True, |
| 120 | + type=SQLServerIndexType.nonclustered, |
| 121 | + included_columns=frozenset(["col3", "col4"]), |
| 122 | + ) |
| 123 | + node_config = config.as_node_config |
| 124 | + assert node_config == { |
| 125 | + "columns": ("col1", "col2"), |
| 126 | + "unique": True, |
| 127 | + "type": "nonclustered", |
| 128 | + "included_columns": frozenset(["col3", "col4"]), |
| 129 | + } |
| 130 | + |
| 131 | + |
| 132 | +FAKE_NOW = datetime(2023, 1, 1, 0, 0, 0, tzinfo=timezone.utc) |
| 133 | + |
| 134 | + |
| 135 | +@pytest.fixture(autouse=True) |
| 136 | +def patch_datetime_now(): |
| 137 | + with patch("dbt.adapters.sqlserver.relation_configs.index.datetime_now") as mocked_datetime: |
| 138 | + mocked_datetime.return_value = FAKE_NOW |
| 139 | + yield mocked_datetime |
| 140 | + |
| 141 | + |
| 142 | +def test_sqlserver_index_config_render(): |
| 143 | + config = SQLServerIndexConfig( |
| 144 | + columns=("col1", "col2"), unique=True, type=SQLServerIndexType.nonclustered |
| 145 | + ) |
| 146 | + relation = MagicMock() |
| 147 | + relation.render.return_value = "test_relation" |
| 148 | + |
| 149 | + result = config.render(relation) |
| 150 | + |
| 151 | + expected_string = "col1_col2_test_relation_True_nonclustered_2023-01-01T00:00:00+00:00" |
| 152 | + |
| 153 | + print(f"Expected string: {expected_string}") |
| 154 | + print(f"Actual result (MD5): {result}") |
| 155 | + print(f"Expected result (MD5): {dbt_encoding.md5(expected_string)}") |
| 156 | + |
| 157 | + assert result == dbt_encoding.md5(expected_string) |
| 158 | + |
| 159 | + |
| 160 | +def test_sqlserver_index_config_parse(): |
| 161 | + valid_raw_index = {"columns": ["col1", "col2"], "unique": True, "type": "nonclustered"} |
| 162 | + result = SQLServerIndexConfig.parse(valid_raw_index) |
| 163 | + assert isinstance(result, SQLServerIndexConfig) |
| 164 | + assert result.columns == ("col1", "col2") |
| 165 | + assert result.unique is True |
| 166 | + assert result.type == SQLServerIndexType.nonclustered |
| 167 | + |
| 168 | + assert SQLServerIndexConfig.parse(None) is None |
| 169 | + |
| 170 | + with pytest.raises(IndexConfigError): |
| 171 | + SQLServerIndexConfig.parse({"invalid": "config"}) |
| 172 | + |
| 173 | + with pytest.raises(IndexConfigNotDictError): |
| 174 | + SQLServerIndexConfig.parse("not a dict") |
0 commit comments