recce-nightly 1.16.0.20250813__py3-none-any.whl → 1.16.0.20250817__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 (78) hide show
  1. recce/VERSION +1 -1
  2. recce/cli.py +13 -4
  3. recce/data/404.html +1 -1
  4. recce/data/_next/static/{RX_nnCGyE7ZOeoTStwYCZ → -zBI1h60_HeMe-9ZrCw7s}/_buildManifest.js +1 -1
  5. recce/data/_next/static/chunks/068b80ea-7fab85837b21fed5.js +1 -0
  6. recce/data/_next/static/chunks/{0ddaf06c-c7961285f66460f6.js → 0ddaf06c-05b8d2a8a3e5c6f3.js} +1 -1
  7. recce/data/_next/static/chunks/{12f8fac4-16838e42d28d45c3.js → 12f8fac4-170216f89d5576c6.js} +1 -1
  8. recce/data/_next/static/chunks/{235b8375-8c84c51d7bd4f6aa.js → 235b8375-c2d62e0b44e3ae74.js} +1 -1
  9. recce/data/_next/static/chunks/{2541941f-2cd3a7c2d629bd33.js → 2541941f-d65696bd5d37dfa2.js} +1 -1
  10. recce/data/_next/static/chunks/25c097a4-51d7c9060de49a6c.js +1 -0
  11. recce/data/_next/static/chunks/27e92eb0-4d8ec3c6cdf0d5f1.js +1 -0
  12. recce/data/_next/static/chunks/{2fc37c1e-910deebeb3d77c90.js → 2fc37c1e-fd3c94ef85344810.js} +1 -1
  13. recce/data/_next/static/chunks/370-bd3e0bf7237858bc.js +1 -0
  14. recce/data/_next/static/chunks/{3a92ee20-0400ffe460c7c803.js → 3a92ee20-a33bb5964d0b2f37.js} +1 -1
  15. recce/data/_next/static/chunks/490-ce854af0bc670d3e.js +1 -0
  16. recce/data/_next/static/chunks/575-2443813ff441d02c.js +1 -0
  17. recce/data/_next/static/chunks/{62446465-423c03bb8c1f59b6.js → 62446465-5277e7ad93431c07.js} +1 -1
  18. recce/data/_next/static/chunks/{6af7f9e9-60aa8706f49dae45.js → 6af7f9e9-1b5f068628caacd4.js} +1 -1
  19. recce/data/_next/static/chunks/{6cf54382-49d52ae6e564e2ac.js → 6cf54382-9bc9b4c7b7e4b8d8.js} +1 -1
  20. recce/data/_next/static/chunks/6dc81886-2b329a8d619994c9.js +1 -0
  21. recce/data/_next/static/chunks/{715e4acc-9e2e6df4eb3809d1.js → 715e4acc-be8c974e746c0e77.js} +1 -1
  22. recce/data/_next/static/chunks/76-54eeed3fa75d6146.js +10 -0
  23. recce/data/_next/static/chunks/{8d700b6a.7fe2c8c3f4e333a6.js → 8d700b6a.6c883c62894c0d11.js} +1 -1
  24. recce/data/_next/static/chunks/975-341ea105d3d5e6ca.js +30 -0
  25. recce/data/_next/static/chunks/{ae307f12-01100009689ace61.js → ae307f12-0f5382a8bd52593f.js} +1 -1
  26. recce/data/_next/static/chunks/app/_not-found/page-f6afb3793e4b6ce1.js +1 -0
  27. recce/data/_next/static/chunks/app/layout-6b127f6a9d30f81d.js +1 -0
  28. recce/data/_next/static/chunks/app/page-6e7f45c0d0eb69b3.js +1 -0
  29. recce/data/_next/static/chunks/c0015c5c-6adfd10682179dba.js +1 -0
  30. recce/data/_next/static/chunks/{1268aea1-6dc1251c01bd724b.js → c41d1589-27a36c0adae3f11b.js} +4 -4
  31. recce/data/_next/static/chunks/{d90cfbaa-e7d779b3912afeec.js → d90cfbaa-10a3b2cefa2eb9ba.js} +1 -1
  32. recce/data/_next/static/chunks/{e07c302e-cd170429646873e1.js → e07c302e-21163cafc81583fb.js} +1 -1
  33. recce/data/_next/static/chunks/{fa5fb511-15fb438349ad5b97.js → fa5fb511-722a622636dc535b.js} +1 -1
  34. recce/data/_next/static/chunks/{framework-7950757d31580329.js → framework-d3cdc9ad10f589c3.js} +1 -1
  35. recce/data/_next/static/chunks/main-0aa311ca5eab4c3d.js +1 -0
  36. recce/data/_next/static/chunks/main-app-a6bd599e25466331.js +1 -0
  37. recce/data/_next/static/chunks/pages/_app-f73a7d040f1ea45c.js +1 -0
  38. recce/data/_next/static/chunks/pages/_error-c550ce619d2ef6f8.js +1 -0
  39. recce/data/_next/static/chunks/{webpack-84df6dd5ae3cf908.js → webpack-e707d93a7b1691e8.js} +1 -1
  40. recce/data/_next/static/css/9ba7df14573e8fe7.css +1 -0
  41. recce/data/index.html +1 -1
  42. recce/data/index.txt +14 -14
  43. recce/state/__init__.py +31 -0
  44. recce/state/cloud.py +384 -0
  45. recce/state/const.py +26 -0
  46. recce/state/local.py +56 -0
  47. recce/state/state.py +118 -0
  48. recce/state/state_loader.py +179 -0
  49. {recce_nightly-1.16.0.20250813.dist-info → recce_nightly-1.16.0.20250817.dist-info}/METADATA +1 -1
  50. {recce_nightly-1.16.0.20250813.dist-info → recce_nightly-1.16.0.20250817.dist-info}/RECORD +58 -54
  51. tests/test_cli.py +3 -3
  52. tests/test_core.py +147 -0
  53. tests/test_server.py +6 -6
  54. recce/data/_next/static/chunks/0376eeba-3db2196398d62270.js +0 -1
  55. recce/data/_next/static/chunks/068b80ea-833a129468ee1622.js +0 -1
  56. recce/data/_next/static/chunks/273-f3fa401bd2b6fc91.js +0 -10
  57. recce/data/_next/static/chunks/338-2e7eed5135c64550.js +0 -30
  58. recce/data/_next/static/chunks/367-ae35f5a152ee1ce5.js +0 -1
  59. recce/data/_next/static/chunks/6dc81886-78e2efe4538794ae.js +0 -1
  60. recce/data/_next/static/chunks/72-181b430654230f0e.js +0 -1
  61. recce/data/_next/static/chunks/786-774e3e3ed70a41b3.js +0 -1
  62. recce/data/_next/static/chunks/a69d64b4-d6890125a87b0aba.js +0 -1
  63. recce/data/_next/static/chunks/app/_not-found/page-c7ef8ed6dc07aaeb.js +0 -1
  64. recce/data/_next/static/chunks/app/layout-744f0a78e9e50e60.js +0 -1
  65. recce/data/_next/static/chunks/app/page-e8f798c2ae3f59c2.js +0 -1
  66. recce/data/_next/static/chunks/c0015c5c-82c219792582c104.js +0 -1
  67. recce/data/_next/static/chunks/main-app-4df79eb11c34d43c.js +0 -1
  68. recce/data/_next/static/chunks/main-cd6c104af638214a.js +0 -1
  69. recce/data/_next/static/chunks/pages/_app-73008661edbd5e05.js +0 -1
  70. recce/data/_next/static/chunks/pages/_error-cf8bbdc3cf76c83f.js +0 -1
  71. recce/data/_next/static/css/188a3a1687e2a064.css +0 -1
  72. recce/state.py +0 -865
  73. tests/test_state.py +0 -134
  74. /recce/data/_next/static/{RX_nnCGyE7ZOeoTStwYCZ → -zBI1h60_HeMe-9ZrCw7s}/_ssgManifest.js +0 -0
  75. {recce_nightly-1.16.0.20250813.dist-info → recce_nightly-1.16.0.20250817.dist-info}/WHEEL +0 -0
  76. {recce_nightly-1.16.0.20250813.dist-info → recce_nightly-1.16.0.20250817.dist-info}/entry_points.txt +0 -0
  77. {recce_nightly-1.16.0.20250813.dist-info → recce_nightly-1.16.0.20250817.dist-info}/licenses/LICENSE +0 -0
  78. {recce_nightly-1.16.0.20250813.dist-info → recce_nightly-1.16.0.20250817.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,179 @@
1
+ import logging
2
+ import threading
3
+ import time
4
+ from abc import ABC, abstractmethod
5
+ from typing import Dict, Literal, Optional, Tuple, Union, final
6
+
7
+ from recce.exceptions import RecceException
8
+ from recce.pull_request import fetch_pr_metadata
9
+
10
+ from ..util.io import SupportedFileTypes, file_io_factory
11
+ from .const import (
12
+ RECCE_CLOUD_TOKEN_MISSING,
13
+ )
14
+ from .state import RecceState
15
+
16
+ logger = logging.getLogger("uvicorn")
17
+
18
+
19
+ class RecceStateLoader(ABC):
20
+ def __init__(
21
+ self,
22
+ review_mode: bool = False,
23
+ cloud_mode: bool = False,
24
+ state_file: Optional[str] = None,
25
+ cloud_options: Optional[Dict[str, str]] = None,
26
+ initial_state: Optional[RecceState] = None,
27
+ ):
28
+ self.review_mode = review_mode
29
+ self.cloud_mode = cloud_mode
30
+ self.state_file = state_file
31
+ self.cloud_options = cloud_options or {}
32
+ self.error_message = None
33
+ self.hint_message = None
34
+ self.state: RecceState | None = initial_state
35
+ self.state_lock = threading.Lock()
36
+ self.state_etag = None
37
+ self.pr_info = None
38
+ self.catalog: Literal["github", "preview"] = "github"
39
+ self.share_id = None
40
+
41
+ if self.cloud_mode:
42
+ if self.cloud_options.get("github_token"):
43
+ self.catalog = "github"
44
+ self.pr_info = fetch_pr_metadata(
45
+ cloud=self.cloud_mode, github_token=self.cloud_options.get("github_token")
46
+ )
47
+ if self.pr_info.id is None:
48
+ raise RecceException("Cannot get the pull request information from GitHub.")
49
+ elif self.cloud_options.get("api_token"):
50
+ self.catalog = "preview"
51
+ self.share_id = self.cloud_options.get("share_id")
52
+ else:
53
+ raise RecceException(RECCE_CLOUD_TOKEN_MISSING.error_message)
54
+
55
+ @abstractmethod
56
+ def verify(self) -> bool:
57
+ """
58
+ Verify the state loader configuration.
59
+ Returns:
60
+ bool: True if the configuration is valid, False otherwise.
61
+ """
62
+ raise NotImplementedError("Subclasses must implement this method.")
63
+
64
+ @property
65
+ def token(self):
66
+ return self.cloud_options.get("github_token") or self.cloud_options.get("api_token")
67
+
68
+ @property
69
+ def error_and_hint(self) -> (Union[str, None], Union[str, None]):
70
+ return self.error_message, self.hint_message
71
+
72
+ def update(self, state: RecceState):
73
+ self.state = state
74
+
75
+ @final
76
+ def load(self, refresh=False) -> RecceState:
77
+ if self.state is not None and refresh is False:
78
+ return self.state
79
+ self.state_lock.acquire()
80
+ try:
81
+ self.state, self.state_etag = self._load_state()
82
+ finally:
83
+ self.state_lock.release()
84
+ return self.state
85
+
86
+ @abstractmethod
87
+ def _load_state(self) -> Tuple[RecceState, str]:
88
+ """
89
+ Load the state from the specified source (file or cloud).
90
+ Returns:
91
+ RecceState: The loaded state object.
92
+ str: The etag of the state file (if applicable).
93
+ """
94
+ raise NotImplementedError("Subclasses must implement this method.")
95
+
96
+ def save_as(self, state_file: str, state: RecceState = None):
97
+ if self.cloud_mode:
98
+ raise Exception("Cannot save the state to Recce Cloud.")
99
+
100
+ self.state_file = state_file
101
+ self.export(state)
102
+
103
+ @final
104
+ def export(self, state: RecceState = None) -> Union[str, None]:
105
+ if state is not None:
106
+ self.update(state)
107
+
108
+ start_time = time.time()
109
+ self.state_lock.acquire()
110
+ try:
111
+ message, state_etag = self._export_state()
112
+ self.state_etag = state_etag
113
+ end_time = time.time()
114
+ elapsed_time = end_time - start_time
115
+ finally:
116
+ self.state_lock.release()
117
+ logger.info(f"Store state completed in {elapsed_time:.2f} seconds")
118
+ return message
119
+
120
+ @abstractmethod
121
+ def _export_state(self, state: RecceState = None) -> Tuple[Union[str, None], str]:
122
+ """
123
+ Export the current Recce state to a file or cloud storage.
124
+ Returns:
125
+ str: A message indicating the result of the export operation.
126
+ str: The etag of the exported state file (if applicable).
127
+ """
128
+ raise NotImplementedError("Subclasses must implement this method.")
129
+
130
+ def _export_state_to_file(self, file_path: str, file_type: SupportedFileTypes = SupportedFileTypes.FILE) -> str:
131
+ """
132
+ Store the state to a file. Store happens when terminating the server or run instance.
133
+ """
134
+
135
+ json_data = self.state.to_json()
136
+ io = file_io_factory(file_type)
137
+
138
+ io.write(file_path, json_data)
139
+ return f"The state file is stored at '{file_path}'"
140
+
141
+ def refresh(self):
142
+ new_state = self.load(refresh=True)
143
+ return new_state
144
+
145
+ def check_conflict(self) -> bool:
146
+ if not self.cloud_mode:
147
+ return False
148
+
149
+ metadata = self._get_metadata_from_recce_cloud()
150
+ if not metadata:
151
+ return False
152
+
153
+ state_etag = metadata.get("etag")
154
+ return state_etag != self.state_etag
155
+
156
+ def info(self):
157
+ if self.state is None:
158
+ self.error_message = "No state is loaded."
159
+ return None
160
+
161
+ state_info = {
162
+ "mode": "cloud" if self.cloud_mode else "local",
163
+ "source": None,
164
+ }
165
+ if self.cloud_mode:
166
+ state_info["source"] = "Recce Cloud"
167
+ state_info["pull_request"] = self.pr_info
168
+ else:
169
+ state_info["source"] = self.state_file
170
+ return state_info
171
+
172
+ @abstractmethod
173
+ def purge(self) -> bool:
174
+ """
175
+ Purge the state file or cloud storage.
176
+ Returns:
177
+ bool: True if the purge was successful, False otherwise.
178
+ """
179
+ raise NotImplementedError("Subclasses must implement this method.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: recce-nightly
3
- Version: 1.16.0.20250813
3
+ Version: 1.16.0.20250817
4
4
  Summary: Environment diff tool for dbt
5
5
  Home-page: https://github.com/InfuseAI/recce
6
6
  Author: InfuseAI Dev Team
@@ -1,7 +1,7 @@
1
- recce/VERSION,sha256=-dX_0PWVh-oDWK0ExN15I2skJU33S3e627n4IM8y4Qo,16
1
+ recce/VERSION,sha256=LgdhFDUzTwEfPBZOqGNV1DvNaGwGqdifO__NmVKrjwk,16
2
2
  recce/__init__.py,sha256=yNb0QT-yoStex0VZALNJvUwtPLommoVCStcow31guqo,2392
3
3
  recce/artifact.py,sha256=ds0erp0-gGnH4Nx2vvapWR02pX8FmKQcqcd6QGqiVPw,6536
4
- recce/cli.py,sha256=KB9LEzeBasl2xJ4t5oBaNMIipOMOX0NprdyizszIU-Q,44564
4
+ recce/cli.py,sha256=ECfV2IQg79_kOElCYBxd2ubBeG6KfoQqf6xcn4Tm9D4,44732
5
5
  recce/config.py,sha256=A4CbKcIdwQ4A9Q2ba4riHUshVQw1D-qDelMOdlt2khU,4661
6
6
  recce/connect_to_cloud.py,sha256=_SkX2pdyXa9FNyaHvItyYVPF2nZxy2JnCyd_o_psh2Y,4750
7
7
  recce/core.py,sha256=MtBWxemvCQDdUITwkU2JyuQYqcjlA0a76M8Kg53ECxQ,10754
@@ -12,7 +12,6 @@ recce/github.py,sha256=PEpM6ZRiinsPbXSWj4aJCKbZrN1jUXzpzAfJq_CGah4,7420
12
12
  recce/pull_request.py,sha256=aW0B1NE2LUKTam1S4TQ7smXB9KLE1DV8GnyBqNXA6j8,3832
13
13
  recce/run.py,sha256=PNafwUUJdIG8b1o0y1QuvjACpA3E-5a3keH45CrWHN0,14044
14
14
  recce/server.py,sha256=qW81QRZVYEfeDT0n6FwsN9TGhzk6-Zx2mPUjjy01DRM,22473
15
- recce/state.py,sha256=p4CBneaat-OFwhABfj_zG6n0mPGzrYE-F5xi28Cnzuc,34069
16
15
  recce/summary.py,sha256=Mbxvxr9KazR5o9icqhhjiGHsoAiWxQU4PdN7HytBJ1c,19154
17
16
  recce/adapter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
17
  recce/adapter/base.py,sha256=T_JNeLHgiHSaegw-DbrvHOaYjMyZcjj2Qtg5cWh_fco,3548
@@ -24,51 +23,51 @@ recce/apis/check_api.py,sha256=KMCXSMl1qqzx2jQgRqCrD4j_cY3EHBbM3H2-t-6saAU,6227
24
23
  recce/apis/check_func.py,sha256=gktbCcyk3WGvWRJJ-wDnwv7NrIny2nTHWLl1-kdiVRo,4183
25
24
  recce/apis/run_api.py,sha256=eOaxOxXDkH59uqGCd4blld7edavUx7JU_DCd2WAYrL8,3416
26
25
  recce/apis/run_func.py,sha256=6wC8TDU-h7TLr2VZH7HNsWaUVlQ9HBN5N_dwqfi4lMY,7440
27
- recce/data/404.html,sha256=iYLvgsmZMX8xL2Y10i-icLyZQKhb6ExhA-OHKgVVIvM,59079
26
+ recce/data/404.html,sha256=ZTSM5IfTlY2NM10T3Mt2C8i_zYm_UXDDGROsq4QQ8dM,59081
28
27
  recce/data/auth_callback.html,sha256=H-XfdlAFiw5VU2RpKBVQbwh1AIqJrPHrFA0S09nNJZA,94779
29
28
  recce/data/favicon.ico,sha256=B2mBumUOnzvUrXrqNkrc5QfdDXjzEXRcWkWur0fJ6sM,2565
30
- recce/data/index.html,sha256=FZI-LIX9jM3An6yKo1Xz-6PArjEs3M5BA6K13HyBmQ0,77443
31
- recce/data/index.txt,sha256=loUpOEFS9tebUWupB7f9PEv42g4QGPI8rB8D-xm3peI,6403
32
- recce/data/_next/static/RX_nnCGyE7ZOeoTStwYCZ/_buildManifest.js,sha256=xih3ZGwUimzWECoZJsiH_MfTi3cNvK2DbJ_VXPRZHWY,544
33
- recce/data/_next/static/RX_nnCGyE7ZOeoTStwYCZ/_ssgManifest.js,sha256=Z49s4suAsf5y_GfnQSvm4qtq2ggxEbZPfEDTXjy6XgA,80
34
- recce/data/_next/static/chunks/0376eeba-3db2196398d62270.js,sha256=ef3PXUZocFm_JH0fYU_EfcaLKov9vMUhrat1YvV-IIU,80953
35
- recce/data/_next/static/chunks/068b80ea-833a129468ee1622.js,sha256=LrX07Wm6qBJ1uVFYOIkium2ZZ7TkhoCzaz5F_D5P-sM,4627
36
- recce/data/_next/static/chunks/0ddaf06c-c7961285f66460f6.js,sha256=rY2q_JCikDHZGB8z6nHMV7TON5MlsD4B2zxkj_9DFkE,697
37
- recce/data/_next/static/chunks/1268aea1-6dc1251c01bd724b.js,sha256=o2kP28fiIxMMpjU4S_PbKzQyLCofztY5NuKb3dE95Js,81786
38
- recce/data/_next/static/chunks/12f8fac4-16838e42d28d45c3.js,sha256=v1Bc31aD0IOpfixlIyQgOkteLw4ZCwYttEg37O8CdQk,152794
39
- recce/data/_next/static/chunks/235b8375-8c84c51d7bd4f6aa.js,sha256=CuHpoYgoktXz63qQW5XLGJdY1Cq0-ZJqk3LTmVOe2ug,1682
40
- recce/data/_next/static/chunks/2541941f-2cd3a7c2d629bd33.js,sha256=03PEb_eExziXtGvmd8-QLru-pPbrkJZh2dj3JR1VZx0,689
41
- recce/data/_next/static/chunks/273-f3fa401bd2b6fc91.js,sha256=HopLAKoEGP0dFN57b_41PkPqWAdxDAGzXPrVB0tHSt4,1277071
42
- recce/data/_next/static/chunks/2fc37c1e-910deebeb3d77c90.js,sha256=npExGSkJthgMbnqcvsiEb9b8aCCf7EQLWu1Io-6By0U,3836
43
- recce/data/_next/static/chunks/338-2e7eed5135c64550.js,sha256=0MRikUq4wS_iboITYISGCEWffqgtIr95bvSA_gVx5KY,264552
44
- recce/data/_next/static/chunks/367-ae35f5a152ee1ce5.js,sha256=PgRntGGaiUZc3lYFiGE1kddE12rkmi7Ao112KXIDzVo,84706
45
- recce/data/_next/static/chunks/3a92ee20-0400ffe460c7c803.js,sha256=wI3cuxjDqcE8hRKMg29Mt2dl16jcQ6lBr4JYcLk3_IU,177908
46
- recce/data/_next/static/chunks/62446465-423c03bb8c1f59b6.js,sha256=xiGo3FRqRpbbBqPwj2UV2zXSfCAPA35TRbyA77kiNMY,2622
47
- recce/data/_next/static/chunks/6af7f9e9-60aa8706f49dae45.js,sha256=bLrPO62eNT0ez4mooE8DDJITbbm_v6kFB_t8e5hlhx8,767
48
- recce/data/_next/static/chunks/6cf54382-49d52ae6e564e2ac.js,sha256=5v9VAkSAZMKNXwaGWjMAkkuP8YKP79F-y6c7RA4U_wE,8973
49
- recce/data/_next/static/chunks/6dc81886-78e2efe4538794ae.js,sha256=IM7QQ9wo-m0E1H1X7mrvEzSFUwm90eJzaom1QIDa_6M,69715
50
- recce/data/_next/static/chunks/715e4acc-9e2e6df4eb3809d1.js,sha256=Oc4RwCTpratqwzi7e5BvF5UIB_kC1foA4F4DKFuigMc,867
51
- recce/data/_next/static/chunks/72-181b430654230f0e.js,sha256=1U7NpDH1sUKrXLx8XebUVjJY9MBy2VDOEf4Z0iY2Ck4,166422
52
- recce/data/_next/static/chunks/786-774e3e3ed70a41b3.js,sha256=ylQ9e5VQSdHyrOZdJsXSycnJL5ZxcAQ3uv5peMz-EQo,11654
53
- recce/data/_next/static/chunks/8d700b6a.7fe2c8c3f4e333a6.js,sha256=drJF3LrBc6gwgD-Yj_rRz-Ap5Cf0_XH7w6HTEwZhcrU,120029
54
- recce/data/_next/static/chunks/a69d64b4-d6890125a87b0aba.js,sha256=M75FwsnNr_AA9jDad2sHuXK9WZxgvN10xCMcn9gRIHU,172734
55
- recce/data/_next/static/chunks/ae307f12-01100009689ace61.js,sha256=-5FIEA3ucphP6IGvMeJkSXJ4jxnxJ-jPuuNl1isHn48,211281
56
- recce/data/_next/static/chunks/c0015c5c-82c219792582c104.js,sha256=39vsBp9YjOMCTy1IehEBh7UCxYbzeh07NQcifOqMV2g,481
57
- recce/data/_next/static/chunks/d90cfbaa-e7d779b3912afeec.js,sha256=fEHB0fWh_Fl1TlK7NaBe11fokveLejfj4R3yOfhrAVQ,1909
58
- recce/data/_next/static/chunks/e07c302e-cd170429646873e1.js,sha256=0uUFHO2Mm5q2SWTHTC5loXxKoWuwOwIvt2sAyH_Rj0c,555
59
- recce/data/_next/static/chunks/fa5fb511-15fb438349ad5b97.js,sha256=UaaRM07bG8X9-E08mxaKLCA98d3V3OP8MqFOvZlBFjc,8899
60
- recce/data/_next/static/chunks/framework-7950757d31580329.js,sha256=WJSQaB_D-Qck95sQYmkMg3clAB7GfsgPYVAmEmOMkrk,182740
61
- recce/data/_next/static/chunks/main-app-4df79eb11c34d43c.js,sha256=3QIEMM6pO9b9MvW7xK4Toukx48-I-X1-0eqc4Bv7YaY,567
62
- recce/data/_next/static/chunks/main-cd6c104af638214a.js,sha256=pYGvgDmnAThczzBHShkI4dMuXUSZnzq6TIkW4ZI-61U,117829
29
+ recce/data/index.html,sha256=tLnxhyDTmIZsWkniEJCUjUlrIa_ni7BC6T8HJiRvd0c,77442
30
+ recce/data/index.txt,sha256=lTm8hehQUC-KN98SXNlAnYltKgudJPck8TNFbYIqGbw,6402
31
+ recce/data/_next/static/-zBI1h60_HeMe-9ZrCw7s/_buildManifest.js,sha256=l8kfhgQIEvC1Ede_vT6iLavFimajvA32VzTAPK28pSw,544
32
+ recce/data/_next/static/-zBI1h60_HeMe-9ZrCw7s/_ssgManifest.js,sha256=Z49s4suAsf5y_GfnQSvm4qtq2ggxEbZPfEDTXjy6XgA,80
33
+ recce/data/_next/static/chunks/068b80ea-7fab85837b21fed5.js,sha256=irzMcuBMTPe09LlF8z1CI-7_-Tr1rkA7oyuL6K_77DU,4628
34
+ recce/data/_next/static/chunks/0ddaf06c-05b8d2a8a3e5c6f3.js,sha256=vcGJX-WEXhfmuI5WKhFYPOZgNQoddWfFD565DniftLw,697
35
+ recce/data/_next/static/chunks/12f8fac4-170216f89d5576c6.js,sha256=1RvykSI64NYLTPwaCyxexU91wmra1Hg2XpvNfDu2mpw,152794
36
+ recce/data/_next/static/chunks/235b8375-c2d62e0b44e3ae74.js,sha256=yxUPrQ5pIhTYt-MijQhQxl8uXailxmebL5-_p20RmI8,1682
37
+ recce/data/_next/static/chunks/2541941f-d65696bd5d37dfa2.js,sha256=uxtHe-PUeC7kFG88JwOHTZnhuUcLEwcMpwy8M15E7Oo,689
38
+ recce/data/_next/static/chunks/25c097a4-51d7c9060de49a6c.js,sha256=CAPNgJd_YPVRR4bEIlqSJvzEelxKbBHDhaT-Jgj3S5M,80951
39
+ recce/data/_next/static/chunks/27e92eb0-4d8ec3c6cdf0d5f1.js,sha256=L5tol9qPfJurNZyLt9stkOVIeSgWUqLzEh6kMUL8Hjo,172733
40
+ recce/data/_next/static/chunks/2fc37c1e-fd3c94ef85344810.js,sha256=aCCYev-qd6QV11nP0Ki-TxOFReBULkAU15ieCNys8wQ,3836
41
+ recce/data/_next/static/chunks/370-bd3e0bf7237858bc.js,sha256=epoqgDgP1Y2vEnkJDQnArFVj8ObuW4qqxSWHToWVcAY,166418
42
+ recce/data/_next/static/chunks/3a92ee20-a33bb5964d0b2f37.js,sha256=9O1MzNoBzy_CJsJ_9kNXsOfUCEPu6IPF1LaZl4SdXTo,177908
43
+ recce/data/_next/static/chunks/490-ce854af0bc670d3e.js,sha256=qcOfVDlsS9UEioRtaUSUjZetYGrehutf3E64V8BUHl4,84719
44
+ recce/data/_next/static/chunks/575-2443813ff441d02c.js,sha256=Uz72cwal6dDOkGZzBsC7a4ER7odRIupMYe204Rw8n3A,11652
45
+ recce/data/_next/static/chunks/62446465-5277e7ad93431c07.js,sha256=38AOs2vIyqmxHVXRG4VRFu1QjG1y6i1Ws3CpEzRrC8E,2622
46
+ recce/data/_next/static/chunks/6af7f9e9-1b5f068628caacd4.js,sha256=jAFwFVuMQAGoqIPgFBVjYe0v0rfb_JHxVocgh3oRQiY,767
47
+ recce/data/_next/static/chunks/6cf54382-9bc9b4c7b7e4b8d8.js,sha256=W7aT-oWMJkkONrHvS7m1a9uGleQwxkwto1uUtLDMP5c,8973
48
+ recce/data/_next/static/chunks/6dc81886-2b329a8d619994c9.js,sha256=iwW6qRDhe1OiueluOLtTjSCe5JM2EeDc150erqN5MJ4,69716
49
+ recce/data/_next/static/chunks/715e4acc-be8c974e746c0e77.js,sha256=eAMUMEVesI_Y2ILgBXKis3j1aMAanMemVF9f8wudSGw,867
50
+ recce/data/_next/static/chunks/76-54eeed3fa75d6146.js,sha256=P5XYY99i4SmK7CuxV_jipeIofpUtX0VeeTQH048ZegE,1278576
51
+ recce/data/_next/static/chunks/8d700b6a.6c883c62894c0d11.js,sha256=UWq6kJeCbsXU53rRLrWwJ2LovmFdSgtxNtbBHd8i6Fk,120029
52
+ recce/data/_next/static/chunks/975-341ea105d3d5e6ca.js,sha256=8GgemELCpkQG96SeizeRnvemGVTxG8NoN7SbntqmDGg,264544
53
+ recce/data/_next/static/chunks/ae307f12-0f5382a8bd52593f.js,sha256=gUtta4lpvB204FEngGBQSiZGtkOIDcnA16HZjmXUCFY,211281
54
+ recce/data/_next/static/chunks/c0015c5c-6adfd10682179dba.js,sha256=c-3i-rXXLgMYYXTI6XUpSbzVqGUjc-r43MwXVv6OCNA,480
55
+ recce/data/_next/static/chunks/c41d1589-27a36c0adae3f11b.js,sha256=Q9hCSQqvLuOlaNdLhgoyi_puHreAi7AvqXQND_FwXrQ,81783
56
+ recce/data/_next/static/chunks/d90cfbaa-10a3b2cefa2eb9ba.js,sha256=MWVOsgm6cUWBJ46ro6W7nKHAdlytfLiOu2xXVH-s9Xo,1909
57
+ recce/data/_next/static/chunks/e07c302e-21163cafc81583fb.js,sha256=SwwtRu6Qf8wbzNPzCsSviwvV3rlCPcKVMbp6lyUNFvI,555
58
+ recce/data/_next/static/chunks/fa5fb511-722a622636dc535b.js,sha256=2zOvlL6XYPTD3ZqP8FHHbCQLAjY3WmStWdhWyLgLDBE,8899
59
+ recce/data/_next/static/chunks/framework-d3cdc9ad10f589c3.js,sha256=mcscUrllIKLVC_A3tCZvZj_2rXmEmTPmIXdhlFEhJ5Q,182740
60
+ recce/data/_next/static/chunks/main-0aa311ca5eab4c3d.js,sha256=GLyhf-IMRzYUoaMIyGz5WHNtq7D69Lm7p-VWICm8Q-k,117836
61
+ recce/data/_next/static/chunks/main-app-a6bd599e25466331.js,sha256=wxhVwz8VAfzs7joHV0YqpehyfOgrMdPFkN82-ecRqaU,570
63
62
  recce/data/_next/static/chunks/polyfills-42372ed130431b0a.js,sha256=CXPB1kyIrcjjyVBBDLWLKI9yEY1ZZbeASUON648vloM,112594
64
- recce/data/_next/static/chunks/webpack-84df6dd5ae3cf908.js,sha256=_FsecrHvMVA-0EfxZv-v8svx7W50DkEH-jb1NbVSSlU,3580
65
- recce/data/_next/static/chunks/app/layout-744f0a78e9e50e60.js,sha256=C1sJ1DG8RYccuG-VVTUBO-mmSjiOvJPdxqS9z0maJLk,1410
66
- recce/data/_next/static/chunks/app/page-e8f798c2ae3f59c2.js,sha256=5zckfX5OoWAY3HAqSLKdawS1XgWiWkbFLvbwP02LDs4,166586
67
- recce/data/_next/static/chunks/app/_not-found/page-c7ef8ed6dc07aaeb.js,sha256=1FE2NEJaSY3ZE8PzMkICM5FcbAbGa8K5ee0ucm2gNfU,2673
68
- recce/data/_next/static/chunks/pages/_app-73008661edbd5e05.js,sha256=wV9XbJ2FbdpnEztEcX5YE7ecw4ec7LPp2fYhOnLbcoo,240
69
- recce/data/_next/static/chunks/pages/_error-cf8bbdc3cf76c83f.js,sha256=k0UKnF-ueCkgYTKsUejnYmf31Stt9q5mOjX66XWVqrY,217
70
- recce/data/_next/static/css/188a3a1687e2a064.css,sha256=fKJMV0A2hM3cjuRmNhx2V_IL8SPaEsfucP-1sc0QVy4,12258
63
+ recce/data/_next/static/chunks/webpack-e707d93a7b1691e8.js,sha256=Ibkw2UCDBVUi1MRPLO8wEAN86p380LWw6zF1R_Qamb8,3580
64
+ recce/data/_next/static/chunks/app/layout-6b127f6a9d30f81d.js,sha256=O9k3qTFd19Z-tiHYGAiMGUNY6OQ6scwZQXSwEqhun5E,1412
65
+ recce/data/_next/static/chunks/app/page-6e7f45c0d0eb69b3.js,sha256=-d989hXb8kBf-4J_YmS4iHjQUWh3Baag8xKBq0vWWLU,166630
66
+ recce/data/_next/static/chunks/app/_not-found/page-f6afb3793e4b6ce1.js,sha256=yi2tvMWSs05jxM-LZ_P9JKOPztQSJF_van1IJIVeV5A,2680
67
+ recce/data/_next/static/chunks/pages/_app-f73a7d040f1ea45c.js,sha256=0llEmHlHOPS9UZbHENhdUObZ7tDbQV2VCZ0LId_f22w,240
68
+ recce/data/_next/static/chunks/pages/_error-c550ce619d2ef6f8.js,sha256=44UdnARhGJhgwgzpgZubFW_oD2tPnoqrV1xRUyquY_E,221
71
69
  recce/data/_next/static/css/8edca58d4abcf908.css,sha256=Ab-wk7RhsXgiX5fkJsrCJ-EVvrS1hZtOFpI0uZQSgL0,8093
70
+ recce/data/_next/static/css/9ba7df14573e8fe7.css,sha256=HbimhkWMsVpAgDq9vpbDaQE-7ProdWfvkS6RzO7meMk,12308
72
71
  recce/data/_next/static/css/abdb9814a3dd18bb.css,sha256=Do5x9FJaOIe-pSBlbk_PP7PQAYFbDXTTl_0PWY4cnBM,1327
73
72
  recce/data/_next/static/css/c21263c1520b615b.css,sha256=2TrfHvYUzCBJKLdPzYN6hOAg1n8CoOsJFo36LJPQbOI,13037
74
73
  recce/data/_next/static/media/montserrat-cyrillic-800-normal.22628180.woff2,sha256=P5Sx5PNkdTlDYzAdulW_OPRfMokDLi6XTpmS8KJSRoI,11148
@@ -95,6 +94,12 @@ recce/models/__init__.py,sha256=F7cgALtdWnwv37R0eEgKZ_yBsMwxWnUfo3jAZ3u6qyU,209
95
94
  recce/models/check.py,sha256=jjR1SGyczjrqyK-0Zas6ikLIGSgVp54lvfcQA19AniI,1588
96
95
  recce/models/run.py,sha256=QK2gvOWvko9YYhd2NLs3BPt5l4MSCZGwpzTAiqx9zJw,1161
97
96
  recce/models/types.py,sha256=9ReOyIv3rUD3_cIQfB9Rplb0L2YQuZr4kS9Zai30nB8,5234
97
+ recce/state/__init__.py,sha256=V1FRPTQJUz-uwI3Cn8wDa5Z9bueVs86MR_1Ti4RGfPc,650
98
+ recce/state/cloud.py,sha256=FJS9ktORHT17nBeh1ZDq0PVC21W1Dj8Xz5u4u8X3FLM,15443
99
+ recce/state/const.py,sha256=Me3uVQHi_uZysl3tfpmghPInuKF236X6LfMKVvJlCgA,827
100
+ recce/state/local.py,sha256=bZIkl7cAfyYaGgYEr3uD_JLrtwlHBnu8_o1Qz2djQzw,1920
101
+ recce/state/state.py,sha256=L1cEAl6S9rxTvSIc8ICNwOYoCYK5tR3M0kBo4NTcXK4,3617
102
+ recce/state/state_loader.py,sha256=vGM8HCIv_3PGfcKPgq_GgG_3roloaDL_Am0oTreHmTk,5979
98
103
  recce/tasks/__init__.py,sha256=b553AtDHjYROgmMePv_Hv_X3fjh4iEn11gzzpUJz6_o,610
99
104
  recce/tasks/core.py,sha256=JFYa1CfgOiRPQ7KVTwMuxJjhMB-pvCwB-xezVt-h3RU,4080
100
105
  recce/tasks/dataframe.py,sha256=03UBWwt0DFTXlaEOtnV5i_mxdRKD7UbRayewEL2Ub48,3650
@@ -120,16 +125,15 @@ recce/util/pydantic_model.py,sha256=KumKuyCjbTzEMsKLE4-b-eZfp0gLhYDdmVtw1-hxiJw,
120
125
  recce/util/recce_cloud.py,sha256=mqZn63Fh1kZaT0L6AR9K4yqTnGpZBGEWGBHFapLmS_w,9882
121
126
  recce/util/singleton.py,sha256=1cU99I0f9tjuMQLMJyLsK1oK3fZJMsO5-TbRHAMXqds,627
122
127
  recce/yaml/__init__.py,sha256=EgXYlFeJZchatUClRDXbIC5Oqb2_nBvB2NqItYVihio,1292
123
- recce_nightly-1.16.0.20250813.dist-info/licenses/LICENSE,sha256=CQjjMy9aYPhfe8xG_bcpIfKtNkdxLZ5IOb8oPygtUhY,11343
128
+ recce_nightly-1.16.0.20250817.dist-info/licenses/LICENSE,sha256=CQjjMy9aYPhfe8xG_bcpIfKtNkdxLZ5IOb8oPygtUhY,11343
124
129
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
125
- tests/test_cli.py,sha256=_ikuZW4bYEGyj5A_V7nDSyBa1tVZqc0TVm-TJ0rEYVo,5467
130
+ tests/test_cli.py,sha256=4N_F_lg00fGBuK3I9EyaF2Doi7PpKCrjqbuJ5YI1KqE,5467
126
131
  tests/test_config.py,sha256=ODDFe_XF6gphmSmmc422dGLBaCCmG-IjDzTkD5SJsJE,1557
127
132
  tests/test_connect_to_cloud.py,sha256=b2fgV8L1iQBdEwh6RumMsIIyYg7GtMOMRz1dvE3WRPg,3059
128
- tests/test_core.py,sha256=7NKQc_8Krjsp3imMMmXHV6hQ7Kvw8QGE2nVtigebZYE,735
133
+ tests/test_core.py,sha256=WgCFm8Au3YQI-V5UbZ6LA8irMNHRc7NWutIq2Mny0LE,6242
129
134
  tests/test_dbt.py,sha256=VzXvdoJNwwEmKNhJJDNB1N_qZYwp1aoJQ1sLcoyRBmk,1316
130
135
  tests/test_pull_request.py,sha256=HmZo5MoDaoKSgPwbLxJ3Ur3ajZ7IxhkzJxaOmhg6bwE,3562
131
- tests/test_server.py,sha256=l2EDIonpDU3CzuCpZSXTCyH_X32xdscSz-bdNSuTkjA,3397
132
- tests/test_state.py,sha256=phSYlyxTx2Sq0K8DXUrfaO5Jc7lgZeeWNNtc9JD3fYc,4988
136
+ tests/test_server.py,sha256=sq03awzG-kvLNJwHaZGDvVjd2uAs502XUKZ2nLNwvRQ,3391
133
137
  tests/test_summary.py,sha256=D0WvAkdO-vzGcvholH2rfS1wTxUXjVHwWm59fWy45eA,2876
134
138
  tests/adapter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
139
  tests/adapter/dbt_adapter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -149,8 +153,8 @@ tests/tasks/test_row_count.py,sha256=21PaP2aq-x8-pqwzWHRT1sixhQ8g3CQNRWOZTTmbK0s
149
153
  tests/tasks/test_schema.py,sha256=7ds4Vx8ixaiIWDR49Lvjem4xlPkRP1cXazDRY3roUak,3121
150
154
  tests/tasks/test_top_k.py,sha256=YR_GS__DJsbDlQVaEEdJvNQ3fh1VmV5Nb3G7lb0r6YM,1779
151
155
  tests/tasks/test_valuediff.py,sha256=_xQJGgxsXoy2NYk_Z6Hsw2FlVh6zk2nN_iUueyRN1e8,2046
152
- recce_nightly-1.16.0.20250813.dist-info/METADATA,sha256=4LHS2M2wdSQcUMBikpNhYio1TFQxcwYDLKRoVnqnuR4,9366
153
- recce_nightly-1.16.0.20250813.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
154
- recce_nightly-1.16.0.20250813.dist-info/entry_points.txt,sha256=oqoY_IiwIqXbgrIsPnlqUqao2eiIeP2dprowkOlmeyg,40
155
- recce_nightly-1.16.0.20250813.dist-info/top_level.txt,sha256=6PKGVpf75idP0C6KEaldDzzZUauIxNu1ZDstau1pI4I,12
156
- recce_nightly-1.16.0.20250813.dist-info/RECORD,,
156
+ recce_nightly-1.16.0.20250817.dist-info/METADATA,sha256=defY6OjhIYa5CEEp1JalxmRs-7d2XrJ1XUO4fheyqBY,9366
157
+ recce_nightly-1.16.0.20250817.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
158
+ recce_nightly-1.16.0.20250817.dist-info/entry_points.txt,sha256=oqoY_IiwIqXbgrIsPnlqUqao2eiIeP2dprowkOlmeyg,40
159
+ recce_nightly-1.16.0.20250817.dist-info/top_level.txt,sha256=6PKGVpf75idP0C6KEaldDzzZUauIxNu1ZDstau1pI4I,12
160
+ recce_nightly-1.16.0.20250817.dist-info/RECORD,,
tests/test_cli.py CHANGED
@@ -6,7 +6,7 @@ from click.testing import CliRunner
6
6
  from recce.cli import run as cli_command_run
7
7
  from recce.cli import server as cli_command_server
8
8
  from recce.core import RecceContext
9
- from recce.state import RecceStateLoader
9
+ from recce.state import CloudStateLoader
10
10
 
11
11
 
12
12
  def test_cmd_version():
@@ -48,11 +48,11 @@ class TestCommandServer(TestCase):
48
48
  @patch.object(RecceContext, "verify_required_artifacts")
49
49
  @patch("recce.util.recce_cloud.get_recce_cloud_onboarding_state")
50
50
  @patch("recce.cli.uvicorn.run")
51
- @patch("recce.cli.RecceStateLoader")
51
+ @patch("recce.cli.CloudStateLoader")
52
52
  def test_cmd_server_with_cloud(
53
53
  self, mock_state_loader_class, mock_run, mock_get_recce_cloud_onboarding_state, mock_verify_required_artifacts
54
54
  ):
55
- mock_state_loader = MagicMock(spec=RecceStateLoader)
55
+ mock_state_loader = MagicMock(spec=CloudStateLoader)
56
56
  mock_state_loader.verify.return_value = True
57
57
  mock_state_loader.review_mode = True
58
58
  mock_get_recce_cloud_onboarding_state.return_value = "completed"
tests/test_core.py CHANGED
@@ -1,6 +1,153 @@
1
1
  # noinspection PyUnresolvedReferences
2
+ import os
3
+ import unittest
4
+ from datetime import datetime
5
+
6
+ from recce.core import RecceContext
7
+ from recce.models import Check, Run, RunType
8
+ from recce.state import ArtifactsRoot, FileStateLoader, RecceState
2
9
  from tests.adapter.dbt_adapter.conftest import dbt_test_helper # noqa: F401
3
10
 
11
+ current_dir = os.path.dirname(os.path.abspath(__file__))
12
+
13
+
14
+ class TestRecceState(unittest.TestCase):
15
+ def test_load(self):
16
+ run = Run(type=RunType.QUERY, params=dict(sql_template="select * from users"))
17
+ check = Check(name="check 1", description="desc 1", type=run.type, params=run.params)
18
+
19
+ state = RecceState(runs=[run], checks=[check])
20
+ json_content = state.to_json()
21
+ new_state = RecceState.from_json(json_content)
22
+
23
+ run_loaded = new_state.runs[0]
24
+ check_loaded = new_state.checks[0]
25
+
26
+ assert run.run_id == run_loaded.run_id
27
+ assert check.check_id == check_loaded.check_id
28
+
29
+ def test_merge_checks(self):
30
+ check1 = Check(name="test1", description="", type="query")
31
+ check2 = Check(name="test2", description="", type="query", updated_at=datetime(2000, 1, 1))
32
+ check2_2 = Check(
33
+ name="test2_2", description="", type="query", updated_at=datetime(2020, 1, 1), check_id=check2.check_id
34
+ )
35
+ check3 = Check(name="test3", description="", type="query")
36
+
37
+ context = RecceContext()
38
+ state = RecceState(checks=[check1], runs=[])
39
+ context.import_state(state)
40
+ self.assertEqual(1, len(context.checks))
41
+ self.assertEqual(check1.name, context.checks[0].name)
42
+
43
+ context = RecceContext(checks=[check1, check2])
44
+ state = RecceState(checks=[check1, check2_2, check3], runs=[])
45
+ context.import_state(state)
46
+ self.assertEqual(3, len(context.checks))
47
+ self.assertEqual(check2_2.name, context.checks[1].name)
48
+
49
+ def test_merge_preset_checks(self):
50
+ check1 = Check(
51
+ name="test1",
52
+ description="test1",
53
+ type="query",
54
+ params=dict(foo="bar"),
55
+ updated_at=datetime(2000, 1, 1),
56
+ is_preset=True,
57
+ )
58
+ check2 = Check(
59
+ name="test2",
60
+ description="test2",
61
+ type="query",
62
+ params=dict(foo="bar"),
63
+ updated_at=datetime(2001, 1, 1),
64
+ is_preset=True,
65
+ )
66
+
67
+ context = RecceContext(checks=[check1])
68
+ state = RecceState(checks=[check2], runs=[])
69
+ context.import_state(state)
70
+ self.assertEqual(1, len(context.checks))
71
+ self.assertEqual(check2.name, context.checks[0].name)
72
+
73
+ context = RecceContext(checks=[check2])
74
+ state = RecceState(checks=[check1], runs=[])
75
+ context.import_state(state)
76
+ self.assertEqual(1, len(context.checks))
77
+ self.assertEqual(check2.name, context.checks[0].name)
78
+
79
+ def test_revert_checks(self):
80
+ check1 = Check(name="test1", description="", type="query")
81
+ check2 = Check(name="test2", description="", type="query")
82
+ check2_2 = Check(name="test2_2", description="", type="query", check_id=check2.check_id)
83
+ check3 = Check(name="test3", description="", type="query")
84
+
85
+ context = RecceContext(checks=[check1, check2])
86
+ state = RecceState(checks=[check2_2, check3], runs=[])
87
+ context.import_state(state, merge=False)
88
+ self.assertEqual(2, len(context.checks))
89
+ self.assertEqual(check2_2.name, context.checks[0].name)
90
+
91
+ def test_merge_runs(self):
92
+ run1 = Run(type="query")
93
+ run2 = Run(type="query")
94
+ run3 = Run(type="query")
95
+
96
+ context = RecceContext(runs=[])
97
+ state = RecceState(runs=[run1])
98
+ context.import_state(state)
99
+ self.assertEqual(1, len(context.runs))
100
+
101
+ context = RecceContext(runs=[run1, run2])
102
+ state = RecceState(runs=[run2, run3])
103
+ context.import_state(state)
104
+ self.assertEqual(3, len(context.runs))
105
+
106
+ def test_merge_dbt_artifacts(self):
107
+ import json
108
+ import os
109
+
110
+ with open(os.path.join(current_dir, "manifest.json"), "r") as f:
111
+ manifest = json.load(f)
112
+ manifest["metadata"]["generated_at"] = "2000-01-01T00:00:00Z"
113
+ artifacts = ArtifactsRoot(
114
+ base=dict(
115
+ manifest=manifest,
116
+ ),
117
+ current=dict(
118
+ manifest=manifest,
119
+ ),
120
+ )
121
+
122
+ from tests.adapter.dbt_adapter.dbt_test_helper import DbtTestHelper
123
+
124
+ adapter = DbtTestHelper().adapter
125
+ adapter.import_artifacts(artifacts)
126
+ self.assertNotEqual(adapter.base_manifest.metadata.invocation_id, manifest.get("metadata").get("invocation_id"))
127
+
128
+ manifest["metadata"]["generated_at"] = "2099-01-01T00:00:00Z"
129
+ adapter.import_artifacts(artifacts)
130
+ self.assertEqual(adapter.base_manifest.metadata.invocation_id, manifest.get("metadata").get("invocation_id"))
131
+
132
+ def test_state_loader(self):
133
+ # copy ./recce_state.json to temp and open
134
+
135
+ # use library to create a temp file in the context
136
+ import os
137
+ import shutil
138
+ import tempfile
139
+
140
+ with tempfile.NamedTemporaryFile() as f:
141
+ # copy ./recce_state.json to temp file
142
+ current_dir = os.path.dirname(os.path.abspath(__file__))
143
+ state_file = os.path.join(current_dir, "recce_state.json")
144
+ shutil.copy(state_file, f.name)
145
+
146
+ # load the state file
147
+ state_loader = FileStateLoader(state_file=f.name)
148
+ state = state_loader.load()
149
+ assert len(state.runs) == 17
150
+
4
151
 
5
152
  def test_lineage_diff(dbt_test_helper):
6
153
  sql_model1 = """
tests/test_server.py CHANGED
@@ -31,9 +31,9 @@ def test_health():
31
31
 
32
32
  def test_stateless(dbt_test_helper):
33
33
  context = default_context()
34
- from recce.state import RecceStateLoader
34
+ from recce.state import FileStateLoader
35
35
 
36
- context.state_loader = RecceStateLoader()
36
+ context.state_loader = FileStateLoader()
37
37
  client = TestClient(app)
38
38
  response = client.get("/api/info")
39
39
  assert response.status_code == 200
@@ -44,9 +44,9 @@ def test_stateless(dbt_test_helper):
44
44
 
45
45
  def test_file_mode(dbt_test_helper):
46
46
  context = default_context()
47
- from recce.state import RecceStateLoader
47
+ from recce.state import FileStateLoader
48
48
 
49
- context.state_loader = RecceStateLoader(state_file="/tmp/recce_state.json")
49
+ context.state_loader = FileStateLoader(state_file="/tmp/recce_state.json")
50
50
  client = TestClient(app)
51
51
  response = client.get("/api/info")
52
52
  assert response.status_code == 200
@@ -63,9 +63,9 @@ def test_saveas_and_rename(dbt_test_helper, temp_folder):
63
63
  state_file3 = os.path.join(temp_folder, "recce_state3.json")
64
64
  os.makedirs(os.path.join(temp_folder, "dir.json"))
65
65
 
66
- from recce.state import RecceStateLoader
66
+ from recce.state import FileStateLoader
67
67
 
68
- context.state_loader = RecceStateLoader(state_file=state_file)
68
+ context.state_loader = FileStateLoader(state_file=state_file)
69
69
  client = TestClient(app)
70
70
 
71
71
  response = client.post("/api/save", json={"filename": "recce_state2.json"})