Coverage for tests/unit_tests/managers/test_remote_config.py: 100%
125 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03:53 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 03:53 +0000
1"""
2Tests of remote_config.py and it's integration with ApioContext.
3"""
5# TODO: Add test coverage of the "refresh-failure-on" logic.
7import json
8from datetime import datetime, timedelta
9from tests.conftest import ApioRunner
10from apio.managers.remote_config import (
11 get_datetime_stamp,
12 days_between_datetime_stamps,
13)
14from apio.utils import util
15from apio.apio_context import (
16 ApioContext,
17 PackagesPolicy,
18 ProjectPolicy,
19 RemoteConfigPolicy,
20)
23def get_test_data(
24 loaded_by_apio_version: str,
25 loaded_at_days: int,
26 loaded_from: str,
27):
28 """Returns a fake cached-remote-config.json content. 'loaded_at_days' is
29 the value of the remote config "loaded-at" relative to today in days."""
30 loaded_at_datetime = datetime.now() + timedelta(days=loaded_at_days)
31 loaded_at_stamp = get_datetime_stamp(loaded_at_datetime)
32 assert isinstance(loaded_at_stamp, str)
34 return {
35 "remote-config": {
36 "packages": {
37 "drivers": {
38 "repository": {
39 "name": "tools-drivers",
40 "organization": "fpgawars",
41 },
42 "release": {
43 "tag": "2026-08-07",
44 "package": "apio-drivers-${PLATFORM}-${YYYYMMDD}.tgz",
45 },
46 },
47 "examples": {
48 "repository": {
49 "name": "apio-examples",
50 "organization": "fpgawars",
51 },
52 "release": {
53 "tag": "2026-08-06",
54 "package": "apio-examples-${YYYYMMDD}.tgz",
55 },
56 },
57 },
58 },
59 "metadata": {
60 "loaded-by": loaded_by_apio_version,
61 "loaded-at": loaded_at_stamp,
62 "loaded-from": loaded_from,
63 },
64 }
67def test_datetime_stamp_diff_days():
68 """Test the datetime timestamp diff."""
70 assert (
71 get_datetime_stamp(
72 datetime(year=2025, month=6, day=30, hour=14, minute=45)
73 )
74 == "2025-06-30-14-45"
75 )
77 ts_now = get_datetime_stamp()
78 assert (
79 days_between_datetime_stamps(
80 ts_now,
81 ts_now,
82 default=9999,
83 )
84 == 0
85 )
87 assert (
88 days_between_datetime_stamps(
89 "2025-06-15-07-30",
90 "2025-06-16-00-01",
91 default=9999,
92 )
93 == 1
94 )
96 assert (
97 days_between_datetime_stamps(
98 "2025-06-16-00-01",
99 "2025-06-15-07-30",
100 default=9999,
101 )
102 == -1
103 )
105 assert (
106 days_between_datetime_stamps(
107 "2025-06-15-00-00",
108 "2025-06-15-23-59",
109 default=9999,
110 )
111 == 0
112 )
114 assert (
115 days_between_datetime_stamps(
116 "2025-06-15-00-0x",
117 "2025-06-15-23-59",
118 default=9999,
119 )
120 == 9999
121 )
123 assert (
124 days_between_datetime_stamps(
125 "2025-06-15-20-15",
126 "2025-06-20-00-01",
127 default=9999,
128 )
129 == 5
130 )
133def test_cached_config_ok(apio_runner: ApioRunner):
134 """Tests remote config resolution when the cached config is ok."""
136 with apio_runner.in_sandbox() as sb:
138 # -- Get an actual fresh remote config.
139 with apio_runner.with_logger() as log:
140 base_apio_ctx = ApioContext(
141 project_policy=ProjectPolicy.NO_PROJECT,
142 remote_config_policy=RemoteConfigPolicy.GET_FRESH,
143 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
144 )
145 assert "Fetching" in log.out
146 remote_config_url = base_apio_ctx.remote_config.metadata["loaded-from"]
148 # -- Write a test cached remote config.
149 path = sb.home_dir / "cached-remote-config.json"
150 test_data = get_test_data(
151 util.get_apio_version_str(), 0, remote_config_url
152 )
153 sb.write_file(
154 path,
155 json.dumps(
156 test_data,
157 indent=2,
158 ),
159 exists_ok=True,
160 )
162 # -- Init an apio context that should return the test cached
163 # -- remote config.
164 with apio_runner.with_logger() as log:
165 apio_ctx = ApioContext(
166 project_policy=ProjectPolicy.NO_PROJECT,
167 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
168 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
169 )
170 assert "Cached remote config is unsuitable" not in log.out
171 assert "Fetching" not in log.out
172 assert apio_ctx.remote_config.data == test_data["remote-config"]
175def test_cached_config_different_apio_version(apio_runner: ApioRunner):
176 """Tests remote config resolution when the cached config is from a
177 different apio version."""
179 with apio_runner.in_sandbox() as sb:
181 # -- Get an actual fresh remote config.
182 with apio_runner.with_logger() as log:
183 base_apio_ctx = ApioContext(
184 project_policy=ProjectPolicy.NO_PROJECT,
185 remote_config_policy=RemoteConfigPolicy.GET_FRESH,
186 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
187 )
188 assert "Fetching" in log.out
189 remote_config_url = base_apio_ctx.remote_config.metadata["loaded-from"]
191 # -- Write a test cached remote config. Using a fake apio version.
192 path = sb.home_dir / "cached-remote-config.json"
193 test_data = get_test_data("1.0.0", 0, remote_config_url)
194 sb.write_file(
195 path,
196 json.dumps(
197 test_data,
198 indent=2,
199 ),
200 exists_ok=True,
201 )
203 # -- Init an apio context that should return the test cached
204 # -- remote config.
205 with apio_runner.with_logger() as log:
206 apio_ctx = ApioContext(
207 project_policy=ProjectPolicy.NO_PROJECT,
208 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
209 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
210 )
211 assert (
212 "No suitable cached remote config file (Apio version mismatch)"
213 in log.out
214 )
215 assert "Fetching" in log.out
216 assert apio_ctx.remote_config.data == base_apio_ctx.remote_config.data
219def test_cached_config_different_apio_src_url(
220 apio_runner: ApioRunner,
221):
222 """Tests remote config resolution when the cached config is from a
223 different URL."""
225 with apio_runner.in_sandbox() as sb:
227 # -- Get an actual fresh remote config.
228 with apio_runner.with_logger() as log:
229 base_apio_ctx = ApioContext(
230 project_policy=ProjectPolicy.NO_PROJECT,
231 remote_config_policy=RemoteConfigPolicy.GET_FRESH,
232 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
233 )
234 assert "Fetching" in log.out
236 # -- Write a test cached remote config. Using a fake loaded-from url.
237 path = sb.home_dir / "cached-remote-config.json"
238 test_data = get_test_data(
239 util.get_apio_version_str(), 0, "https://github.com/fake/url.json"
240 )
241 sb.write_file(
242 path,
243 json.dumps(
244 test_data,
245 indent=2,
246 ),
247 exists_ok=True,
248 )
250 # -- Init an apio context that should return the test cached
251 # -- remote config.
252 with apio_runner.with_logger() as log:
253 apio_ctx = ApioContext(
254 project_policy=ProjectPolicy.NO_PROJECT,
255 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
256 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
257 )
258 assert (
259 "No suitable cached remote config file (source URL mismatch)"
260 in log.out
261 )
262 assert "Fetching" in log.out
263 assert apio_ctx.remote_config.data == base_apio_ctx.remote_config.data
266def test_cached_remote_config_too_old(apio_runner: ApioRunner):
267 """Tests remote config resolution when the cached config is too old."""
269 with apio_runner.in_sandbox() as sb:
271 # -- Get an actual fresh remote config.
272 with apio_runner.with_logger() as log:
273 base_apio_ctx = ApioContext(
274 project_policy=ProjectPolicy.NO_PROJECT,
275 remote_config_policy=RemoteConfigPolicy.GET_FRESH,
276 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
277 )
278 assert "Fetching" in log.out
279 remote_config_url = base_apio_ctx.remote_config.metadata["loaded-from"]
281 # -- Write a test cached remote config that is 10 days old.
282 path = sb.home_dir / "cached-remote-config.json"
283 test_data = get_test_data(
284 util.get_apio_version_str(), -10, remote_config_url
285 )
286 sb.write_file(
287 path,
288 json.dumps(
289 test_data,
290 indent=2,
291 ),
292 exists_ok=True,
293 )
295 # -- Init an apio context that should return a fresh remote config.
296 with apio_runner.with_logger() as log:
297 apio_ctx = ApioContext(
298 project_policy=ProjectPolicy.NO_PROJECT,
299 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
300 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
301 )
302 assert "No suitable cached remote config file (stale)" in log.out
303 assert "Fetching" in log.out
304 assert apio_ctx.remote_config.data == base_apio_ctx.remote_config.data
307def test_cached_remote_config_too_new(apio_runner: ApioRunner):
308 """Tests remote config resolution when the cached config is
309 from the future."""
311 with apio_runner.in_sandbox() as sb:
313 # -- Get an actual fresh remote config.
314 with apio_runner.with_logger() as log:
315 base_apio_ctx = ApioContext(
316 project_policy=ProjectPolicy.NO_PROJECT,
317 remote_config_policy=RemoteConfigPolicy.GET_FRESH,
318 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
319 )
320 assert "Fetching" in log.out
321 remote_config_url = base_apio_ctx.remote_config.metadata["loaded-from"]
323 # -- Write a test cached remote config that was downloaded 10 days
324 # -- in the future.
325 path = sb.home_dir / "cached-remote-config.json"
326 test_data = get_test_data(
327 util.get_apio_version_str(), 10, remote_config_url
328 )
329 sb.write_file(
330 path,
331 json.dumps(
332 test_data,
333 indent=2,
334 ),
335 exists_ok=True,
336 )
338 # -- Init an apio context that should return a fresh remote config.
339 with apio_runner.with_logger() as log:
340 apio_ctx = ApioContext(
341 project_policy=ProjectPolicy.NO_PROJECT,
342 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
343 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
344 )
345 assert "No suitable cached remote config file (stale)" in log.out
346 assert "Fetching" in log.out
347 assert apio_ctx.remote_config.data == base_apio_ctx.remote_config.data
350def test_corrupt_cached_remote_config(apio_runner: ApioRunner):
351 """Tests remote config resolution when the cached config file is
352 corrupt."""
354 with apio_runner.in_sandbox() as sb:
356 # -- Get an actual fresh remote config.
357 with apio_runner.with_logger() as log:
358 base_apio_ctx = ApioContext(
359 project_policy=ProjectPolicy.NO_PROJECT,
360 remote_config_policy=RemoteConfigPolicy.GET_FRESH,
361 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
362 )
363 assert "Fetching" in log.out
365 # -- Write a test cached remote config that is corrupt.
366 path = sb.home_dir / "cached-remote-config.json"
367 test_data = "{ corrupt json file }"
368 sb.write_file(
369 path,
370 json.dumps(
371 test_data,
372 indent=2,
373 ),
374 exists_ok=True,
375 )
377 # -- Init an apio context that should return a fresh remote config.
378 with apio_runner.with_logger() as log:
379 apio_ctx = ApioContext(
380 project_policy=ProjectPolicy.NO_PROJECT,
381 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
382 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
383 )
384 assert (
385 "No suitable cached remote config file (couldn't parse)" in log.out
386 )
387 assert "Fetching" in log.out
388 assert apio_ctx.remote_config.data == base_apio_ctx.remote_config.data
391def test_no_cached_remote_config(apio_runner: ApioRunner):
392 """Tests remote config resolution when there is no cached remote
393 config file."""
395 with apio_runner.in_sandbox() as sb:
397 # -- Check that the cache file doesn't exist.
398 path = sb.home_dir / "cached-remote-config.json"
399 assert not path.exists()
401 # -- Init an apio context that should return a fresh remote config.
402 with apio_runner.with_logger() as log:
403 apio_ctx = ApioContext(
404 project_policy=ProjectPolicy.NO_PROJECT,
405 remote_config_policy=RemoteConfigPolicy.CACHED_OK,
406 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
407 )
408 assert (
409 "No suitable cached remote config file (no cache file)" in log.out
410 )
411 assert "Fetching" in log.out
412 assert "oss-cad-suite" in apio_ctx.remote_config.data["packages"]
415def test_forced_fresh_remote_config_ok(apio_runner: ApioRunner):
416 """Tests remote config resolution when the cached config is ignored
417 because a fresh config was requested."""
419 with apio_runner.in_sandbox() as sb:
421 # -- Get an actual fresh remote config.
422 with apio_runner.with_logger() as log:
423 base_apio_ctx = ApioContext(
424 project_policy=ProjectPolicy.NO_PROJECT,
425 remote_config_policy=RemoteConfigPolicy.GET_FRESH,
426 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
427 )
428 assert "Fetching" in log.out
429 remote_config_url = base_apio_ctx.remote_config.metadata["loaded-from"]
431 # -- Write a test cached remote config.
432 path = sb.home_dir / "cached-remote-config.json"
433 test_data = get_test_data(
434 util.get_apio_version_str(), 0, remote_config_url
435 )
436 sb.write_file(
437 path,
438 json.dumps(
439 test_data,
440 indent=2,
441 ),
442 exists_ok=True,
443 )
445 # -- Init an apio context that should fetch a fresh config.
446 with apio_runner.with_logger() as log:
447 apio_ctx = ApioContext(
448 project_policy=ProjectPolicy.NO_PROJECT,
449 remote_config_policy=RemoteConfigPolicy.GET_FRESH,
450 packages_policy=PackagesPolicy.IGNORE_PACKAGES,
451 )
452 assert "Cached remote config is unsuitable" not in log.out
453 assert "Fetching" in log.out
454 assert apio_ctx.remote_config.data == base_apio_ctx.remote_config.data