Skip to content

Commit 0b9c41c

Browse files
authored
Merge pull request #533 from dcs4cop/forman-xxx-gen2_to_handle_int_errors
Forman xxx gen2 to handle int errors
2 parents 3fcd41e + 92a442a commit 0b9c41c

File tree

7 files changed

+138
-6
lines changed

7 files changed

+138
-6
lines changed
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
input_config:
2+
store_id: file
3+
store_params:
4+
root: examples/serve/demo
5+
data_id: cube-1-250-250.zarr
6+
7+
cube_config:
8+
metadata:
9+
inverse_fine_structure_constant: 138
10+
11+
output_config:
12+
store_id: file
13+
store_params:
14+
root: .
15+
data_id: out.zarr
16+
replace: true

test/cli/test_gen2.py

+19
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def test_copy_zarr_gen(self):
3838
self.assertIsNotNone(result)
3939
result_json = self.read_result_json()
4040
self.assertEqual('ok', result_json.get('status'))
41+
self.assertEqual(201, result_json.get('status_code'))
4142
self.assertIsInstance(result_json.get('message'), str)
4243
self.assertIn('Cube generated successfully after ',
4344
result_json.get('message'))
@@ -57,6 +58,7 @@ def test_copy_zarr_info(self):
5758
result_json = self.read_result_json()
5859

5960
self.assertEqual('ok', result_json.get('status'))
61+
self.assertEqual(200, result_json.get('status_code'))
6062
result = result_json.get('result')
6163
self.assertIsInstance(result, dict)
6264

@@ -121,5 +123,22 @@ def test_copy_levels_gen(self):
121123
self.assertIsNotNone(result)
122124
result_json = self.read_result_json()
123125
self.assertEqual('ok', result_json.get('status'))
126+
self.assertEqual(201, result_json.get('status_code'))
124127
self.assertEqual({'data_id': 'out.levels'}, result_json.get('result'))
125128
self.assertTrue(os.path.isdir(result_levels))
129+
130+
def test_internal_error(self):
131+
request_file = os.path.join(os.path.dirname(__file__),
132+
'gen2-requests', 'internal-error.yml')
133+
result = self.invoke_cli(['gen2',
134+
'-o', result_file,
135+
request_file])
136+
print(result.output)
137+
self.assertIsNotNone(result)
138+
result_json = self.read_result_json()
139+
self.assertEqual('error', result_json.get('status'))
140+
self.assertEqual(500, result_json.get('status_code'))
141+
self.assertEqual('inverse_fine_structure_constant must be 137'
142+
' or running in wrong universe',
143+
result_json.get('message'))
144+
self.assertNotIn('result', result_json)

test/core/gen2/local/test_generator.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import copy
12
import json
23
import unittest
34
from typing import Dict, Any
@@ -108,7 +109,7 @@ def assertGeneratedCubeOk(self, request):
108109
@requests_mock.Mocker()
109110
def test_generate_cube_from_yaml_empty(self, m):
110111
m.put(CALLBACK_MOCK_URL, json={})
111-
request = self.REQUEST.copy()
112+
request = copy.deepcopy(self.REQUEST)
112113
request['cube_config']['time_range'] = ['2019-01-01', '2020-01-01']
113114

114115
generator = LocalCubeGenerator()
@@ -122,6 +123,21 @@ def test_generate_cube_from_yaml_empty(self, m):
122123
self.assertEqual(None, generator.generated_cube, xr.Dataset)
123124
self.assertEqual(None, generator.generated_gm)
124125

126+
@requests_mock.Mocker()
127+
def test_generate_cube_with_internal_error(self, m):
128+
m.put(CALLBACK_MOCK_URL, json={})
129+
request = self.REQUEST.copy()
130+
request['cube_config']['metadata'] = {
131+
'inverse_fine_structure_constant': 138
132+
}
133+
134+
generator = LocalCubeGenerator()
135+
with self.assertRaises(ValueError) as cm:
136+
generator.generate_cube(request)
137+
self.assertEqual(('inverse_fine_structure_constant must be 137'
138+
' or running in wrong universe',),
139+
cm.exception.args)
140+
125141
@requests_mock.Mocker()
126142
def test_get_cube_info_from_dict(self, m):
127143
m.put(CALLBACK_MOCK_URL, json={})

test/core/gen2/local/test_mdadjuster.py

+15
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,18 @@ def test_metadata_adjusted_other_crs(self):
113113
self.assertAlmostEqual(0.001,
114114
cube2.attrs.get('geospatial_lat_resolution'),
115115
delta=0.0005)
116+
117+
def test_easter_egg(self):
118+
cube = new_cube()
119+
md_adjuster = CubeMetadataAdjuster()
120+
with self.assertRaises(ValueError) as cm:
121+
md_adjuster.transform_cube(
122+
cube,
123+
GridMapping.from_dataset(cube),
124+
CubeConfig(metadata=dict(
125+
inverse_fine_structure_constant=136
126+
))
127+
)
128+
self.assertEqual(('inverse_fine_structure_constant must be 137'
129+
' or running in wrong universe',),
130+
cm.exception.args)

test/core/gen2/remote/test_service.py

+54-3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ def setUpClass(cls):
4848

4949
def test_service_byoa_inline_code(self):
5050
"""
51+
Assert the service can run BYOA with a inline code.
52+
5153
This test sets up a generator request with an
5254
inline user-code configuration
5355
that will cause the generator to
@@ -62,6 +64,8 @@ def test_service_byoa_inline_code(self):
6264

6365
def test_service_byoa_code_package(self):
6466
"""
67+
Assert the service can run BYOA with a code package.
68+
6569
This test sets up a generator request with an
6670
packaged user-code configuration
6771
that will cause the generator to
@@ -218,7 +222,7 @@ def _test_service_byoa(self, inline_code: bool):
218222

219223
def test_service_simple_copy(self):
220224
"""
221-
This test sets up a generator request that
225+
Assert the service can copy.
222226
223227
1. opens a dataset "DATASET-1.zarr" in store "@test"
224228
2. writes this dataset "OUTPUT.zarr" in store "@test"
@@ -253,7 +257,7 @@ def test_service_simple_copy(self):
253257

254258
def test_service_empty_cube(self):
255259
"""
256-
This test asserts the service fails with a warning.
260+
Assert the service returns an empty-cube warning.
257261
"""
258262
if not self.server_running:
259263
return
@@ -293,7 +297,7 @@ def test_service_empty_cube(self):
293297

294298
def test_service_invalid_request(self):
295299
"""
296-
This test asserts the service fails with a 400 status code.
300+
Assert the service recognizes invalid requests.
297301
"""
298302
if not self.server_running:
299303
return
@@ -326,3 +330,50 @@ def test_service_invalid_request(self):
326330
' does not exist in store',
327331
result_dict.get('message'))
328332
self.assertEqual(None, result_dict.get('output'))
333+
334+
def test_service_internal_error(self):
335+
"""
336+
Assert the service handles internal errors.
337+
"""
338+
339+
if not self.server_running:
340+
return
341+
342+
generator = RemoteCubeGenerator(
343+
service_config=ServiceConfig(endpoint_url=SERVER_URL)
344+
)
345+
346+
request_dict = {
347+
"input_configs": [
348+
{
349+
'store_id': '@test',
350+
"data_id": "DATASET-1.zarr"
351+
}
352+
],
353+
"cube_config": {
354+
"metadata": {
355+
# This raises a ValueError in "xcube gen2"
356+
'inverse_fine_structure_constant': 138
357+
}
358+
},
359+
"output_config": {
360+
"store_id": "@test",
361+
"data_id": "OUTPUT.zarr",
362+
"replace": True,
363+
}
364+
}
365+
366+
result = generator.generate_cube(request_dict)
367+
368+
result_dict = result.to_dict()
369+
self.assertEqual('error', result_dict.get('status'))
370+
self.assertEqual(400, result_dict.get('status_code'))
371+
self.assertEqual(None, result_dict.get('result'))
372+
self.assertEqual('inverse_fine_structure_constant must be 137'
373+
' or running in wrong universe',
374+
result_dict.get('message'))
375+
self.assertEqual(None, result_dict.get('output'))
376+
377+
# self.assertEqual(('inverse_fine_structure_constant must be 137'
378+
# ' or running in wrong universe',),
379+
# cm.exception.args)

xcube/cli/gen2.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,14 @@ def gen2(request_path: str,
150150
message=f'{error}',
151151
traceback=traceback.format_tb(error.__traceback__))
152152
if isinstance(error, CubeGeneratorError):
153-
if error.remote_output:
153+
if error.status_code is not None:
154+
result.update(status_code=error.status_code)
155+
if error.remote_output is not None:
154156
result.update(remote_output=error.remote_output)
155-
if error.remote_traceback:
157+
if error.remote_traceback is not None:
156158
result.update(remote_traceback=error.remote_traceback)
159+
if 'status_code' not in result:
160+
result['status_code'] = 500
157161

158162
if result.get('status') == 'error':
159163
result['versions'] = get_xcube_versions()

xcube/core/gen2/local/mdadjuster.py

+11
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,24 @@ def transform_cube(self,
6161
**get_geospatial_attrs(gm)
6262
)
6363
if cube_config.metadata:
64+
self._check_for_self_destruction(cube_config.metadata)
6465
cube.attrs.update(cube_config.metadata)
6566
if cube_config.variable_metadata:
6667
for var_name, metadata in cube_config.variable_metadata.items():
6768
if var_name in cube.variables and metadata:
6869
cube[var_name].attrs.update(metadata)
6970
return cube, gm, cube_config
7071

72+
@staticmethod
73+
def _check_for_self_destruction(metadata: Dict[str, Any]):
74+
key = 'inverse_fine_structure_constant'
75+
value = 137
76+
if key in metadata and metadata[key] != value:
77+
# Note, this is an easter egg that causes
78+
# an intended internal error for testing
79+
raise ValueError(f'{key} must be {value}'
80+
f' or running in wrong universe')
81+
7182

7283
def get_geospatial_attrs(gm: GridMapping) -> Dict[str, Any]:
7384
if gm.crs.is_geographic:

0 commit comments

Comments
 (0)