recce-nightly 1.10.0.20250625__py3-none-any.whl → 1.30.0.20251221__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of recce-nightly might be problematic. Click here for more details.

Files changed (229) hide show
  1. recce/VERSION +1 -1
  2. recce/__init__.py +5 -0
  3. recce/adapter/dbt_adapter/__init__.py +343 -245
  4. recce/apis/check_api.py +20 -14
  5. recce/apis/check_events_api.py +353 -0
  6. recce/apis/check_func.py +5 -5
  7. recce/apis/run_func.py +32 -3
  8. recce/artifact.py +76 -3
  9. recce/cli.py +705 -82
  10. recce/config.py +2 -2
  11. recce/connect_to_cloud.py +1 -1
  12. recce/core.py +3 -3
  13. recce/data/404/index.html +2 -0
  14. recce/data/404.html +2 -22
  15. recce/data/__next.@lineage.!KHNsb3Qp.__PAGE__.txt +7 -0
  16. recce/data/__next.@lineage.!KHNsb3Qp.txt +4 -0
  17. recce/data/__next.__PAGE__.txt +6 -0
  18. recce/data/__next._full.txt +32 -0
  19. recce/data/__next._head.txt +8 -0
  20. recce/data/__next._index.txt +14 -0
  21. recce/data/__next._tree.txt +8 -0
  22. recce/data/_next/static/chunks/025a7e3e3f9f40ae.js +1 -0
  23. recce/data/_next/static/chunks/0ce56d67ef5779ca.js +4 -0
  24. recce/data/_next/static/chunks/1a6a78780155dac7.js +48 -0
  25. recce/data/_next/static/chunks/1de8485918b9182a.css +2 -0
  26. recce/data/_next/static/chunks/1e4b1b50d1e34993.js +1 -0
  27. recce/data/_next/static/chunks/206d5d181e4c738e.js +1 -0
  28. recce/data/_next/static/chunks/2c357efc34c5b859.js +25 -0
  29. recce/data/_next/static/chunks/2e9d95d2d48c479c.js +1 -0
  30. recce/data/_next/static/chunks/2f016dc4a3edad2e.js +2 -0
  31. recce/data/_next/static/chunks/313251962d698f7c.js +1 -0
  32. recce/data/_next/static/chunks/3a9f021f38eb5574.css +1 -0
  33. recce/data/_next/static/chunks/40079da8d2b8f651.js +1 -0
  34. recce/data/_next/static/chunks/4599182bffb64661.js +38 -0
  35. recce/data/_next/static/chunks/4e62f6e184173580.js +1 -0
  36. recce/data/_next/static/chunks/5c4dfb0d09eaa401.js +1 -0
  37. recce/data/_next/static/chunks/69e4f06ccfdfc3ac.js +1 -0
  38. recce/data/_next/static/chunks/6b206cb4707d6bee.js +1 -0
  39. recce/data/_next/static/chunks/6d8557f062aa4386.css +1 -0
  40. recce/data/_next/static/chunks/7fbe3650bd83b6b5.js +1 -0
  41. recce/data/_next/static/chunks/83fa823a825674f6.js +1 -0
  42. recce/data/_next/static/chunks/848a6c9b5f55f7ed.js +1 -0
  43. recce/data/_next/static/chunks/859462b0858aef88.css +2 -0
  44. recce/data/_next/static/chunks/923964f18c87d0f1.css +1 -0
  45. recce/data/_next/static/chunks/939390f911895d7c.js +48 -0
  46. recce/data/_next/static/chunks/99a9817237a07f43.js +1 -0
  47. recce/data/_next/static/chunks/9fed8b4b2b924054.js +5 -0
  48. recce/data/_next/static/chunks/b6949f6c5892110c.js +1 -0
  49. recce/data/_next/static/chunks/b851a1d3f8149828.js +1 -0
  50. recce/data/_next/static/chunks/c734f9ad957de0b4.js +1 -0
  51. recce/data/_next/static/chunks/cdde321b0ec75717.js +2 -0
  52. recce/data/_next/static/chunks/d0f91117d77ff844.css +1 -0
  53. recce/data/_next/static/chunks/d6c8667911c2500f.js +1 -0
  54. recce/data/_next/static/chunks/da8dab68c02752cf.js +74 -0
  55. recce/data/_next/static/chunks/dc074049c9d12d97.js +109 -0
  56. recce/data/_next/static/chunks/ee7f1a8227342421.js +1 -0
  57. recce/data/_next/static/chunks/fa2f4e56c2fccc73.js +1 -0
  58. recce/data/_next/static/chunks/turbopack-1fad664f62979b93.js +3 -0
  59. recce/data/_next/static/media/favicon.a8d38d84.ico +0 -0
  60. recce/data/_next/static/media/montserrat-cyrillic-800-normal.d80d830d.woff2 +0 -0
  61. recce/data/_next/static/media/{montserrat-cyrillic-800-normal.bd5c9f50.woff → montserrat-cyrillic-800-normal.f9d58125.woff} +0 -0
  62. recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.076c2a93.woff2 +0 -0
  63. recce/data/_next/static/media/montserrat-latin-800-normal.cde454cc.woff2 +0 -0
  64. recce/data/_next/static/media/{montserrat-latin-800-normal.fc315020.woff → montserrat-latin-800-normal.d5761935.woff} +0 -0
  65. recce/data/_next/static/media/montserrat-latin-ext-800-normal.40ec0659.woff2 +0 -0
  66. recce/data/_next/static/media/{montserrat-latin-ext-800-normal.2e5381b2.woff → montserrat-latin-ext-800-normal.b671449b.woff} +0 -0
  67. recce/data/_next/static/media/{montserrat-vietnamese-800-normal.20c545e6.woff → montserrat-vietnamese-800-normal.9f7b8541.woff} +0 -0
  68. recce/data/_next/static/media/montserrat-vietnamese-800-normal.f9eb854e.woff2 +0 -0
  69. recce/data/_next/static/nX-Uz0AH6Tc6hIQUFGqaB/_buildManifest.js +11 -0
  70. recce/data/_next/static/nX-Uz0AH6Tc6hIQUFGqaB/_clientMiddlewareManifest.json +1 -0
  71. recce/data/_not-found/__next._full.txt +24 -0
  72. recce/data/_not-found/__next._head.txt +8 -0
  73. recce/data/_not-found/__next._index.txt +13 -0
  74. recce/data/_not-found/__next._not-found.__PAGE__.txt +5 -0
  75. recce/data/_not-found/__next._not-found.txt +4 -0
  76. recce/data/_not-found/__next._tree.txt +6 -0
  77. recce/data/_not-found/index.html +2 -0
  78. recce/data/_not-found/index.txt +24 -0
  79. recce/data/auth_callback.html +1 -1
  80. recce/data/checks/__next.@lineage.__DEFAULT__.txt +7 -0
  81. recce/data/checks/__next._full.txt +39 -0
  82. recce/data/checks/__next._head.txt +8 -0
  83. recce/data/checks/__next._index.txt +14 -0
  84. recce/data/checks/__next._tree.txt +8 -0
  85. recce/data/checks/__next.checks.__PAGE__.txt +10 -0
  86. recce/data/checks/__next.checks.txt +4 -0
  87. recce/data/checks/index.html +2 -0
  88. recce/data/checks/index.txt +39 -0
  89. recce/data/index.html +2 -27
  90. recce/data/index.txt +32 -8
  91. recce/data/lineage/__next.@lineage.__DEFAULT__.txt +7 -0
  92. recce/data/lineage/__next._full.txt +39 -0
  93. recce/data/lineage/__next._head.txt +8 -0
  94. recce/data/lineage/__next._index.txt +14 -0
  95. recce/data/lineage/__next._tree.txt +8 -0
  96. recce/data/lineage/__next.lineage.__PAGE__.txt +10 -0
  97. recce/data/lineage/__next.lineage.txt +4 -0
  98. recce/data/lineage/index.html +2 -0
  99. recce/data/lineage/index.txt +39 -0
  100. recce/data/query/__next.@lineage.__DEFAULT__.txt +7 -0
  101. recce/data/query/__next._full.txt +37 -0
  102. recce/data/query/__next._head.txt +8 -0
  103. recce/data/query/__next._index.txt +14 -0
  104. recce/data/query/__next._tree.txt +8 -0
  105. recce/data/query/__next.query.__PAGE__.txt +9 -0
  106. recce/data/query/__next.query.txt +4 -0
  107. recce/data/query/index.html +2 -0
  108. recce/data/query/index.txt +37 -0
  109. recce/event/CONFIG.bak +1 -0
  110. recce/event/__init__.py +9 -8
  111. recce/event/collector.py +6 -2
  112. recce/event/track.py +10 -0
  113. recce/github.py +1 -1
  114. recce/mcp_server.py +725 -0
  115. recce/models/check.py +433 -15
  116. recce/models/types.py +61 -2
  117. recce/pull_request.py +1 -1
  118. recce/run.py +37 -17
  119. recce/server.py +216 -21
  120. recce/state/__init__.py +31 -0
  121. recce/state/cloud.py +644 -0
  122. recce/state/const.py +26 -0
  123. recce/state/local.py +56 -0
  124. recce/state/state.py +119 -0
  125. recce/state/state_loader.py +174 -0
  126. recce/summary.py +25 -3
  127. recce/tasks/dataframe.py +63 -1
  128. recce/tasks/query.py +40 -3
  129. recce/tasks/rowcount.py +4 -1
  130. recce/tasks/schema.py +4 -1
  131. recce/tasks/utils.py +147 -0
  132. recce/tasks/valuediff.py +85 -57
  133. recce/util/api_token.py +11 -2
  134. recce/util/breaking.py +10 -1
  135. recce/util/cll.py +1 -2
  136. recce/util/cloud/__init__.py +15 -0
  137. recce/util/cloud/base.py +115 -0
  138. recce/util/cloud/check_events.py +190 -0
  139. recce/util/cloud/checks.py +242 -0
  140. recce/util/io.py +2 -2
  141. recce/util/lineage.py +19 -18
  142. recce/util/perf_tracking.py +85 -0
  143. recce/util/recce_cloud.py +254 -5
  144. recce/util/startup_perf.py +121 -0
  145. recce/yaml/__init__.py +2 -2
  146. {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.30.0.20251221.dist-info}/METADATA +91 -71
  147. recce_nightly-1.30.0.20251221.dist-info/RECORD +183 -0
  148. {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.30.0.20251221.dist-info}/WHEEL +1 -2
  149. recce/data/_next/static/abCX3x3UoIdRLEDWxx4xd/_buildManifest.js +0 -1
  150. recce/data/_next/static/chunks/181-acc61ddada3bc0ca.js +0 -43
  151. recce/data/_next/static/chunks/1bff33f1-1ef85cf5e658a751.js +0 -1
  152. recce/data/_next/static/chunks/217-879a84d70f7a907c.js +0 -2
  153. recce/data/_next/static/chunks/29e3cc0d-60045b2e47aa3916.js +0 -1
  154. recce/data/_next/static/chunks/36e1c10d-8e7be4a6c1f6ab2d.js +0 -1
  155. recce/data/_next/static/chunks/3998a672-03adacad07b346ac.js +0 -1
  156. recce/data/_next/static/chunks/3a92ee20-1081c360214f9602.js +0 -1
  157. recce/data/_next/static/chunks/42-cd3c06533f5fd47c.js +0 -9
  158. recce/data/_next/static/chunks/450c323b-fd94e7ffaa4a5efa.js +0 -1
  159. recce/data/_next/static/chunks/47d8844f-929aed9b1c73a905.js +0 -1
  160. recce/data/_next/static/chunks/608-3b079b544e5d5f5e.js +0 -15
  161. recce/data/_next/static/chunks/6dc81886-adbfa45836061d79.js +0 -1
  162. recce/data/_next/static/chunks/7a8a3e83-edf6dc64b5d5f0a5.js +0 -1
  163. recce/data/_next/static/chunks/7f27ae6c-d5f0438edd5c2a5b.js +0 -1
  164. recce/data/_next/static/chunks/86730205-cfb14e3f051bab35.js +0 -1
  165. recce/data/_next/static/chunks/8d700b6a.8bb140898499c512.js +0 -1
  166. recce/data/_next/static/chunks/92-607cd1af83c41f43.js +0 -1
  167. recce/data/_next/static/chunks/9746af58-a42b7d169cacadf0.js +0 -1
  168. recce/data/_next/static/chunks/a30376cd-de84559016d7e133.js +0 -1
  169. recce/data/_next/static/chunks/app/_not-found/page-01ed58b7f971d311.js +0 -1
  170. recce/data/_next/static/chunks/app/layout-177a410a97e0d018.js +0 -1
  171. recce/data/_next/static/chunks/app/page-da6e046a8235dbfc.js +0 -1
  172. recce/data/_next/static/chunks/b63b1b3f-4282bdcf459e075c.js +0 -1
  173. recce/data/_next/static/chunks/bbda5537-9ec25eb1dd62348a.js +0 -1
  174. recce/data/_next/static/chunks/c132bf7d-08cb668a789d6afd.js +0 -1
  175. recce/data/_next/static/chunks/ce84277d-2e5d1d46910cf052.js +0 -1
  176. recce/data/_next/static/chunks/febdd86e-c6b525341634b860.js +0 -54
  177. recce/data/_next/static/chunks/fee69bc6-2dbccaf9b90474e6.js +0 -1
  178. recce/data/_next/static/chunks/framework-ded83d71b51ce901.js +0 -1
  179. recce/data/_next/static/chunks/main-app-39061b0166c47f55.js +0 -1
  180. recce/data/_next/static/chunks/main-b5b3ae20a1405261.js +0 -1
  181. recce/data/_next/static/chunks/pages/_app-437c455677d62394.js +0 -1
  182. recce/data/_next/static/chunks/pages/_error-e7650df18ca04bde.js +0 -1
  183. recce/data/_next/static/chunks/webpack-7b49d5ba7e3a434d.js +0 -1
  184. recce/data/_next/static/css/17a96168e3a9db13.css +0 -1
  185. recce/data/_next/static/css/1b121dc4d36aeb4d.css +0 -3
  186. recce/data/_next/static/css/35c6679a098e1e34.css +0 -1
  187. recce/data/_next/static/css/951e2e0eea2d4a5b.css +0 -14
  188. recce/data/_next/static/media/montserrat-cyrillic-800-normal.22628180.woff2 +0 -0
  189. recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.94a63aea.woff2 +0 -0
  190. recce/data/_next/static/media/montserrat-latin-800-normal.6f8fa298.woff2 +0 -0
  191. recce/data/_next/static/media/montserrat-latin-ext-800-normal.013b84f9.woff2 +0 -0
  192. recce/data/_next/static/media/montserrat-vietnamese-800-normal.c0035377.woff2 +0 -0
  193. recce/data/_next/static/media/reload-image.79aabb7d.svg +0 -4
  194. recce/state.py +0 -786
  195. recce_nightly-1.10.0.20250625.dist-info/RECORD +0 -154
  196. recce_nightly-1.10.0.20250625.dist-info/top_level.txt +0 -2
  197. tests/__init__.py +0 -0
  198. tests/adapter/__init__.py +0 -0
  199. tests/adapter/dbt_adapter/__init__.py +0 -0
  200. tests/adapter/dbt_adapter/conftest.py +0 -17
  201. tests/adapter/dbt_adapter/dbt_test_helper.py +0 -298
  202. tests/adapter/dbt_adapter/test_dbt_adapter.py +0 -25
  203. tests/adapter/dbt_adapter/test_dbt_cll.py +0 -384
  204. tests/adapter/dbt_adapter/test_selector.py +0 -202
  205. tests/tasks/__init__.py +0 -0
  206. tests/tasks/conftest.py +0 -4
  207. tests/tasks/test_histogram.py +0 -129
  208. tests/tasks/test_lineage.py +0 -55
  209. tests/tasks/test_preset_checks.py +0 -64
  210. tests/tasks/test_profile.py +0 -397
  211. tests/tasks/test_query.py +0 -151
  212. tests/tasks/test_row_count.py +0 -135
  213. tests/tasks/test_schema.py +0 -122
  214. tests/tasks/test_top_k.py +0 -77
  215. tests/tasks/test_valuediff.py +0 -85
  216. tests/test_cli.py +0 -133
  217. tests/test_config.py +0 -43
  218. tests/test_connect_to_cloud.py +0 -82
  219. tests/test_core.py +0 -29
  220. tests/test_dbt.py +0 -36
  221. tests/test_pull_request.py +0 -130
  222. tests/test_server.py +0 -104
  223. tests/test_state.py +0 -134
  224. tests/test_summary.py +0 -65
  225. /recce/data/_next/static/chunks/{polyfills-42372ed130431b0a.js → a6dad97d9634a72d.js} +0 -0
  226. /recce/data/_next/static/media/{montserrat-cyrillic-ext-800-normal.e6e0d8d0.woff → montserrat-cyrillic-ext-800-normal.a4fa76b5.woff} +0 -0
  227. /recce/data/_next/static/{abCX3x3UoIdRLEDWxx4xd → nX-Uz0AH6Tc6hIQUFGqaB}/_ssgManifest.js +0 -0
  228. {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.30.0.20251221.dist-info}/entry_points.txt +0 -0
  229. {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.30.0.20251221.dist-info}/licenses/LICENSE +0 -0
tests/tasks/test_query.py DELETED
@@ -1,151 +0,0 @@
1
- import pytest
2
-
3
- from recce.tasks import QueryDiffTask, QueryTask
4
-
5
-
6
- def test_query_diff_in_client(dbt_test_helper):
7
- csv_data_curr = """
8
- customer_id,name,age
9
- 1,Alice,30
10
- 2,Bob,25
11
- 3,Charlie,35
12
- """
13
-
14
- csv_data_base = """
15
- customer_id,name,age
16
- 1,Alice,35
17
- 2,Bob,25
18
- 3,Charlie,35
19
- """
20
-
21
- dbt_test_helper.create_model("customers", csv_data_base, csv_data_curr)
22
- params = dict(sql_template='select * from {{ ref("customers") }}')
23
- task = QueryTask(params)
24
- run_result = task.execute()
25
- assert len(run_result.current.data) == 3
26
-
27
-
28
- def test_query_diff_in_client(dbt_test_helper):
29
- csv_data_curr = """
30
- customer_id,name,age
31
- 1,Alice,30
32
- 2,Bob,25
33
- 3,Charlie,35
34
- """
35
-
36
- csv_data_base = """
37
- customer_id,name,age
38
- 1,Alice,35
39
- 2,Bob,25
40
- 3,Charlie,35
41
- """
42
-
43
- dbt_test_helper.create_model("customers", csv_data_base, csv_data_curr)
44
- params = dict(sql_template='select * from {{ ref("customers") }}')
45
- task = QueryDiffTask(params)
46
- run_result = task.execute()
47
- assert len(run_result.base.data) == 3
48
- assert len(run_result.current.data) == 3
49
-
50
- params = dict(
51
- base_sql_template='select * from {{ ref("customers") }} where customer_id <= 2',
52
- sql_template='select * from {{ ref("customers") }}',
53
- )
54
- task = QueryDiffTask(params)
55
- run_result = task.execute()
56
- assert len(run_result.base.data) == 2
57
- assert len(run_result.current.data) == 3
58
-
59
-
60
- def test_query_diff_in_warehouse(dbt_test_helper):
61
- csv_data_curr = """
62
- customer_id,name,age
63
- 1,Alice,30
64
- 2,Bob,25
65
- 3,Charlie,35
66
- """
67
-
68
- csv_data_base = """
69
- customer_id,name,age
70
- 1,Alice,35
71
- 2,Bob,25
72
- 3,Charlie,35
73
- """
74
-
75
- dbt_test_helper.create_model("customers", csv_data_base, csv_data_curr)
76
- params = dict(sql_template='select * from {{ ref("customers") }}', primary_keys=["customer_id"])
77
- task = QueryDiffTask(params)
78
- run_result = task.execute()
79
- assert len(run_result.diff.data) == 2
80
-
81
- params = dict(
82
- base_sql_template='select * from {{ ref("customers") }} where customer_id == 1',
83
- sql_template='select * from {{ ref("customers") }}',
84
- primary_keys=["customer_id"],
85
- )
86
- task = QueryDiffTask(params)
87
- run_result = task.execute()
88
- assert len(run_result.diff.data) == 4
89
-
90
-
91
- def test_validator():
92
- from recce.tasks.query import QueryCheckValidator, QueryDiffCheckValidator
93
-
94
- def validate(params: dict = {}, view_options: dict = {}):
95
- QueryCheckValidator().validate(
96
- {
97
- "name": "test",
98
- "type": "query",
99
- "params": params,
100
- "view_options": view_options,
101
- }
102
- )
103
-
104
- def validate_diff(params: dict = {}, view_options: dict = {}):
105
- QueryDiffCheckValidator().validate(
106
- {
107
- "name": "test",
108
- "type": "query_diff",
109
- "params": params,
110
- "view_options": view_options,
111
- }
112
- )
113
-
114
- # query
115
- validate({"sql_template": "select * from abc"})
116
-
117
- # diff in client
118
- validate_diff({"sql_template": "select * from abc"})
119
- validate_diff(
120
- {
121
- "sql_template": "select * from abc",
122
- "base_sql_template": "select * from abc",
123
- }
124
- )
125
-
126
- # diff in warehouse
127
- validate_diff(
128
- {
129
- "primary_keys": ["customer_id"],
130
- "sql_template": "select * from abc",
131
- }
132
- )
133
- validate_diff(
134
- {"sql_template": "select * from abc", "base_sql_template": "select * from abc", "primary_keys": ["customer_id"]}
135
- )
136
- validate_diff(
137
- {"sql_template": "select * from abc", "base_sql_template": "select * from abc", "primary_keys": ["customer_id"]}
138
- )
139
-
140
- # invalid
141
- with pytest.raises(ValueError):
142
- validate()
143
-
144
- with pytest.raises(ValueError):
145
- validate_diff()
146
-
147
- with pytest.raises(ValueError):
148
- validate_diff({"sql_template": 123, "primary_keys": "xyz"})
149
-
150
- with pytest.raises(ValueError):
151
- validate_diff({"sql_template": "s", "primary_keys": "xyz"})
@@ -1,135 +0,0 @@
1
- import pytest
2
-
3
- from recce.tasks import RowCountDiffTask
4
-
5
-
6
- def test_row_count(dbt_test_helper):
7
- csv_data_curr = """
8
- customer_id,name,age
9
- 1,Alice,30
10
- 2,Bob,25
11
- 3,Charlie,35
12
- """
13
-
14
- csv_data_base = """
15
- customer_id,name,age
16
- 1,Alice,35
17
- 2,Bob,25
18
- """
19
-
20
- dbt_test_helper.create_model("customers", csv_data_base, csv_data_curr, unique_id="model.customers")
21
- task = RowCountDiffTask(dict(node_names=["customers"]))
22
- run_result = task.execute()
23
- assert run_result["customers"]["base"] == 2
24
- assert run_result["customers"]["curr"] == 3
25
-
26
- task = RowCountDiffTask(dict(node_names=["customers_"]))
27
- run_result = task.execute()
28
- assert run_result["customers_"]["base"] is None
29
- assert run_result["customers_"]["curr"] is None
30
-
31
- task = RowCountDiffTask(dict(node_ids=["model.customers"]))
32
- run_result = task.execute()
33
- assert run_result["customers"]["base"] == 2
34
- assert run_result["customers"]["curr"] == 3
35
-
36
-
37
- def test_row_count_with_selector(dbt_test_helper):
38
- csv_data_1 = """
39
- customer_id,name,age
40
- 1,Alice,30
41
- 2,Bob,25
42
- 3,Charlie,35
43
- """
44
-
45
- csv_data_2 = """
46
- customer_id,name,age
47
- 1,Alice,35
48
- 2,Bob,25
49
- """
50
-
51
- dbt_test_helper.create_model("model_1", csv_data_1, csv_data_2, depends_on=[])
52
- dbt_test_helper.create_model(
53
- "model_2", csv_data_1, csv_data_1, depends_on=["model_1"], package_name="other_package"
54
- )
55
- task = RowCountDiffTask(dict(select="model_1"))
56
- run_result = task.execute()
57
- assert len(run_result) == 1
58
-
59
- task = RowCountDiffTask(dict(select="model_1+"))
60
- run_result = task.execute()
61
- assert len(run_result) == 2
62
-
63
-
64
- def test_validator():
65
- from recce.tasks.rowcount import RowCountDiffCheckValidator
66
-
67
- validator = RowCountDiffCheckValidator()
68
-
69
- def validate(params: dict):
70
- validator.validate(
71
- {
72
- "name": "test",
73
- "type": "row_count_diff",
74
- "params": params,
75
- }
76
- )
77
-
78
- # Select all modesl
79
- validate({})
80
-
81
- # Select by node name
82
- validate(
83
- {
84
- "node_names": ["abc"],
85
- }
86
- )
87
- with pytest.raises(ValueError):
88
- validate(
89
- {
90
- "node_names": "abc",
91
- }
92
- )
93
-
94
- # Select by node id
95
- validate(
96
- {
97
- "node_ids": ["model.abc"],
98
- }
99
- )
100
-
101
- # Select by selector
102
- validate(
103
- {
104
- "select": "customers",
105
- "exclude": "customers",
106
- "packages": ["jaffle_shop"],
107
- "view_mode": "all",
108
- }
109
- )
110
-
111
- # packages should be an array
112
- with pytest.raises(ValueError):
113
- validate(
114
- {
115
- "packages": "jaffle_shop",
116
- }
117
- )
118
-
119
- # view_mode should be 'all' or 'changed_models'
120
- validate(
121
- {
122
- "view_mode": None,
123
- }
124
- )
125
- validate(
126
- {
127
- "view_mode": "all",
128
- }
129
- )
130
- with pytest.raises(ValueError):
131
- validate(
132
- {
133
- "view_mode": "abc",
134
- }
135
- )
@@ -1,122 +0,0 @@
1
- import os
2
- from unittest import TestCase
3
- from unittest.mock import MagicMock
4
-
5
- import pytest
6
-
7
- from recce.adapter.dbt_adapter import DbtAdapter, load_catalog, load_manifest
8
- from recce.core import RecceContext, set_default_context
9
- from recce.run import schema_diff_should_be_approved
10
-
11
-
12
- def test_validator():
13
- from recce.tasks.schema import SchemaDiffCheckValidator
14
-
15
- validator = SchemaDiffCheckValidator()
16
-
17
- def validate(params: dict):
18
- validator.validate(
19
- {
20
- "name": "test",
21
- "type": "schema_diff",
22
- "params": params,
23
- }
24
- )
25
-
26
- # Select all models
27
- validate({})
28
-
29
- # Select by node name
30
- validate(
31
- {
32
- "node_id": "abc",
33
- }
34
- )
35
- validate(
36
- {
37
- "node_id": ["abc"],
38
- }
39
- )
40
-
41
- # Select by selector
42
- validate(
43
- {
44
- "select": "customers",
45
- "exclude": "customers",
46
- "packages": ["jaffle_shop"],
47
- "view_mode": "all",
48
- }
49
- )
50
-
51
- # packages should be an array
52
- with pytest.raises(ValueError):
53
- validate(
54
- {
55
- "packages": "jaffle_shop",
56
- }
57
- )
58
-
59
- # view_mode should be 'all' or 'changed_models'
60
- validate(
61
- {
62
- "view_mode": None,
63
- }
64
- )
65
- validate(
66
- {
67
- "view_mode": "all",
68
- }
69
- )
70
- with pytest.raises(ValueError):
71
- validate(
72
- {
73
- "view_mode": "abc",
74
- }
75
- )
76
-
77
-
78
- test_root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
79
-
80
-
81
- class TestSchemaDiffAutoApprove(TestCase):
82
-
83
- def setUp(self):
84
- self.default_context = MagicMock(spec=RecceContext)
85
- manifest = load_manifest(path=os.path.join(test_root_path, "manifest.json"))
86
- catalog = load_catalog(path=os.path.join(test_root_path, "catalog.json"))
87
- dbt_adapter = DbtAdapter(curr_manifest=manifest, curr_catalog=catalog)
88
- self.default_context.adapter = dbt_adapter
89
-
90
- dbt_adapter.adapter = MagicMock()
91
- dbt_adapter.adapter.type.return_value = None
92
-
93
- dbt_adapter.select_nodes = MagicMock()
94
- # Base and Current will be the same
95
- self.default_context.get_lineage.return_value = dbt_adapter.get_lineage()
96
- set_default_context(self.default_context)
97
-
98
- def test_schema_diff_should_be_approved(self):
99
- # Node_id is string
100
- is_approved = schema_diff_should_be_approved(
101
- {
102
- "node_id": "model.jaffle_shop.customers",
103
- }
104
- )
105
- assert is_approved is True
106
-
107
- # Node_id is list
108
- is_approved = schema_diff_should_be_approved(
109
- {
110
- "node_id": ["model.jaffle_shop.customers"],
111
- }
112
- )
113
- assert is_approved is True
114
-
115
- # Select all models
116
- self.default_context.adapter.select_nodes.return_value = ["model.jaffle_shop.customers"]
117
- is_approved = schema_diff_should_be_approved(
118
- {
119
- "select": "customers",
120
- }
121
- )
122
- assert is_approved is True
tests/tasks/test_top_k.py DELETED
@@ -1,77 +0,0 @@
1
- import pytest
2
-
3
- from recce.tasks import TopKDiffTask
4
- from recce.tasks.top_k import TopKDiffCheckValidator
5
-
6
-
7
- def test_top_k(dbt_test_helper):
8
- csv_data_curr = """
9
- customer_id,name,age
10
- 1,Alice,30
11
- 2,Bob,25
12
- 3,Charlie,35
13
- 4,Bob,35
14
- """
15
-
16
- csv_data_base = """
17
- customer_id,name,age
18
- 1,Alice,35
19
- 2,Bob,25
20
- 3,Charlie,35
21
- 4,,35
22
- """
23
-
24
- dbt_test_helper.create_model("customers", csv_data_base, csv_data_curr)
25
-
26
- params = dict(
27
- model="customers",
28
- column_name="name",
29
- k=50,
30
- )
31
-
32
- task = TopKDiffTask(params)
33
- run_result = task.execute()
34
-
35
- # {
36
- # 'values': ['Bob', 'Alice', 'Charlie'],
37
- # 'counts': [2, 1, 1],
38
- # 'valids': 4,
39
- # 'total': 4
40
- # }
41
- assert run_result["current"]["values"][0] == "Bob"
42
- assert run_result["current"]["counts"][0] == 2
43
- assert run_result["current"]["valids"] == 4
44
- assert run_result["current"]["total"] == 4
45
-
46
- # {
47
- # 'values': ['Bob', 'Alice', 'Charlie'],
48
- # 'counts': [1, 1, 1],
49
- # 'valids': 3,
50
- # 'total': 4
51
- # }
52
- assert run_result["base"]["counts"][0] == 1
53
- assert run_result["base"]["valids"] == 3
54
- assert run_result["base"]["total"] == 4
55
-
56
-
57
- def test_validator():
58
- def validate(params: dict = {}, view_options: dict = {}):
59
- TopKDiffCheckValidator().validate(
60
- {
61
- "name": "test",
62
- "type": "top_k_diff",
63
- "params": params,
64
- "view_options": view_options,
65
- }
66
- )
67
-
68
- validate(
69
- {
70
- "model": "customers",
71
- "column_name": "name",
72
- "k": 50,
73
- }
74
- )
75
-
76
- with pytest.raises(ValueError):
77
- validate({})
@@ -1,85 +0,0 @@
1
- import pytest
2
-
3
- from recce.tasks import ValueDiffDetailTask, ValueDiffTask
4
-
5
-
6
- def test_value_diff(dbt_test_helper):
7
- csv_data_curr = """
8
- customer_id,name,age
9
- 1,Alice,30
10
- 2,Bob,25
11
- 3,Charlie,35
12
- """
13
-
14
- csv_data_base = """
15
- customer_id,name,age
16
- 1,Alice,35
17
- 2,Bob,25
18
- 3,Charlie,35
19
- """
20
-
21
- dbt_test_helper.create_model("customers", csv_data_base, csv_data_curr)
22
- params = dict(model="customers", primary_key=["customer_id"])
23
- task = ValueDiffTask(params)
24
- run_result = task.execute()
25
- assert len(run_result.data.columns) == 3
26
- assert len(run_result.data.data) == 3
27
-
28
- params = dict(model="customers", primary_key=["customer_id"])
29
- task = ValueDiffDetailTask(params)
30
- run_result = task.execute()
31
- assert len(run_result.columns) == 5
32
- assert len(run_result.data) == 2
33
-
34
-
35
- def test_validator():
36
- from recce.tasks.valuediff import ValueDiffCheckValidator
37
-
38
- def validate(params: dict = {}, view_options: dict = {}):
39
- ValueDiffCheckValidator().validate(
40
- {
41
- "name": "test",
42
- "type": "value_diff",
43
- "params": params,
44
- "view_options": view_options,
45
- }
46
- )
47
-
48
- validate(
49
- {
50
- "model": "customers",
51
- "primary_key": "customer_id",
52
- }
53
- )
54
- validate(
55
- {
56
- "model": "customers",
57
- "primary_key": ["customer_id"],
58
- }
59
- )
60
- validate(
61
- {
62
- "model": "customers",
63
- "primary_key": ["customer_id"],
64
- "columns": ["name", "age"],
65
- }
66
- )
67
-
68
- with pytest.raises(ValueError):
69
- validate({})
70
-
71
- with pytest.raises(ValueError):
72
- validate(
73
- {
74
- "model": "customers",
75
- }
76
- )
77
-
78
- with pytest.raises(ValueError):
79
- validate(
80
- {
81
- "model": "customers",
82
- "primary_key": ["customer_id"],
83
- "columns": "name",
84
- }
85
- )
tests/test_cli.py DELETED
@@ -1,133 +0,0 @@
1
- from unittest import TestCase
2
- from unittest.mock import MagicMock, patch
3
-
4
- from click.testing import CliRunner
5
-
6
- from recce.cli import run as cli_command_run
7
- from recce.cli import server as cli_command_server
8
- from recce.core import RecceContext
9
- from recce.state import RecceStateLoader
10
-
11
-
12
- def test_cmd_version():
13
- from recce import __version__
14
- from recce.cli import version
15
-
16
- runner = CliRunner()
17
- result = runner.invoke(version, [])
18
- assert result.exit_code == 0
19
- assert result.output.replace("\n", "") == __version__
20
-
21
-
22
- class TestCommandServer(TestCase):
23
- def setUp(self):
24
- self.runner = CliRunner()
25
- pass
26
-
27
- @patch.object(RecceContext, "verify_required_artifacts")
28
- @patch("recce.cli.uvicorn.run")
29
- def test_cmd_server(self, mock_run, mock_verify_required_artifacts):
30
- from recce.server import app
31
-
32
- mock_verify_required_artifacts.return_value = True, None
33
- self.runner.invoke(cli_command_server, ["--host", "unittest", "--port", 5566, "--single-env"])
34
- mock_run.assert_called_once_with(app, host="unittest", port=5566, lifespan="on")
35
-
36
- @patch("recce.cli.uvicorn.run")
37
- def test_cmd_server_with_cloud_without_password(self, mock_run):
38
- # Should fail if no password is provided
39
- result = self.runner.invoke(cli_command_server, ["--cloud"])
40
- assert result.exit_code == 1
41
-
42
- @patch("recce.cli.uvicorn.run")
43
- def test_cmd_server_with_cloud_without_token(self, mock_run):
44
- # Should fail if no token is provided
45
- result = self.runner.invoke(cli_command_server, ["--cloud", "--password", "unittest"])
46
- assert result.exit_code == 1
47
-
48
- @patch.object(RecceContext, "verify_required_artifacts")
49
- @patch("recce.util.recce_cloud.get_recce_cloud_onboarding_state")
50
- @patch("recce.cli.uvicorn.run")
51
- @patch("recce.cli.RecceStateLoader")
52
- def test_cmd_server_with_cloud(
53
- self, mock_state_loader_class, mock_run, mock_get_recce_cloud_onboarding_state, mock_verify_required_artifacts
54
- ):
55
- mock_state_loader = MagicMock(spec=RecceStateLoader)
56
- mock_state_loader.verify.return_value = True
57
- mock_state_loader.review_mode = True
58
- mock_get_recce_cloud_onboarding_state.return_value = "completed"
59
- mock_verify_required_artifacts.return_value = True, None
60
-
61
- mock_state_loader_class.return_value = mock_state_loader
62
- self.runner.invoke(
63
- cli_command_server, ["--cloud", "--password", "unittest", "--cloud-token", "unittest", "--single-env"]
64
- )
65
- mock_state_loader_class.assert_called_once()
66
- mock_run.assert_called_once()
67
-
68
- @patch.object(RecceContext, "verify_required_artifacts")
69
- @patch("os.path.isdir", side_effect=lambda path: True if path == "existed_folder" else False)
70
- @patch("recce.cli.uvicorn.run")
71
- @patch("recce.server.AppState")
72
- def test_cmd_server_with_single_env(self, mock_app_state, mock_run, mock_isdir, mock_verify_required_artifacts):
73
- mock_verify_required_artifacts.return_value = True, None
74
- self.runner.invoke(
75
- cli_command_server,
76
- ["--single-env", "--target-path", "existed_folder", "--target-base-path", "non_existed_folder"],
77
- )
78
- mock_run.assert_called_once()
79
-
80
- # Onboarding mode should be set to True
81
- app_state_call_args = mock_app_state.call_args
82
- app_state_flag = app_state_call_args.kwargs["flag"]
83
- assert "single_env_onboarding" in app_state_flag
84
- assert app_state_flag["single_env_onboarding"] is True
85
- assert "show_relaunch_hint" in app_state_flag
86
- assert app_state_flag["show_relaunch_hint"] is True
87
-
88
- # The target_base_path should be set to the same as target_path
89
- verify_required_artifacts_args = mock_verify_required_artifacts.call_args
90
- assert (
91
- verify_required_artifacts_args.kwargs["target_path"]
92
- == verify_required_artifacts_args.kwargs["target_base_path"]
93
- )
94
-
95
- @patch.object(RecceContext, "verify_required_artifacts")
96
- @patch("os.path.isdir", side_effect=lambda path: True if path == "existed_folder" else False)
97
- @patch("recce.cli.uvicorn.run")
98
- @patch("recce.server.AppState")
99
- def test_cmd_server_with_single_env_but_review_mode_enabled(
100
- self, mock_app_state, mock_run, mock_isdir, mock_verify_required_artifacts
101
- ):
102
- mock_verify_required_artifacts.return_value = True, None
103
- self.runner.invoke(
104
- cli_command_server,
105
- [
106
- "existed_state_file",
107
- "--review",
108
- "--single-env",
109
- "--target-path",
110
- "existed_folder",
111
- "--target-base-path",
112
- "non_existed_folder",
113
- ],
114
- )
115
- mock_run.assert_called_once()
116
- app_state_call_args = mock_app_state.call_args
117
- app_state_flag = app_state_call_args.kwargs["flag"]
118
- assert "single_env_onboarding" in app_state_flag
119
- assert app_state_flag["single_env_onboarding"] is False
120
-
121
-
122
- class TestCommandRun(TestCase):
123
- def setUp(self):
124
- self.runner = CliRunner()
125
- pass
126
-
127
- @patch.object(RecceContext, "verify_required_artifacts")
128
- @patch("recce.cli.cli_run")
129
- def test_cmd_run(self, mock_cli_run, mock_verify_required_artifacts):
130
- mock_verify_required_artifacts.return_value = True, None
131
-
132
- self.runner.invoke(cli_command_run, [])
133
- mock_cli_run.assert_called_once()