test_constraints.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. # Copyright Materialize, Inc. and contributors. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License in the LICENSE file at the
  6. # root of this repository, or online at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import pytest
  16. from dbt.tests.adapter.constraints.fixtures import (
  17. model_contract_header_schema_yml,
  18. )
  19. from dbt.tests.adapter.constraints.test_constraints import (
  20. BaseConstraintQuotedColumn,
  21. BaseConstraintsRuntimeDdlEnforcement,
  22. BaseIncrementalConstraintsColumnsEqual,
  23. BaseIncrementalConstraintsRollback,
  24. BaseIncrementalConstraintsRuntimeDdlEnforcement,
  25. BaseIncrementalContractSqlHeader,
  26. BaseModelConstraintsRuntimeEnforcement,
  27. BaseTableConstraintsColumnsEqual,
  28. BaseTableContractSqlHeader,
  29. BaseViewConstraintsColumnsEqual,
  30. )
  31. from dbt.tests.util import run_dbt, run_sql_with_adapter
  32. from fixtures import (
  33. contract_invalid_cluster_schema_yml,
  34. contract_pseudo_types_yml,
  35. nullability_assertions_schema_yml,
  36. test_materialized_view,
  37. test_pseudo_types,
  38. test_view,
  39. )
  40. # Materialize does not support time zones or materializing session variables, so
  41. # override the original fixture.
  42. override_model_contract_sql_header_sql = """
  43. {{
  44. config(
  45. materialized = "table"
  46. )
  47. }}
  48. {% call set_sql_header(config) %}
  49. set session time zone 'UTC';
  50. {%- endcall %}
  51. select 'UTC' as column_name
  52. """
  53. class TestTableConstraintsColumnsEqualMaterialize(BaseTableConstraintsColumnsEqual):
  54. pass
  55. class TestViewConstraintsColumnsEqualMaterialize(BaseViewConstraintsColumnsEqual):
  56. pass
  57. @pytest.mark.skip(reason="dbt-materialize does not support constraints")
  58. class TestConstraintQuotedColumnMaterialize(BaseConstraintQuotedColumn):
  59. pass
  60. @pytest.mark.skip(reason="dbt-materialize does not support incremental models")
  61. class TestIncrementalContractSqlHeaderMaterialize(BaseIncrementalContractSqlHeader):
  62. pass
  63. @pytest.mark.skip(reason="dbt-materialize does not support incremental models")
  64. class TestIncrementalConstraintsColumnsEqualMaterialize(
  65. BaseIncrementalConstraintsColumnsEqual
  66. ):
  67. pass
  68. @pytest.mark.skip(reason="dbt-materialize does not support incremental models")
  69. class TestIncrementalConstraintsRollbackMaterialize(BaseIncrementalConstraintsRollback):
  70. pass
  71. @pytest.mark.skip(reason="dbt-materialize does not support incremental models")
  72. class TestIncrementalConstraintsRuntimeDdlEnforcementMaterialize(
  73. BaseIncrementalConstraintsRuntimeDdlEnforcement
  74. ):
  75. pass
  76. @pytest.mark.skip(reason="dbt-materialize does not support constraints")
  77. class TestConstraintsRuntimeDdlEnforcementMaterialize(
  78. BaseConstraintsRuntimeDdlEnforcement
  79. ):
  80. pass
  81. @pytest.mark.skip(reason="dbt-materialize does not support constraints")
  82. class TestModelConstraintsRuntimeEnforcementMaterialize(
  83. BaseModelConstraintsRuntimeEnforcement
  84. ):
  85. pass
  86. class TestNullabilityAssertions:
  87. @pytest.fixture(scope="class")
  88. def models(self):
  89. return {
  90. "nullability_assertions_schema.yml": nullability_assertions_schema_yml,
  91. "test_nullability_assertions_ddl.sql": test_materialized_view,
  92. }
  93. def test_ddl_enforcement(self, project):
  94. nullability_assertions_model = ".".join(
  95. [
  96. project.adapter.config.credentials.database,
  97. project.adapter.config.credentials.schema,
  98. "test_nullability_assertions_ddl",
  99. ]
  100. )
  101. run_dbt(["run"])
  102. # confirm that the correct constraint DDL is generated
  103. nullability_assertions_ddl = run_sql_with_adapter(
  104. project.adapter,
  105. f"SHOW CREATE MATERIALIZED VIEW {nullability_assertions_model}",
  106. fetch="one",
  107. )
  108. assert (
  109. "ASSERT NOT NULL = a, ASSERT NOT NULL = b" in nullability_assertions_ddl[1]
  110. )
  111. class TestTableContractSqlHeaderMaterialize(BaseTableContractSqlHeader):
  112. @pytest.fixture(scope="class")
  113. def models(self):
  114. return {
  115. "my_model_contract_sql_header.sql": override_model_contract_sql_header_sql,
  116. "constraints_schema.yml": model_contract_header_schema_yml,
  117. }
  118. class TestContractInvalidCluster:
  119. @pytest.fixture(scope="class")
  120. def models(self):
  121. return {
  122. "contract_invalid_cluster.yml": contract_invalid_cluster_schema_yml,
  123. "contract_invalid_cluster.sql": test_view,
  124. }
  125. # In the absence of the pre-installed `quickstart` cluster, Materialize should
  126. # not error if data contracts are enforced.
  127. # See database-issues#7091: https://github.com/MaterializeInc/database-issues/issues/7091
  128. def test_materialize_drop_quickstart(self, project):
  129. project.run_sql("DROP CLUSTER quickstart CASCADE")
  130. run_dbt(["run", "--models", "contract_invalid_cluster"], expect_pass=True)
  131. project.run_sql("CREATE CLUSTER quickstart SIZE = '1'")
  132. class TestContractPseudoTypes:
  133. @pytest.fixture(scope="class")
  134. def models(self):
  135. return {
  136. "contract_pseudo_types.yml": contract_pseudo_types_yml,
  137. "contract_pseudo_types.sql": test_pseudo_types,
  138. }
  139. # Pseudo-types in Materialize cannot be cast using the cast() function, so we
  140. # special-handle their NULL casting for contract validation.
  141. # See database-issues#5211: https://github.com/MaterializeInc/database-issues/issues/5211
  142. def test_pseudo_types(self, project):
  143. run_dbt(["run", "--models", "contract_pseudo_types"], expect_pass=True)