|
2 | 2 | import shutil |
3 | 3 | import tempfile |
4 | 4 | import unittest |
| 5 | +from io import StringIO |
5 | 6 | from unittest.mock import MagicMock, mock_open, patch |
6 | 7 |
|
7 | 8 | import numpy as np |
| 9 | +import requests |
| 10 | +import yaml |
| 11 | +from rich.console import Console |
8 | 12 |
|
9 | 13 | from CodeEntropy.run import RunManager |
10 | 14 |
|
@@ -45,8 +49,8 @@ def setup_citation_file(self, mock_file): |
45 | 49 | """ |
46 | 50 | citation_content = """\ |
47 | 51 | authors: |
48 | | - - given-names: Name1 |
49 | | - family-names: Name2 |
| 52 | + - given-names: Alice |
| 53 | + family-names: Smith |
50 | 54 | """ |
51 | 55 |
|
52 | 56 | mock_file.return_value = mock_open(read_data=citation_content).return_value |
@@ -122,18 +126,129 @@ def test_create_job_folder_with_invalid_job_suffix( |
122 | 126 | self.assertEqual(new_folder_path, expected_path) |
123 | 127 | mock_makedirs.assert_called_once_with(expected_path, exist_ok=True) |
124 | 128 |
|
125 | | - @patch("builtins.open", new_callable=mock_open) |
126 | | - def test_load_citation_data(self, mock_file): |
| 129 | + @patch("requests.get") |
| 130 | + def test_load_citation_data_success(self, mock_get): |
| 131 | + """Should return parsed dict when CITATION.cff loads successfully.""" |
| 132 | + mock_yaml = """ |
| 133 | + authors: |
| 134 | + - given-names: Alice |
| 135 | + family-names: Smith |
| 136 | + title: TestProject |
| 137 | + version: 1.0 |
| 138 | + date-released: 2025-01-01 |
127 | 139 | """ |
128 | | - Test loading the citation data from CITATION.cff. |
129 | | - """ |
130 | | - self.setup_citation_file(mock_file) |
131 | | - instance = RunManager("dummy") # replace with your class |
132 | | - data = instance.load_citation_data("CITATION.cff") |
| 140 | + mock_response = MagicMock() |
| 141 | + mock_response.status_code = 200 |
| 142 | + mock_response.text = mock_yaml |
| 143 | + mock_get.return_value = mock_response |
| 144 | + |
| 145 | + instance = RunManager("dummy") |
| 146 | + data = instance.load_citation_data() |
| 147 | + |
| 148 | + self.assertIsInstance(data, dict) |
| 149 | + self.assertEqual(data["title"], "TestProject") |
| 150 | + self.assertEqual(data["authors"][0]["given-names"], "Alice") |
| 151 | + |
| 152 | + @patch("requests.get") |
| 153 | + def test_load_citation_data_network_error(self, mock_get): |
| 154 | + """Should return None if network request fails.""" |
| 155 | + mock_get.side_effect = requests.exceptions.ConnectionError("Network down") |
| 156 | + |
| 157 | + instance = RunManager("dummy") |
| 158 | + data = instance.load_citation_data() |
| 159 | + |
| 160 | + self.assertIsNone(data) |
| 161 | + |
| 162 | + @patch("requests.get") |
| 163 | + def test_load_citation_data_http_error(self, mock_get): |
| 164 | + """Should return None if HTTP response is non-200.""" |
| 165 | + mock_response = MagicMock() |
| 166 | + mock_response.status_code = 404 |
| 167 | + mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError() |
| 168 | + mock_get.return_value = mock_response |
| 169 | + |
| 170 | + instance = RunManager("dummy") |
| 171 | + data = instance.load_citation_data() |
| 172 | + |
| 173 | + self.assertIsNone(data) |
| 174 | + |
| 175 | + @patch("requests.get") |
| 176 | + def test_load_citation_data_invalid_yaml(self, mock_get): |
| 177 | + """Should raise YAML error if file content is invalid YAML.""" |
| 178 | + mock_response = MagicMock() |
| 179 | + mock_response.status_code = 200 |
| 180 | + mock_response.text = "invalid: [oops" |
| 181 | + mock_get.return_value = mock_response |
| 182 | + |
| 183 | + instance = RunManager("dummy") |
| 184 | + |
| 185 | + with self.assertRaises(yaml.YAMLError): |
| 186 | + instance.load_citation_data() |
| 187 | + |
| 188 | + @patch.object(RunManager, "load_citation_data") |
| 189 | + def test_show_splash_with_citation(self, mock_load): |
| 190 | + """Should render full splash screen when citation data is present.""" |
| 191 | + mock_load.return_value = { |
| 192 | + "title": "TestProject", |
| 193 | + "version": "1.0", |
| 194 | + "date-released": "2025-01-01", |
| 195 | + "url": "https://example.com", |
| 196 | + "abstract": "This is a test abstract.", |
| 197 | + "authors": [ |
| 198 | + {"given-names": "Alice", "family-names": "Smith", "affiliation": "Uni"} |
| 199 | + ], |
| 200 | + } |
| 201 | + |
| 202 | + buf = StringIO() |
| 203 | + test_console = Console(file=buf, force_terminal=False, width=80) |
| 204 | + |
| 205 | + instance = RunManager("dummy") |
| 206 | + with patch("CodeEntropy.run.console", test_console): |
| 207 | + instance.show_splash() |
| 208 | + |
| 209 | + output = buf.getvalue() |
| 210 | + |
| 211 | + self.assertIn("Version 1.0", output) |
| 212 | + self.assertIn("2025-01-01", output) |
| 213 | + self.assertIn("https://example.com", output) |
| 214 | + self.assertIn("This is a test abstract.", output) |
| 215 | + self.assertIn("Alice Smith", output) |
| 216 | + |
| 217 | + @patch.object(RunManager, "load_citation_data", return_value=None) |
| 218 | + def test_show_splash_without_citation(self, mock_load): |
| 219 | + """Should render minimal splash screen when no citation data.""" |
| 220 | + buf = StringIO() |
| 221 | + test_console = Console(file=buf, force_terminal=False, width=80) |
| 222 | + |
| 223 | + instance = RunManager("dummy") |
| 224 | + with patch("CodeEntropy.run.console", test_console): |
| 225 | + instance.show_splash() |
| 226 | + |
| 227 | + output = buf.getvalue() |
| 228 | + |
| 229 | + self.assertNotIn("Version", output) |
| 230 | + self.assertNotIn("Contributors", output) |
| 231 | + self.assertIn("Welcome to CodeEntropy", output) |
| 232 | + |
| 233 | + @patch.object(RunManager, "load_citation_data") |
| 234 | + def test_show_splash_missing_fields(self, mock_load): |
| 235 | + """Should gracefully handle missing optional fields in citation data.""" |
| 236 | + mock_load.return_value = { |
| 237 | + "title": "PartialProject", |
| 238 | + # no version, no date, no authors, no abstract |
| 239 | + } |
| 240 | + |
| 241 | + buf = StringIO() |
| 242 | + test_console = Console(file=buf, force_terminal=False, width=80) |
| 243 | + |
| 244 | + instance = RunManager("dummy") |
| 245 | + with patch("CodeEntropy.run.console", test_console): |
| 246 | + instance.show_splash() |
| 247 | + |
| 248 | + output = buf.getvalue() |
133 | 249 |
|
134 | | - self.assertIn("authors", data) |
135 | | - self.assertEqual(data["authors"][0]["given-names"], "Name1") |
136 | | - self.assertEqual(data["authors"][0]["family-names"], "Name2") |
| 250 | + self.assertIn("Version ?", output) |
| 251 | + self.assertIn("No description available.", output) |
137 | 252 |
|
138 | 253 | def test_run_entropy_workflow(self): |
139 | 254 | """ |
|
0 commit comments