recce-nightly 1.20.0.20250921__py3-none-any.whl → 1.20.0.20250923__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.
- recce/VERSION +1 -1
- recce/adapter/dbt_adapter/__init__.py +1 -1
- recce/artifact.py +2 -2
- recce/cli.py +221 -0
- recce/config.py +2 -2
- recce/connect_to_cloud.py +1 -1
- recce/data/404.html +1 -1
- recce/data/index.html +1 -1
- recce/data/index.txt +1 -1
- recce/event/__init__.py +6 -6
- recce/github.py +1 -1
- recce/pull_request.py +1 -1
- recce/run.py +2 -2
- recce/server.py +1 -1
- recce/util/io.py +2 -2
- recce/util/recce_cloud.py +39 -0
- recce/yaml/__init__.py +2 -2
- {recce_nightly-1.20.0.20250921.dist-info → recce_nightly-1.20.0.20250923.dist-info}/METADATA +1 -1
- {recce_nightly-1.20.0.20250921.dist-info → recce_nightly-1.20.0.20250923.dist-info}/RECORD +26 -25
- tests/test_cloud_listing_cli.py +324 -0
- /recce/data/_next/static/{7VDijMrfP7uONDsXVHgCK → LTOgqhm_v-aMoQq1O-5iu}/_buildManifest.js +0 -0
- /recce/data/_next/static/{7VDijMrfP7uONDsXVHgCK → LTOgqhm_v-aMoQq1O-5iu}/_ssgManifest.js +0 -0
- {recce_nightly-1.20.0.20250921.dist-info → recce_nightly-1.20.0.20250923.dist-info}/WHEEL +0 -0
- {recce_nightly-1.20.0.20250921.dist-info → recce_nightly-1.20.0.20250923.dist-info}/entry_points.txt +0 -0
- {recce_nightly-1.20.0.20250921.dist-info → recce_nightly-1.20.0.20250923.dist-info}/licenses/LICENSE +0 -0
- {recce_nightly-1.20.0.20250921.dist-info → recce_nightly-1.20.0.20250923.dist-info}/top_level.txt +0 -0
recce/yaml/__init__.py
CHANGED
|
@@ -34,7 +34,7 @@ def dump(data, stream: Any = None, *, transform: Any = None) -> Any:
|
|
|
34
34
|
|
|
35
35
|
def safe_load_yaml(file_path):
|
|
36
36
|
try:
|
|
37
|
-
with open(file_path, "r") as f:
|
|
37
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
38
38
|
payload = safe_load(f)
|
|
39
39
|
except yaml.YAMLError as e:
|
|
40
40
|
print(e)
|
|
@@ -45,7 +45,7 @@ def safe_load_yaml(file_path):
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def round_trip_load_yaml(file_path):
|
|
48
|
-
with open(file_path, "r") as f:
|
|
48
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
49
49
|
try:
|
|
50
50
|
payload = load(f)
|
|
51
51
|
except yaml.YAMLError as e:
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
recce/VERSION,sha256=
|
|
1
|
+
recce/VERSION,sha256=LC4SLqnM58gc6CdkHFRQ-pn57pr6H046h5J_jtKuDRw,16
|
|
2
2
|
recce/__init__.py,sha256=yNb0QT-yoStex0VZALNJvUwtPLommoVCStcow31guqo,2392
|
|
3
|
-
recce/artifact.py,sha256=
|
|
4
|
-
recce/cli.py,sha256=
|
|
5
|
-
recce/config.py,sha256=
|
|
6
|
-
recce/connect_to_cloud.py,sha256=
|
|
3
|
+
recce/artifact.py,sha256=BdNW5eQQqEKu5cRzA_XusKMIN73V7-MTQP0VxnLdd9E,9265
|
|
4
|
+
recce/cli.py,sha256=ws50nwIzWvYcLNPDfcI6N69k5DaaIONKxloqa-5UdE8,52989
|
|
5
|
+
recce/config.py,sha256=noWimlOdGHAtBhNoApK5gRTQMwNPmUGAmZu39MV7szo,4697
|
|
6
|
+
recce/connect_to_cloud.py,sha256=CT7ELcVCTNa9ukSp_gMrJ0Av2UCUpF9H03tIk9y7U-4,4768
|
|
7
7
|
recce/core.py,sha256=MtBWxemvCQDdUITwkU2JyuQYqcjlA0a76M8Kg53ECxQ,10754
|
|
8
8
|
recce/diff.py,sha256=L2_bzQ3__PO-0aeir8PHF8FvSOUmQ8WcDXgML1-mHdY,748
|
|
9
9
|
recce/exceptions.py,sha256=SclQ678GrHGjw7p_ZFJ3vZaL_yMU5xABeIAm2u_W2bk,592
|
|
10
10
|
recce/git.py,sha256=8Eg-6NzL-KjA3rT-ibbAyaCwGlzV0JqH3yGikrJNMDA,2344
|
|
11
|
-
recce/github.py,sha256=
|
|
12
|
-
recce/pull_request.py,sha256=
|
|
13
|
-
recce/run.py,sha256=
|
|
14
|
-
recce/server.py,sha256=
|
|
11
|
+
recce/github.py,sha256=vIwHTpB8YWEUcHQlW0nToUUKmGGO0NcmB_Q5RvxQLLE,7438
|
|
12
|
+
recce/pull_request.py,sha256=dLHsdiMtL4NC6Vkwp8Mq4baJmnqbm19lfiDti9MREN0,3850
|
|
13
|
+
recce/run.py,sha256=ctp-K2FmrsGrS4xEZKDMbMtitNw2QKJ6Tq_0OvCWaaM,14080
|
|
14
|
+
recce/server.py,sha256=Q9Uc_YV8pgE0txZB9eVB9N1CD2HWpj2sZZxnhtQu_aQ,22748
|
|
15
15
|
recce/summary.py,sha256=Mbxvxr9KazR5o9icqhhjiGHsoAiWxQU4PdN7HytBJ1c,19154
|
|
16
16
|
recce/adapter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
recce/adapter/base.py,sha256=T_JNeLHgiHSaegw-DbrvHOaYjMyZcjj2Qtg5cWh_fco,3548
|
|
18
18
|
recce/adapter/sqlmesh_adapter.py,sha256=IU3N-F6ToDoO7_bV5vsG8pmTuDcbFtewTIuCxedTaRM,5046
|
|
19
|
-
recce/adapter/dbt_adapter/__init__.py,sha256=
|
|
19
|
+
recce/adapter/dbt_adapter/__init__.py,sha256=o7OowlYfuE1jBaHxrJ5PyiEBmK4OpdpscwxvtpMIG_M,66840
|
|
20
20
|
recce/adapter/dbt_adapter/dbt_version.py,sha256=M7aedZIWslXnJsryK8Ki4OL_t2oAKxy4uE2pRwfWIkk,1228
|
|
21
21
|
recce/apis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
recce/apis/check_api.py,sha256=KMCXSMl1qqzx2jQgRqCrD4j_cY3EHBbM3H2-t-6saAU,6227
|
|
23
23
|
recce/apis/check_func.py,sha256=gktbCcyk3WGvWRJJ-wDnwv7NrIny2nTHWLl1-kdiVRo,4183
|
|
24
24
|
recce/apis/run_api.py,sha256=eOaxOxXDkH59uqGCd4blld7edavUx7JU_DCd2WAYrL8,3416
|
|
25
25
|
recce/apis/run_func.py,sha256=6wC8TDU-h7TLr2VZH7HNsWaUVlQ9HBN5N_dwqfi4lMY,7440
|
|
26
|
-
recce/data/404.html,sha256=
|
|
26
|
+
recce/data/404.html,sha256=6mltyQJi7pTYZbF9VZ_LVbuEEIx4EqPcevuyQzk_m2I,59087
|
|
27
27
|
recce/data/auth_callback.html,sha256=H-XfdlAFiw5VU2RpKBVQbwh1AIqJrPHrFA0S09nNJZA,94779
|
|
28
28
|
recce/data/favicon.ico,sha256=B2mBumUOnzvUrXrqNkrc5QfdDXjzEXRcWkWur0fJ6sM,2565
|
|
29
|
-
recce/data/index.html,sha256=
|
|
30
|
-
recce/data/index.txt,sha256=
|
|
31
|
-
recce/data/_next/static/
|
|
32
|
-
recce/data/_next/static/
|
|
29
|
+
recce/data/index.html,sha256=Hdeb5Tj_3sUXetH9GIm9ItYjkN5xuscWkOuE9UcJNGE,77450
|
|
30
|
+
recce/data/index.txt,sha256=swQe8QCp9jRzQM1tyG0UmH6c_nwKaneHnjyRgH-gzrk,6409
|
|
31
|
+
recce/data/_next/static/LTOgqhm_v-aMoQq1O-5iu/_buildManifest.js,sha256=avQsL1oPzYwcdZvar5cABpd9QZyaNp5U46Y9ET0fq24,544
|
|
32
|
+
recce/data/_next/static/LTOgqhm_v-aMoQq1O-5iu/_ssgManifest.js,sha256=Z49s4suAsf5y_GfnQSvm4qtq2ggxEbZPfEDTXjy6XgA,80
|
|
33
33
|
recce/data/_next/static/chunks/068b80ea-5225768a06b94b6f.js,sha256=9-CgOMA67KPg0s3LBhBMLS7nl5icw5BMDhs8Zk-TjO4,4628
|
|
34
34
|
recce/data/_next/static/chunks/0ddaf06c-47655700b71b89f7.js,sha256=ZAiDuPV8ad-3MSwYva4LCOLRFOyaSENn2vBB6ZSU3So,696
|
|
35
35
|
recce/data/_next/static/chunks/12f8fac4-10d6e05f7330d12b.js,sha256=79b2y12TyKWM49nc8dsZRwojyWcC7Yz-P7-f3wBfy9s,152792
|
|
@@ -87,7 +87,7 @@ recce/data/imgs/feedback/thumbs-up.png,sha256=VF3BH8bmYEqcSsMDJO57xMqW4t6crCXUXa
|
|
|
87
87
|
recce/data/logo/recce-logo-white.png,sha256=y3re8iEucJnMUkAkRS2CjWHTlOydyvgWdWjuQKcXDbk,46923
|
|
88
88
|
recce/event/CONFIG,sha256=w8_AVcNu_JF-t8lNmjOqtsXbeOawMzpEhkISaMlm-iU,48
|
|
89
89
|
recce/event/SENTRY_DNS,sha256=nWXZevLC4qDScvNtjs329X13oxqvtFI_Kiu6cfCDOBA,83
|
|
90
|
-
recce/event/__init__.py,sha256=
|
|
90
|
+
recce/event/__init__.py,sha256=gf8c9cZaAW6nx0hmvQWeSYTmM1uICHn25qW81t9eNTY,8721
|
|
91
91
|
recce/event/collector.py,sha256=Tqe5nMAMvDveaJ1_AfGaq23y5ccoRqhIC1-fTdVZAVg,5720
|
|
92
92
|
recce/event/track.py,sha256=3wBzpB8l2gtnDzjh-wJatmLeBbtnP0q-BLQZQVQ23sM,5310
|
|
93
93
|
recce/models/__init__.py,sha256=F7cgALtdWnwv37R0eEgKZ_yBsMwxWnUfo3jAZ3u6qyU,209
|
|
@@ -116,18 +116,19 @@ recce/util/api_token.py,sha256=NE8lIFcr2GZ6ft4FS3zcsbvZaFdBF-e-fMYAYL2PDkM,2938
|
|
|
116
116
|
recce/util/breaking.py,sha256=ajCOWMR8Qw492wCiuz5_DKqpI3uoj_qHuJjvelEoSkw,13068
|
|
117
117
|
recce/util/cache.py,sha256=QB6wzxe0M3jNTwP0M27Ys8F2hF-oda4-LyXXG9THuZQ,646
|
|
118
118
|
recce/util/cll.py,sha256=p8BrsG_1Lm-jXO8n4ulUgFw13C4mN0WjCXZbihDxQrs,13253
|
|
119
|
-
recce/util/io.py,sha256=
|
|
119
|
+
recce/util/io.py,sha256=UTUDncG4habAZZSZ1Y7i7Zpzu6FBtsfWI3y96k-0zkc,3495
|
|
120
120
|
recce/util/lineage.py,sha256=0XXOO05eJ3GPUNX9V9GBQS2iRFXxWZJoJbV7w-jQ0t8,2470
|
|
121
121
|
recce/util/logger.py,sha256=6UgLFkRiur9jJfu2ZRdo4LUvMw4f75V-l-1HT1-sgKo,747
|
|
122
122
|
recce/util/onboarding_state.py,sha256=kwFirKlfXdl5WFkR_nmilqGKFyELNcSPMqYq-by35fk,1991
|
|
123
123
|
recce/util/perf_tracking.py,sha256=FjhrdbbXIgybxS_oPpFsJ9VUDR93d7bUs8VNNqpXNxw,2483
|
|
124
124
|
recce/util/pydantic_model.py,sha256=KumKuyCjbTzEMsKLE4-b-eZfp0gLhYDdmVtw1-hxiJw,587
|
|
125
|
-
recce/util/recce_cloud.py,sha256=
|
|
125
|
+
recce/util/recce_cloud.py,sha256=kr0PnnSltLP6Y5RLwsqTMwDBC2u5fdnhYbVQgX9i86A,16622
|
|
126
126
|
recce/util/singleton.py,sha256=1cU99I0f9tjuMQLMJyLsK1oK3fZJMsO5-TbRHAMXqds,627
|
|
127
|
-
recce/yaml/__init__.py,sha256=
|
|
128
|
-
recce_nightly-1.20.0.
|
|
127
|
+
recce/yaml/__init__.py,sha256=PAym5akbtL24Ag7VR7EW8SS2VINNaJU06esbfe-ek-U,1328
|
|
128
|
+
recce_nightly-1.20.0.20250923.dist-info/licenses/LICENSE,sha256=CQjjMy9aYPhfe8xG_bcpIfKtNkdxLZ5IOb8oPygtUhY,11343
|
|
129
129
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
130
130
|
tests/test_cli.py,sha256=CFtP4eJrtcHwpzoEgJAyveQcqxAmDQuWXPrLc_Iny7Q,6626
|
|
131
|
+
tests/test_cloud_listing_cli.py,sha256=77As9n0Sngdh53dfHkShmqDETMTOohlqw1xXO9cIago,14208
|
|
131
132
|
tests/test_config.py,sha256=ODDFe_XF6gphmSmmc422dGLBaCCmG-IjDzTkD5SJsJE,1557
|
|
132
133
|
tests/test_connect_to_cloud.py,sha256=b2fgV8L1iQBdEwh6RumMsIIyYg7GtMOMRz1dvE3WRPg,3059
|
|
133
134
|
tests/test_core.py,sha256=WgCFm8Au3YQI-V5UbZ6LA8irMNHRc7NWutIq2Mny0LE,6242
|
|
@@ -153,8 +154,8 @@ tests/tasks/test_row_count.py,sha256=21PaP2aq-x8-pqwzWHRT1sixhQ8g3CQNRWOZTTmbK0s
|
|
|
153
154
|
tests/tasks/test_schema.py,sha256=7ds4Vx8ixaiIWDR49Lvjem4xlPkRP1cXazDRY3roUak,3121
|
|
154
155
|
tests/tasks/test_top_k.py,sha256=YR_GS__DJsbDlQVaEEdJvNQ3fh1VmV5Nb3G7lb0r6YM,1779
|
|
155
156
|
tests/tasks/test_valuediff.py,sha256=_xQJGgxsXoy2NYk_Z6Hsw2FlVh6zk2nN_iUueyRN1e8,2046
|
|
156
|
-
recce_nightly-1.20.0.
|
|
157
|
-
recce_nightly-1.20.0.
|
|
158
|
-
recce_nightly-1.20.0.
|
|
159
|
-
recce_nightly-1.20.0.
|
|
160
|
-
recce_nightly-1.20.0.
|
|
157
|
+
recce_nightly-1.20.0.20250923.dist-info/METADATA,sha256=Na1ylrZzvUzX_-9KeofiPt7Hgv24XPVT1ygkRFRsyP4,9366
|
|
158
|
+
recce_nightly-1.20.0.20250923.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
159
|
+
recce_nightly-1.20.0.20250923.dist-info/entry_points.txt,sha256=oqoY_IiwIqXbgrIsPnlqUqao2eiIeP2dprowkOlmeyg,40
|
|
160
|
+
recce_nightly-1.20.0.20250923.dist-info/top_level.txt,sha256=6PKGVpf75idP0C6KEaldDzzZUauIxNu1ZDstau1pI4I,12
|
|
161
|
+
recce_nightly-1.20.0.20250923.dist-info/RECORD,,
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import Mock, patch
|
|
3
|
+
|
|
4
|
+
from click.testing import CliRunner
|
|
5
|
+
|
|
6
|
+
from recce.cli import list_organizations, list_projects, list_sessions
|
|
7
|
+
from recce.exceptions import RecceConfigException
|
|
8
|
+
from recce.util.recce_cloud import RecceCloudException
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestCloudListingCLI(unittest.TestCase):
|
|
12
|
+
"""Test cases for the cloud listing CLI commands."""
|
|
13
|
+
|
|
14
|
+
def setUp(self):
|
|
15
|
+
"""Set up test fixtures."""
|
|
16
|
+
self.runner = CliRunner()
|
|
17
|
+
|
|
18
|
+
@patch("recce.cli.prepare_api_token")
|
|
19
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
20
|
+
def test_list_organizations_success(self, mock_recce_cloud, mock_prepare_token):
|
|
21
|
+
"""Test successful list-organizations command."""
|
|
22
|
+
# Setup mocks
|
|
23
|
+
mock_prepare_token.return_value = "test-token"
|
|
24
|
+
mock_cloud_instance = Mock()
|
|
25
|
+
mock_cloud_instance.list_organizations.return_value = [
|
|
26
|
+
{"id": 1, "name": "org1", "display_name": "Organization 1"},
|
|
27
|
+
{"id": 2, "name": "org2", "display_name": "Organization 2"},
|
|
28
|
+
]
|
|
29
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
30
|
+
|
|
31
|
+
# Test command
|
|
32
|
+
result = self.runner.invoke(list_organizations, [])
|
|
33
|
+
|
|
34
|
+
# Assertions
|
|
35
|
+
self.assertEqual(result.exit_code, 0)
|
|
36
|
+
self.assertIn("Organizations", result.output)
|
|
37
|
+
self.assertIn("org1", result.output)
|
|
38
|
+
self.assertIn("Organization 1", result.output)
|
|
39
|
+
self.assertIn("org2", result.output)
|
|
40
|
+
self.assertIn("Organization 2", result.output)
|
|
41
|
+
mock_cloud_instance.list_organizations.assert_called_once()
|
|
42
|
+
|
|
43
|
+
@patch("recce.cli.prepare_api_token")
|
|
44
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
45
|
+
def test_list_organizations_empty(self, mock_recce_cloud, mock_prepare_token):
|
|
46
|
+
"""Test list-organizations command with no organizations."""
|
|
47
|
+
# Setup mocks
|
|
48
|
+
mock_prepare_token.return_value = "test-token"
|
|
49
|
+
mock_cloud_instance = Mock()
|
|
50
|
+
mock_cloud_instance.list_organizations.return_value = []
|
|
51
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
52
|
+
|
|
53
|
+
# Test command
|
|
54
|
+
result = self.runner.invoke(list_organizations, [])
|
|
55
|
+
|
|
56
|
+
# Assertions
|
|
57
|
+
self.assertEqual(result.exit_code, 0)
|
|
58
|
+
self.assertIn("No organizations found", result.output)
|
|
59
|
+
|
|
60
|
+
@patch("recce.cli.prepare_api_token")
|
|
61
|
+
def test_list_organizations_invalid_token(self, mock_prepare_token):
|
|
62
|
+
"""Test list-organizations command with invalid token."""
|
|
63
|
+
# Setup mock to raise exception
|
|
64
|
+
mock_prepare_token.side_effect = RecceConfigException("Invalid token")
|
|
65
|
+
|
|
66
|
+
# Test command
|
|
67
|
+
result = self.runner.invoke(list_organizations, [])
|
|
68
|
+
|
|
69
|
+
# Assertions
|
|
70
|
+
self.assertEqual(result.exit_code, 1)
|
|
71
|
+
|
|
72
|
+
@patch("recce.cli.prepare_api_token")
|
|
73
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
74
|
+
def test_list_projects_with_cli_arg(self, mock_recce_cloud, mock_prepare_token):
|
|
75
|
+
"""Test list-projects command with CLI argument."""
|
|
76
|
+
# Setup mocks
|
|
77
|
+
mock_prepare_token.return_value = "test-token"
|
|
78
|
+
mock_cloud_instance = Mock()
|
|
79
|
+
mock_cloud_instance.list_projects.return_value = [
|
|
80
|
+
{"id": 1, "name": "project1", "display_name": "Project 1"},
|
|
81
|
+
{"id": 2, "name": "project2", "display_name": "Project 2"},
|
|
82
|
+
]
|
|
83
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
84
|
+
|
|
85
|
+
# Test command with CLI argument
|
|
86
|
+
result = self.runner.invoke(list_projects, ["--organization", "8"])
|
|
87
|
+
|
|
88
|
+
# Assertions
|
|
89
|
+
self.assertEqual(result.exit_code, 0)
|
|
90
|
+
self.assertIn("Projects in Organization 8", result.output)
|
|
91
|
+
self.assertIn("project1", result.output)
|
|
92
|
+
self.assertIn("Project 1", result.output)
|
|
93
|
+
mock_cloud_instance.list_projects.assert_called_once_with("8")
|
|
94
|
+
|
|
95
|
+
@patch("recce.cli.prepare_api_token")
|
|
96
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
97
|
+
def test_list_projects_with_env_var(self, mock_recce_cloud, mock_prepare_token):
|
|
98
|
+
"""Test list-projects command with environment variable."""
|
|
99
|
+
# Setup mocks
|
|
100
|
+
mock_prepare_token.return_value = "test-token"
|
|
101
|
+
mock_cloud_instance = Mock()
|
|
102
|
+
mock_cloud_instance.list_projects.return_value = [{"id": 1, "name": "project1", "display_name": "Project 1"}]
|
|
103
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
104
|
+
|
|
105
|
+
# Test command with environment variable
|
|
106
|
+
result = self.runner.invoke(list_projects, [], env={"RECCE_ORGANIZATION_ID": "8"})
|
|
107
|
+
|
|
108
|
+
# Assertions
|
|
109
|
+
self.assertEqual(result.exit_code, 0)
|
|
110
|
+
self.assertIn("Projects in Organization 8", result.output)
|
|
111
|
+
self.assertIn("project1", result.output)
|
|
112
|
+
mock_cloud_instance.list_projects.assert_called_once_with("8")
|
|
113
|
+
|
|
114
|
+
@patch("recce.cli.prepare_api_token")
|
|
115
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
116
|
+
def test_list_projects_cli_overrides_env(self, mock_recce_cloud, mock_prepare_token):
|
|
117
|
+
"""Test list-projects command where CLI argument overrides environment variable."""
|
|
118
|
+
# Setup mocks
|
|
119
|
+
mock_prepare_token.return_value = "test-token"
|
|
120
|
+
mock_cloud_instance = Mock()
|
|
121
|
+
mock_cloud_instance.list_projects.return_value = [{"id": 1, "name": "project1", "display_name": "Project 1"}]
|
|
122
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
123
|
+
|
|
124
|
+
# Test command with both env var and CLI arg (CLI should win)
|
|
125
|
+
result = self.runner.invoke(list_projects, ["--organization", "10"], env={"RECCE_ORGANIZATION_ID": "8"})
|
|
126
|
+
|
|
127
|
+
# Assertions
|
|
128
|
+
self.assertEqual(result.exit_code, 0)
|
|
129
|
+
self.assertIn("Projects in Organization 10", result.output)
|
|
130
|
+
# Verify CLI argument (10) was used, not env var (8)
|
|
131
|
+
mock_cloud_instance.list_projects.assert_called_once_with("10")
|
|
132
|
+
|
|
133
|
+
@patch("recce.cli.prepare_api_token")
|
|
134
|
+
def test_list_projects_missing_organization(self, mock_prepare_token):
|
|
135
|
+
"""Test list-projects command with missing organization ID."""
|
|
136
|
+
# Setup mocks
|
|
137
|
+
mock_prepare_token.return_value = "test-token"
|
|
138
|
+
|
|
139
|
+
# Test command without organization ID
|
|
140
|
+
result = self.runner.invoke(list_projects, [])
|
|
141
|
+
|
|
142
|
+
# Assertions
|
|
143
|
+
self.assertEqual(result.exit_code, 1)
|
|
144
|
+
self.assertIn("Organization ID is required", result.output)
|
|
145
|
+
self.assertIn("--organization", result.output)
|
|
146
|
+
self.assertIn("RECCE_ORGANIZATION_ID", result.output)
|
|
147
|
+
|
|
148
|
+
@patch("recce.cli.prepare_api_token")
|
|
149
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
150
|
+
def test_list_projects_empty(self, mock_recce_cloud, mock_prepare_token):
|
|
151
|
+
"""Test list-projects command with no projects."""
|
|
152
|
+
# Setup mocks
|
|
153
|
+
mock_prepare_token.return_value = "test-token"
|
|
154
|
+
mock_cloud_instance = Mock()
|
|
155
|
+
mock_cloud_instance.list_projects.return_value = []
|
|
156
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
157
|
+
|
|
158
|
+
# Test command
|
|
159
|
+
result = self.runner.invoke(list_projects, ["--organization", "8"])
|
|
160
|
+
|
|
161
|
+
# Assertions
|
|
162
|
+
self.assertEqual(result.exit_code, 0)
|
|
163
|
+
self.assertIn("No projects found in organization 8", result.output)
|
|
164
|
+
|
|
165
|
+
@patch("recce.cli.prepare_api_token")
|
|
166
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
167
|
+
def test_list_sessions_with_cli_args(self, mock_recce_cloud, mock_prepare_token):
|
|
168
|
+
"""Test list-sessions command with CLI arguments."""
|
|
169
|
+
# Setup mocks
|
|
170
|
+
mock_prepare_token.return_value = "test-token"
|
|
171
|
+
mock_cloud_instance = Mock()
|
|
172
|
+
mock_cloud_instance.list_sessions.return_value = [
|
|
173
|
+
{"id": "session1", "name": "PR-123", "is_base": False},
|
|
174
|
+
{"id": "session2", "name": "Base Session", "is_base": True},
|
|
175
|
+
]
|
|
176
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
177
|
+
|
|
178
|
+
# Test command with CLI arguments
|
|
179
|
+
result = self.runner.invoke(list_sessions, ["--organization", "8", "--project", "7"])
|
|
180
|
+
|
|
181
|
+
# Assertions
|
|
182
|
+
self.assertEqual(result.exit_code, 0)
|
|
183
|
+
self.assertIn("Sessions in Project 7", result.output)
|
|
184
|
+
self.assertIn("PR-123", result.output)
|
|
185
|
+
self.assertIn("Base Session", result.output)
|
|
186
|
+
self.assertIn("✓", result.output) # Base session marker
|
|
187
|
+
mock_cloud_instance.list_sessions.assert_called_once_with("8", "7")
|
|
188
|
+
|
|
189
|
+
@patch("recce.cli.prepare_api_token")
|
|
190
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
191
|
+
def test_list_sessions_with_env_vars(self, mock_recce_cloud, mock_prepare_token):
|
|
192
|
+
"""Test list-sessions command with environment variables."""
|
|
193
|
+
# Setup mocks
|
|
194
|
+
mock_prepare_token.return_value = "test-token"
|
|
195
|
+
mock_cloud_instance = Mock()
|
|
196
|
+
mock_cloud_instance.list_sessions.return_value = [{"id": "session1", "name": "Session 1", "is_base": False}]
|
|
197
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
198
|
+
|
|
199
|
+
# Test command with environment variables
|
|
200
|
+
result = self.runner.invoke(list_sessions, [], env={"RECCE_ORGANIZATION_ID": "8", "RECCE_PROJECT_ID": "7"})
|
|
201
|
+
|
|
202
|
+
# Assertions
|
|
203
|
+
self.assertEqual(result.exit_code, 0)
|
|
204
|
+
self.assertIn("Sessions in Project 7", result.output)
|
|
205
|
+
self.assertIn("Session 1", result.output)
|
|
206
|
+
mock_cloud_instance.list_sessions.assert_called_once_with("8", "7")
|
|
207
|
+
|
|
208
|
+
@patch("recce.cli.prepare_api_token")
|
|
209
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
210
|
+
def test_list_sessions_mixed_env_and_cli(self, mock_recce_cloud, mock_prepare_token):
|
|
211
|
+
"""Test list-sessions command with mixed environment variables and CLI args."""
|
|
212
|
+
# Setup mocks
|
|
213
|
+
mock_prepare_token.return_value = "test-token"
|
|
214
|
+
mock_cloud_instance = Mock()
|
|
215
|
+
mock_cloud_instance.list_sessions.return_value = [{"id": "session1", "name": "Session 1", "is_base": False}]
|
|
216
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
217
|
+
|
|
218
|
+
# Test command with env var for org and CLI arg for project
|
|
219
|
+
result = self.runner.invoke(list_sessions, ["--project", "9"], env={"RECCE_ORGANIZATION_ID": "8"})
|
|
220
|
+
|
|
221
|
+
# Assertions
|
|
222
|
+
self.assertEqual(result.exit_code, 0)
|
|
223
|
+
self.assertIn("Sessions in Project 9", result.output)
|
|
224
|
+
# Verify it used env var for org (8) and CLI arg for project (9)
|
|
225
|
+
mock_cloud_instance.list_sessions.assert_called_once_with("8", "9")
|
|
226
|
+
|
|
227
|
+
@patch("recce.cli.prepare_api_token")
|
|
228
|
+
def test_list_sessions_missing_organization(self, mock_prepare_token):
|
|
229
|
+
"""Test list-sessions command with missing organization ID."""
|
|
230
|
+
# Setup mocks
|
|
231
|
+
mock_prepare_token.return_value = "test-token"
|
|
232
|
+
|
|
233
|
+
# Test command without organization ID
|
|
234
|
+
result = self.runner.invoke(list_sessions, ["--project", "7"])
|
|
235
|
+
|
|
236
|
+
# Assertions
|
|
237
|
+
self.assertEqual(result.exit_code, 1)
|
|
238
|
+
self.assertIn("Organization ID is required", result.output)
|
|
239
|
+
self.assertIn("--organization", result.output)
|
|
240
|
+
self.assertIn("RECCE_ORGANIZATION_ID", result.output)
|
|
241
|
+
|
|
242
|
+
@patch("recce.cli.prepare_api_token")
|
|
243
|
+
def test_list_sessions_missing_project(self, mock_prepare_token):
|
|
244
|
+
"""Test list-sessions command with missing project ID."""
|
|
245
|
+
# Setup mocks
|
|
246
|
+
mock_prepare_token.return_value = "test-token"
|
|
247
|
+
|
|
248
|
+
# Test command without project ID
|
|
249
|
+
result = self.runner.invoke(list_sessions, ["--organization", "8"])
|
|
250
|
+
|
|
251
|
+
# Assertions
|
|
252
|
+
self.assertEqual(result.exit_code, 1)
|
|
253
|
+
self.assertIn("Project ID is required", result.output)
|
|
254
|
+
self.assertIn("--project", result.output)
|
|
255
|
+
self.assertIn("RECCE_PROJECT_ID", result.output)
|
|
256
|
+
|
|
257
|
+
@patch("recce.cli.prepare_api_token")
|
|
258
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
259
|
+
def test_list_sessions_empty(self, mock_recce_cloud, mock_prepare_token):
|
|
260
|
+
"""Test list-sessions command with no sessions."""
|
|
261
|
+
# Setup mocks
|
|
262
|
+
mock_prepare_token.return_value = "test-token"
|
|
263
|
+
mock_cloud_instance = Mock()
|
|
264
|
+
mock_cloud_instance.list_sessions.return_value = []
|
|
265
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
266
|
+
|
|
267
|
+
# Test command
|
|
268
|
+
result = self.runner.invoke(list_sessions, ["--organization", "8", "--project", "7"])
|
|
269
|
+
|
|
270
|
+
# Assertions
|
|
271
|
+
self.assertEqual(result.exit_code, 0)
|
|
272
|
+
self.assertIn("No sessions found in project 7", result.output)
|
|
273
|
+
|
|
274
|
+
@patch("recce.cli.prepare_api_token")
|
|
275
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
276
|
+
def test_list_sessions_api_error(self, mock_recce_cloud, mock_prepare_token):
|
|
277
|
+
"""Test list-sessions command with API error."""
|
|
278
|
+
# Setup mocks
|
|
279
|
+
mock_prepare_token.return_value = "test-token"
|
|
280
|
+
mock_cloud_instance = Mock()
|
|
281
|
+
mock_cloud_instance.list_sessions.side_effect = RecceCloudException("Access denied", "Forbidden", 403)
|
|
282
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
283
|
+
|
|
284
|
+
# Test command
|
|
285
|
+
result = self.runner.invoke(list_sessions, ["--organization", "8", "--project", "7"])
|
|
286
|
+
|
|
287
|
+
# Assertions
|
|
288
|
+
self.assertEqual(result.exit_code, 1)
|
|
289
|
+
self.assertIn("Error", result.output)
|
|
290
|
+
|
|
291
|
+
@patch("recce.cli.prepare_api_token")
|
|
292
|
+
@patch("recce.util.recce_cloud.RecceCloud")
|
|
293
|
+
def test_sessions_base_session_display(self, mock_recce_cloud, mock_prepare_token):
|
|
294
|
+
"""Test that base sessions are properly marked with checkmark."""
|
|
295
|
+
# Setup mocks
|
|
296
|
+
mock_prepare_token.return_value = "test-token"
|
|
297
|
+
mock_cloud_instance = Mock()
|
|
298
|
+
mock_cloud_instance.list_sessions.return_value = [
|
|
299
|
+
{"id": "session1", "name": "Regular Session", "is_base": False},
|
|
300
|
+
{"id": "session2", "name": "Base Session", "is_base": True},
|
|
301
|
+
{"id": "session3", "name": "Another Regular", "is_base": False},
|
|
302
|
+
]
|
|
303
|
+
mock_recce_cloud.return_value = mock_cloud_instance
|
|
304
|
+
|
|
305
|
+
# Test command
|
|
306
|
+
result = self.runner.invoke(list_sessions, ["--organization", "8", "--project", "7"])
|
|
307
|
+
|
|
308
|
+
# Assertions
|
|
309
|
+
self.assertEqual(result.exit_code, 0)
|
|
310
|
+
output_lines = result.output.split("\n")
|
|
311
|
+
|
|
312
|
+
# Find the base session line and verify it has the checkmark
|
|
313
|
+
base_session_line = None
|
|
314
|
+
for line in output_lines:
|
|
315
|
+
if "Base Session" in line:
|
|
316
|
+
base_session_line = line
|
|
317
|
+
break
|
|
318
|
+
|
|
319
|
+
self.assertIsNotNone(base_session_line)
|
|
320
|
+
self.assertIn("✓", base_session_line)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
if __name__ == "__main__":
|
|
324
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{recce_nightly-1.20.0.20250921.dist-info → recce_nightly-1.20.0.20250923.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{recce_nightly-1.20.0.20250921.dist-info → recce_nightly-1.20.0.20250923.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{recce_nightly-1.20.0.20250921.dist-info → recce_nightly-1.20.0.20250923.dist-info}/top_level.txt
RENAMED
|
File without changes
|