diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2dc5dc5..797cb044 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -211,6 +211,7 @@ jobs: python3-yaml \ python3-requests \ python3-requests-toolbelt \ + python3-rfl-build \ python3-rfl-log \ python3-venv diff --git a/CHANGELOG.md b/CHANGELOG.md index 2851bc5f..a8513468 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to `RACKSDB_EXTENSIONS`, and to set default schema, extensions, and database paths when the matching `racksdb` / `racksdb-web` option is omitted (#149). - web: Support the same environment variables as CLI. +- pkgs: Add tests dependency on `RFL.build` >= 1.8.0 for parameterized unit + tests. - docs: - Mention support of RHEL 10, Fedora 43, SLES and openSUSE 15 and 16, Ubuntu 26.04 LTS. diff --git a/pyproject.toml b/pyproject.toml index 7ebba140..ac0403c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ tests = [ "coverage", "pytest", "pytest-cov", + "RFL.build >= 1.8.0", ] [project.scripts] diff --git a/racksdb/tests/lib/params.py b/racksdb/tests/lib/params.py deleted file mode 100644 index 335dec1d..00000000 --- a/racksdb/tests/lib/params.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2026 Rackslab -# -# This file is part of RacksDB. -# -# SPDX-License-Identifier: MIT - -import functools -import re - - -def _normalize_case(case): - if isinstance(case, tuple): - return case - return (case,) - - -def _slugify_case(case): - text = "_".join(str(item) for item in _normalize_case(case)) - slug = re.sub(r"[^0-9a-zA-Z]+", "_", text).strip("_").lower() - return slug or "case" - - -def expand_params(cases): - def decorator(func): - func._racksdb_expand_cases = list(cases) - return func - - return decorator - - -def _make_expanded_test(func, args): - @functools.wraps(func) - def expanded(self): - return func(self, *args) - - return expanded - - -def expand_parameterized_tests(cls): - for name, value in list(vars(cls).items()): - cases = getattr(value, "_racksdb_expand_cases", None) - if cases is None: - continue - - for index, case in enumerate(cases, start=1): - args = _normalize_case(case) - slug = _slugify_case(case) - expanded_name = f"{name}_{index:03d}_{slug}" - setattr(cls, expanded_name, _make_expanded_test(value, args)) - - delattr(cls, name) - - return cls diff --git a/racksdb/tests/web/test_blueprint.py b/racksdb/tests/web/test_blueprint.py index 695e3cd7..de8217fa 100644 --- a/racksdb/tests/web/test_blueprint.py +++ b/racksdb/tests/web/test_blueprint.py @@ -17,13 +17,15 @@ from ..lib.web import RacksDBCustomTestResponse from ..lib.common import schema_path, db_path, drawing_schema_path -from ..lib.params import expand_parameterized_tests, expand_params +from rfl.build.testing.params import expand from ..lib.reference import ( TestRacksDBReferenceDB, REFDB_DATACENTERS, REFDB_INFRASTRUCTURES, ) +http_verbs = expand(["get", "post"]) + class FakeRacksDBWebApp(flask.Flask): def __init__(self, schema, db, drawing_schema, openapi): @@ -37,7 +39,6 @@ def __init__(self, schema, db, drawing_schema, openapi): self.register_blueprint(self.blueprint) -@expand_parameterized_tests class TestRacksDBWebBlueprint(TestRacksDBReferenceDB): def setUp(self): try: @@ -465,25 +466,25 @@ def test_racks_fold(self): def client_method(self, verb): return self.client.get if verb == "get" else self.client.post - @expand_params(["get", "post"]) + @http_verbs def test_draw_room_png(self, verb): response = self.client_method(verb)(f"/v{get_version()}/draw/room/noisy.png") self.assertEqual(response.status_code, 200) self.assertEqual(response.mimetype, "image/png") - @expand_params(["get", "post"]) + @http_verbs def test_draw_room_svg(self, verb): response = self.client_method(verb)(f"/v{get_version()}/draw/room/noisy.svg") self.assertEqual(response.status_code, 200) self.assertEqual(response.mimetype, "image/svg+xml") - @expand_params(["get", "post"]) + @http_verbs def test_draw_room_pdf(self, verb): response = self.client_method(verb)(f"/v{get_version()}/draw/room/noisy.pdf") self.assertEqual(response.status_code, 200) self.assertEqual(response.mimetype, "application/pdf") - @expand_params(["get", "post"]) + @http_verbs def test_draw_room_invalid(self, verb): response = self.client_method(verb)(f"/v{get_version()}/draw/room/fail.png") self.assertEqual(response.status_code, 400) # FIXME: should be HTTP/404 @@ -497,7 +498,7 @@ def test_draw_room_invalid(self, verb): }, ) - @expand_params(["get", "post"]) + @http_verbs def test_draw_room_coordinates(self, verb): response = self.client_method(verb)( f"/v{get_version()}/draw/room/noisy.png?coordinates" @@ -513,7 +514,7 @@ def test_draw_room_coordinates(self, verb): coordinates = json.loads(coordinates_part.text) self.assertEqual(coordinates, {}) # FIXME: room coordinates are empty - @expand_params(["get", "post"]) + @http_verbs def test_draw_room_coordinates_yaml(self, verb): response = self.client_method(verb)( f"/v{get_version()}/draw/room/noisy.png?coordinates&coordinates_format=yaml" @@ -620,7 +621,7 @@ def test_draw_post_room_parameters_invalid_format(self): }, ) - @expand_params(["get", "post"]) + @http_verbs def test_draw_infrastructure_png(self, verb): response = self.client_method(verb)( f"/v{get_version()}/draw/infrastructure/mercury.png" @@ -628,7 +629,7 @@ def test_draw_infrastructure_png(self, verb): self.assertEqual(response.status_code, 200) self.assertEqual(response.mimetype, "image/png") - @expand_params(["get", "post"]) + @http_verbs def test_draw_infrastructure_svg(self, verb): response = self.client_method(verb)( f"/v{get_version()}/draw/infrastructure/mercury.svg" @@ -636,7 +637,7 @@ def test_draw_infrastructure_svg(self, verb): self.assertEqual(response.status_code, 200) self.assertEqual(response.mimetype, "image/svg+xml") - @expand_params(["get", "post"]) + @http_verbs def test_draw_infrastructure_pdf(self, verb): response = self.client_method(verb)( f"/v{get_version()}/draw/infrastructure/mercury.pdf" @@ -644,7 +645,7 @@ def test_draw_infrastructure_pdf(self, verb): self.assertEqual(response.status_code, 200) self.assertEqual(response.mimetype, "application/pdf") - @expand_params(["get", "post"]) + @http_verbs def test_draw_infrastructure_invalid(self, verb): response = self.client_method(verb)( f"/v{get_version()}/draw/infrastructure/fail.png" @@ -660,7 +661,7 @@ def test_draw_infrastructure_invalid(self, verb): }, ) - @expand_params(["get", "post"]) + @http_verbs def test_draw_infrastructure_coordinates(self, verb): response = self.client_method(verb)( f"/v{get_version()}/draw/infrastructure/mercury.png?coordinates" @@ -676,7 +677,7 @@ def test_draw_infrastructure_coordinates(self, verb): coordinates = json.loads(coordinates_part.text) self.assertIn("mecn0001", coordinates) - @expand_params(["get", "post"]) + @http_verbs def test_draw_infrastructure_coordinates_yaml(self, verb): response = self.client_method(verb)( f"/v{get_version()}/draw/infrastructure/mercury.png?coordinates&" @@ -794,7 +795,7 @@ def test_draw_post_infrastructure_ignored_query_parameters(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.mimetype, "image/png") - @expand_params(["get", "post"]) + @http_verbs def test_draw_invalid_entity(self, verb): response = self.client_method(verb)(f"/v{get_version()}/draw/fail/noisy.png") self.assertEqual(response.status_code, 400) @@ -808,7 +809,7 @@ def test_draw_invalid_entity(self, verb): }, ) - @expand_params(["get", "post"]) + @http_verbs def test_draw_invalid_format(self, verb): response = self.client_method(verb)(f"/v{get_version()}/draw/room/noisy.fail") self.assertEqual(response.status_code, 400) @@ -822,7 +823,7 @@ def test_draw_invalid_format(self, verb): }, ) - @expand_params(["get", "post"]) + @http_verbs def test_draw_coordinates_invalid_format(self, verb): response = self.client_method(verb)( f"/v{get_version()}/draw/room/noisy.png?coordinates&coordinates_format=fail"