|
1 | 1 | import json |
2 | 2 | import logging |
3 | 3 | import os |
4 | | -import sys |
5 | 4 | import unittest |
6 | 5 | from io import StringIO |
7 | | -from unittest.mock import patch, MagicMock |
| 6 | +from unittest.mock import MagicMock, patch |
8 | 7 |
|
9 | 8 | import boto3 |
10 | 9 | import s3fs |
| 10 | +from botocore import UNSIGNED |
| 11 | +from botocore.session import get_session |
| 12 | +from botocore.client import Config as BotoConfig # avoid conflict with json config |
11 | 13 | from moto import mock_aws |
12 | 14 | from moto.moto_server.threaded_moto_server import ThreadedMotoServer |
13 | 15 |
|
14 | | -from aodn_cloud_optimised.bin.generic_cloud_optimised_creation import main |
15 | | -from aodn_cloud_optimised.lib.clusterLib import ClusterMode |
| 16 | +from aodn_cloud_optimised.bin.generic_cloud_optimised_creation import ( |
| 17 | + DatasetConfig, |
| 18 | + main, |
| 19 | +) |
| 20 | +from aodn_cloud_optimised.lib.config import load_dataset_config |
16 | 21 |
|
17 | 22 | ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) |
18 | 23 |
|
|
28 | 33 | os.path.join(ROOT_DIR, "resources", file_name) for file_name in filenames |
29 | 34 | ] |
30 | 35 |
|
| 36 | +# On purpose wrong! |
31 | 37 | DATASET_CONFIG_NC_ACORN_JSON = os.path.join( |
32 | 38 | ROOT_DIR, |
33 | 39 | "resources", |
34 | | - # "radar_TurquoiseCoast_velocity_hourly_averaged_delayed_qc.json", |
35 | 40 | "wave_buoy_realtime_nonqc.json", |
36 | 41 | ) |
37 | 42 |
|
@@ -120,41 +125,75 @@ def tearDown(self): |
120 | 125 | self.server.stop() |
121 | 126 | del os.environ["RUNNING_UNDER_UNITTEST"] |
122 | 127 |
|
123 | | - @patch("argparse.ArgumentParser.parse_args") |
124 | | - def test_main(self, mock_parse_args): |
125 | | - # Prepare mock arguments |
126 | | - mock_parse_args.return_value = MagicMock( |
127 | | - paths=["IMOS/ACORN/gridded_1h-avg-current-map_QC"], |
128 | | - # filters=["TURQ"], |
129 | | - filters=[".nc"], |
130 | | - exclude="FV02", |
131 | | - suffix=".nc", |
132 | | - raise_error=True, |
133 | | - dataset_config=DATASET_CONFIG_NC_ACORN_JSON, |
134 | | - clear_existing_data=True, |
135 | | - force_previous_parquet_deletion=False, |
136 | | - # cluster_mode=ClusterMode.LOCAL, |
137 | | - cluster_mode=ClusterMode.NONE, |
138 | | - optimised_bucket_name=self.BUCKET_OPTIMISED_NAME, |
139 | | - root_prefix_cloud_optimised_path="testing", |
140 | | - bucket_raw="imos-data", |
141 | | - ) |
| 128 | + def test_main_with_config_and_json_overwrite_fail(self): |
| 129 | + dataset_config = load_dataset_config(DATASET_CONFIG_NC_ACORN_JSON) |
| 130 | + config_validated = DatasetConfig.model_validate(dataset_config) |
| 131 | + |
| 132 | + with open(DATASET_CONFIG_NC_ACORN_JSON) as f: |
| 133 | + raw_json = f.read() |
| 134 | + config_validated = DatasetConfig.model_validate_json(raw_json) |
| 135 | + |
| 136 | + def _mock_boto3_client(service_name, *args, **kwargs): |
| 137 | + if service_name == "s3": |
| 138 | + session = get_session() |
| 139 | + return session.create_client( |
| 140 | + "s3", |
| 141 | + endpoint_url="http://127.0.0.1:5555", |
| 142 | + region_name="us-east-1", |
| 143 | + config=BotoConfig(signature_version=UNSIGNED), |
| 144 | + ) |
| 145 | + raise NotImplementedError(f"Unhandled boto3 service: {service_name}") |
| 146 | + |
| 147 | + with ( |
| 148 | + patch( |
| 149 | + "aodn_cloud_optimised.bin.generic_cloud_optimised_creation.load_config_and_validate", |
| 150 | + new=lambda _: config_validated, |
| 151 | + ), |
| 152 | + patch("argparse.ArgumentParser.parse_args") as mock_parse_args, |
| 153 | + patch("sys.exit") as mock_sys_exit, |
| 154 | + patch( |
| 155 | + "aodn_cloud_optimised.lib.s3Tools.boto3.client", new=_mock_boto3_client |
| 156 | + ), |
| 157 | + ): |
| 158 | + mock_parse_args.return_value = MagicMock( |
| 159 | + config=DATASET_CONFIG_NC_ACORN_JSON, |
| 160 | + json_overwrite=json.dumps( |
| 161 | + { |
| 162 | + "run_settings": { |
| 163 | + "cluster": {"mode": None}, |
| 164 | + "raise_error": True, |
| 165 | + "force_previous_parquet_deletion": False, |
| 166 | + "clear_existing_data": False, |
| 167 | + "paths": [ |
| 168 | + { |
| 169 | + "s3_uri": "s3://imos-data/IMOS/ACORN/gridded_1h-avg-current-map_QC", |
| 170 | + "filter": [], |
| 171 | + }, |
| 172 | + ], |
| 173 | + "optimised_bucket_name": self.BUCKET_OPTIMISED_NAME, |
| 174 | + "root_prefix_cloud_optimised_path": self.ROOT_PREFIX_CLOUD_OPTIMISED_PATH, |
| 175 | + } |
| 176 | + } |
| 177 | + ), |
| 178 | + ) |
142 | 179 |
|
143 | | - # Capture logs |
144 | | - log_stream = StringIO() |
145 | | - log_handler = logging.StreamHandler(log_stream) |
146 | | - logger = logging.getLogger() |
147 | | - logger.addHandler(log_handler) |
| 180 | + log_stream = StringIO() |
| 181 | + log_handler = logging.StreamHandler(log_stream) |
| 182 | + logger = logging.getLogger() |
| 183 | + logger.addHandler(log_handler) |
148 | 184 |
|
149 | | - with self.assertRaises(Exception) as context: |
150 | | - main() |
| 185 | + with self.assertRaises(Exception) as context: |
| 186 | + main() |
| 187 | + mock_sys_exit.assert_called_with(0) |
151 | 188 |
|
152 | | - # with self.assertRaises(SystemExit) as cm: |
153 | | - # main() |
154 | | - # self.assertEqual(cm.exception.code, 1) # Verify exit code |
| 189 | + log_handler.flush() |
| 190 | + captured_logs = log_stream.getvalue().strip().split("\n") |
155 | 191 |
|
156 | | - # Restore stdout |
157 | | - # sys.stdout = sys.__stdout__ |
| 192 | + assert any( |
| 193 | + "Exception: Error in Cloud Optimised process. Forcing script exit" |
| 194 | + in log |
| 195 | + for log in captured_logs |
| 196 | + ) |
158 | 197 |
|
159 | 198 |
|
160 | 199 | if __name__ == "__main__": |
|
0 commit comments