-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathsandbox_cleanup_test.py
More file actions
158 lines (140 loc) · 5.12 KB
/
Copy pathsandbox_cleanup_test.py
File metadata and controls
158 lines (140 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Cleanup status handling through the curated client's real gRPC interceptor."""
from concurrent import futures
from contextlib import nullcontext
from types import SimpleNamespace
import grpc
import pytest
from openshell._proto import openshell_pb2
from openshell.errors import GatewayError
from openshell.sandbox import Sandbox, SandboxClient
@pytest.fixture
def cleanup_client():
state = SimpleNamespace(
exists=False,
code=grpc.StatusCode.NOT_FOUND,
calls=[],
closed=[],
)
def fail(context):
context.set_trailing_metadata((("request-id", "cleanup-regression"),))
context.abort(state.code, "cleanup status")
def get(request, context):
state.calls.append("GetSandbox")
assert request.name == "cleanup-test"
assert request.workspace_scope.workspace == "default"
if not state.exists:
fail(context)
response = openshell_pb2.SandboxResponse()
response.sandbox.metadata.id = "sandbox-1"
response.sandbox.metadata.name = request.name
response.sandbox.metadata.workspace = "default"
response.sandbox.status.phase = openshell_pb2.SANDBOX_PHASE_READY
return response
def delete(request, context):
state.calls.append("DeleteSandbox")
assert request.name == "cleanup-test"
assert request.workspace_scope.workspace == "default"
assert request.allow_missing
if state.code == grpc.StatusCode.NOT_FOUND:
return openshell_pb2.DeleteSandboxResponse(
outcome=openshell_pb2.DELETION_OUTCOME_ALREADY_ABSENT
)
fail(context)
server = grpc.server(futures.ThreadPoolExecutor(max_workers=1))
server.add_generic_rpc_handlers(
(
grpc.method_handlers_generic_handler(
"openshell.v1.OpenShell",
{
"GetSandbox": grpc.unary_unary_rpc_method_handler(
get,
request_deserializer=openshell_pb2.GetSandboxRequest.FromString,
response_serializer=openshell_pb2.SandboxResponse.SerializeToString,
),
"DeleteSandbox": grpc.unary_unary_rpc_method_handler(
delete,
request_deserializer=openshell_pb2.DeleteSandboxRequest.FromString,
response_serializer=openshell_pb2.DeleteSandboxResponse.SerializeToString,
),
},
),
)
)
port = server.add_insecure_port("127.0.0.1:0")
server.start()
client = SandboxClient(
f"127.0.0.1:{port}",
timeout=5,
_bearer_close=lambda: state.closed.append(True),
)
try:
yield client, state
finally:
client.close()
server.stop(0).wait()
def assert_original_call(error, code):
assert isinstance(error, GatewayError)
assert isinstance(error.raw_error, grpc.Call)
assert error.code() == error.raw_error.code() == code
assert error.details() == "cleanup status"
assert ("request-id", "cleanup-regression") in error.trailing_metadata()
@pytest.mark.parametrize(
"code",
[
grpc.StatusCode.NOT_FOUND,
grpc.StatusCode.PERMISSION_DENIED,
grpc.StatusCode.UNAVAILABLE,
],
)
def test_wait_deleted_handles_intercepted_status(cleanup_client, code):
client, state = cleanup_client
state.code = code
# Prove this is the curated intercepted stub, not a fake raw exception.
with pytest.raises(GatewayError) as observed:
client.get("cleanup-test", workspace="default")
assert_original_call(observed.value, code)
expected = (
nullcontext()
if code == grpc.StatusCode.NOT_FOUND
else pytest.raises(GatewayError)
)
with expected as caught:
client.wait_deleted("cleanup-test", workspace="default", timeout_seconds=5)
if caught is not None:
assert_original_call(caught.value, code)
assert state.calls == ["GetSandbox", "GetSandbox"]
@pytest.mark.parametrize(
"code",
[
grpc.StatusCode.NOT_FOUND,
grpc.StatusCode.PERMISSION_DENIED,
grpc.StatusCode.UNAVAILABLE,
],
)
def test_context_cleanup_handles_absence_and_intercepted_errors(
cleanup_client, monkeypatch, code
):
client, state = cleanup_client
state.code = code
state.exists = True
monkeypatch.setattr(
SandboxClient,
"from_active_cluster",
classmethod(lambda _cls, **_kwargs: client),
)
managed = Sandbox(workspace="default", sandbox="cleanup-test")
expected = (
nullcontext()
if code == grpc.StatusCode.NOT_FOUND
else pytest.raises(GatewayError)
)
with expected as caught, managed:
state.exists = False
if caught is not None:
assert_original_call(caught.value, code)
assert state.calls == ["GetSandbox", "GetSandbox", "DeleteSandbox"]
assert state.closed == [True]
assert managed._client is None
assert managed._session is None