From c5b04dd45d09177c1d5c3c24ce6c9762569a351a Mon Sep 17 00:00:00 2001 From: iceman-muc Date: Wed, 20 May 2026 10:49:41 +0200 Subject: [PATCH 1/8] feat(codebeamer): add baseline_id support for tracker and cbQL queries --- lobster/tools/codebeamer/codebeamer.py | 18 ++++++++++++++++++ lobster/tools/codebeamer/config.py | 1 + 2 files changed, 19 insertions(+) diff --git a/lobster/tools/codebeamer/codebeamer.py b/lobster/tools/codebeamer/codebeamer.py index 53f3b73b..7c379070 100755 --- a/lobster/tools/codebeamer/codebeamer.py +++ b/lobster/tools/codebeamer/codebeamer.py @@ -72,6 +72,7 @@ class SupportedConfigKeys(Enum): RETRY_ERROR_CODES = "retry_error_codes" IMPORT_TAGGED = "import_tagged" IMPORT_QUERY = "import_query" + BASELINE_ID = "baseline_id" VERIFY_SSL = "verify_ssl" PAGE_SIZE = "page_size" REFS = "refs" @@ -231,12 +232,16 @@ def get_query(cb_config: Config, query: Union[int, str]): query, page_id, cb_config.page_size)) + if cb_config.baseline_id is not None: + url += f"&baselineId={cb_config.baseline_id}" elif isinstance(query, str): url = ("%s/items/query?page=%u&pageSize=%u&queryString=%s" % (cb_config.base, page_id, cb_config.page_size, query)) + if cb_config.baseline_id is not None: + url += f"&baselineId={cb_config.baseline_id}" data = query_cb_single(cb_config, url) if len(data) != 4: raise MismatchException( @@ -511,6 +516,7 @@ def parse_config_data(data: dict) -> Config: references=ensure_list(data.get(SupportedConfigKeys.REFS.value, [])), import_tagged=data.get(SupportedConfigKeys.IMPORT_TAGGED.value), import_query=data.get(SupportedConfigKeys.IMPORT_QUERY.value), + baseline_id=data.get(SupportedConfigKeys.BASELINE_ID.value), verify_ssl=data.get(SupportedConfigKeys.VERIFY_SSL.value, True), page_size=data.get(SupportedConfigKeys.PAGE_SIZE.value, 100), schema=data.get(SupportedConfigKeys.SCHEMA.value, "Requirement"), @@ -538,6 +544,18 @@ def parse_config_data(data: dict) -> Config: raise KeyError(f"{SupportedConfigKeys.CB_ROOT.value} must start with https://, " f"but value is {config.cb_auth_conf.root}.") + if config.baseline_id is not None: + try: + config.baseline_id = int(config.baseline_id) + except (TypeError, ValueError) as exc: + raise KeyError( + f"{SupportedConfigKeys.BASELINE_ID.value} must be a positive integer." + ) from exc + if config.baseline_id <= 0: + raise KeyError( + f"{SupportedConfigKeys.BASELINE_ID.value} must be a positive integer." + ) + return config diff --git a/lobster/tools/codebeamer/config.py b/lobster/tools/codebeamer/config.py index be582e6b..2a30097a 100644 --- a/lobster/tools/codebeamer/config.py +++ b/lobster/tools/codebeamer/config.py @@ -17,6 +17,7 @@ class Config: references: dict import_tagged: str import_query: Union[str, int] + baseline_id: Optional[int] verify_ssl: bool page_size: int schema: str From b5bdb309586b787fbc1c98ca75d218081e0f19fc Mon Sep 17 00:00:00 2001 From: iceman-muc Date: Wed, 20 May 2026 11:32:46 +0200 Subject: [PATCH 2/8] docs: document baseline_id for lobster-codebeamer - Add 'Querying at a Baseline' subsection to user manual - Add baseline_id to Python API reference (Config params, behavioral notes, error conditions) - Remove baseline_id from report-ID query path (has no effect on /reports/{id}/items endpoint; only works with cbQL strings) --- documentation/manual-lobster_codebeamer.md | 15 +++++++++++++++ lobster/tools/codebeamer/codebeamer.py | 2 -- manual/tools/codebeamer.rst | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/documentation/manual-lobster_codebeamer.md b/documentation/manual-lobster_codebeamer.md index 6bc1fedc..ee48ddee 100644 --- a/documentation/manual-lobster_codebeamer.md +++ b/documentation/manual-lobster_codebeamer.md @@ -122,6 +122,21 @@ Then invoke the `lobster-codebeamer` tool like so: ```bash $ lobster-codebeamer --import-query 4776335 --out system-requirements.lobster ``` + +### Querying at a Baseline + +To restrict results to the state of a specific Codebeamer baseline, add `baseline_id` +to the YAML config file: + +```yaml +import_query: "tracker.id IN (29782591) AND status IN ('Approved')" +baseline_id: 407126303 +``` + +> **Important:** `baseline_id` is only applied when `import_query` is a **cbQL query +> string**. When `import_query` is a numeric report ID, the parameter is silently +> ignored and the current HEAD state is returned instead. + ### Importing only tagged requirements If you are not interested in a completeness check, or your diff --git a/lobster/tools/codebeamer/codebeamer.py b/lobster/tools/codebeamer/codebeamer.py index 7c379070..b803cbe5 100755 --- a/lobster/tools/codebeamer/codebeamer.py +++ b/lobster/tools/codebeamer/codebeamer.py @@ -232,8 +232,6 @@ def get_query(cb_config: Config, query: Union[int, str]): query, page_id, cb_config.page_size)) - if cb_config.baseline_id is not None: - url += f"&baselineId={cb_config.baseline_id}" elif isinstance(query, str): url = ("%s/items/query?page=%u&pageSize=%u&queryString=%s" % (cb_config.base, diff --git a/manual/tools/codebeamer.rst b/manual/tools/codebeamer.rst index 457df03d..c9513146 100644 --- a/manual/tools/codebeamer.rst +++ b/manual/tools/codebeamer.rst @@ -44,6 +44,7 @@ Config references=[], import_tagged=None, import_query=1234, # report ID or cbQL query string + baseline_id=None, # only used when import_query is a cbQL string verify_ssl=True, page_size=100, schema="requirement", # or "implementation" / "activity" @@ -57,6 +58,7 @@ Config - ``references`` (List[str]): Names of Codebeamer fields whose referenced items should be traced (converted to ``req`` tags). - ``import_tagged`` (str | None): Path to an existing LOBSTER artifact whose unresolved ``req`` references define item IDs to import. - ``import_query`` (int | str | None): Report ID (int) or cbQL query string used to fetch items directly. +- ``baseline_id`` (int | None): Codebeamer baseline ID to query against. **Only effective when** ``import_query`` **is a cbQL query string**; ignored when ``import_query`` is a numeric report ID. - ``verify_ssl`` (bool): Whether to verify TLS certificates; set ``True`` in production for security. - ``page_size`` (int): Pagination size for REST queries; a trade-off between round trips and response size (default typically 100). - ``schema`` (str): Target schema type (``requirement``, ``implementation``, ``activity``) controlling class/namespace mapping. @@ -112,6 +114,7 @@ Core Goals Behavioral Notes ---------------- - ``import_tagged`` overrides ``import_query`` if both provided. +- ``baseline_id`` is silently ignored when ``import_query`` is a numeric report ID; use a cbQL string instead. - ``schema`` maps to namespace class: requirement→``req`` / implementation→``imp`` / activity→``act``. - Exponential backoff is implicit (1s, 2s, 4s...) based on ``num_request_retry``. - Missing ``token`` triggers user/password or ``~/.netrc`` resolution. @@ -122,3 +125,4 @@ Error Conditions - Absent both ``import_query`` & ``import_tagged`` → ``KeyError``. - ``num_request_retry <= 0`` → ``ValueError``. - Unrecognised ``schema`` → ``KeyError``. +- ``baseline_id`` is not a positive integer → ``KeyError``. From da220d5f5528ce62dec6c96b09edc4cd382420b1 Mon Sep 17 00:00:00 2001 From: iceman-muc Date: Wed, 20 May 2026 11:40:58 +0200 Subject: [PATCH 3/8] test: fix and extend unit tests for baseline_id in codebeamer - Add baseline_id=None to setUp (required positional field) - Add test_get_query_with_query_and_baseline_id: verifies baselineId is appended to URL when import_query is a cbQL string - Add test_get_query_with_ID_and_baseline_id: verifies baselineId is NOT appended when import_query is a numeric report ID --- .../lobster_codebeamer/test_codebeamer.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests_unit/lobster_codebeamer/test_codebeamer.py b/tests_unit/lobster_codebeamer/test_codebeamer.py index a242bb9e..f4f59427 100644 --- a/tests_unit/lobster_codebeamer/test_codebeamer.py +++ b/tests_unit/lobster_codebeamer/test_codebeamer.py @@ -28,6 +28,7 @@ def setUp(self): references=None, import_tagged=None, import_query=None, + baseline_id=None, verify_ssl=None, page_size=10, schema="Requirement", @@ -118,6 +119,36 @@ def test_get_query_with_query(self, mock_query_cb_single): self.assertEqual(result[0].location.tracker, item_data[0]["tracker"]["id"]) self.assertEqual(result[0].location.version, item_data[0]["version"]) + @patch('lobster.tools.codebeamer.codebeamer.query_cb_single') + def test_get_query_with_query_and_baseline_id(self, mock_query_cb_single): + """baseline_id is appended to the URL when import_query is a cbQL string.""" + self._mock_cb_config.baseline_id = 407126303 + mock_query = "tracker.id IN (29782591)" + mock_query_cb_single.return_value = { + "page": 1, "pageSize": 10, "total": 0, "items": [] + } + get_query(self._mock_cb_config, mock_query) + called_url = mock_query_cb_single.call_args[0][1] + self.assertIn("baselineId=407126303", called_url) + + @patch('lobster.tools.codebeamer.codebeamer.query_cb_single') + def test_get_query_with_ID_and_baseline_id(self, mock_query_cb_single): + """baseline_id is NOT appended to the URL when import_query is a report ID.""" + self._mock_cb_config.baseline_id = 407126303 + mock_query = 171619121 + item_data = [{"item": { + "id": 1, "name": "x", "version": 1, + "tracker": {"id": 1}, + "categories": [{"name": "Requirement"}], + "status": {"name": "Draft"}, + }}] + mock_query_cb_single.return_value = { + "page": 1, "pageSize": 10, "total": 1, "items": item_data + } + get_query(self._mock_cb_config, mock_query) + called_url = mock_query_cb_single.call_args[0][1] + self.assertNotIn("baselineId", called_url) + @patch('lobster.tools.codebeamer.codebeamer.query_cb_single') def test_get_query_with_invalid_data(self, mock_query_cb_single): query_id = 789 From 53bfadbdcf63e75614038ee24132b1b8606d9a83 Mon Sep 17 00:00:00 2001 From: Matthias Eismann Date: Wed, 20 May 2026 15:14:11 +0200 Subject: [PATCH 4/8] fix: use f-string formatting in get_query str branch Replace %-style URL formatting with f-string (consistent with the int branch) while keeping baseline_id logic intact. --- lobster/tools/codebeamer/codebeamer.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lobster/tools/codebeamer/codebeamer.py b/lobster/tools/codebeamer/codebeamer.py index 2806a7d6..d19dac07 100755 --- a/lobster/tools/codebeamer/codebeamer.py +++ b/lobster/tools/codebeamer/codebeamer.py @@ -230,15 +230,10 @@ def get_query(cb_config: Config, query: Union[int, str]): url = (f"{cb_config.base}/reports/{query}/items" f"?page={page_id}&pageSize={cb_config.page_size}") elif isinstance(query, str): - url = ("%s/items/query?page=%u&pageSize=%u&queryString=%s" % - (cb_config.base, - page_id, - cb_config.page_size, - query)) - if cb_config.baseline_id is not None: - url += f"&baselineId={cb_config.baseline_id}" url = (f"{cb_config.base}/items/query?page={page_id}" f"&pageSize={cb_config.page_size}&queryString={query}") + if cb_config.baseline_id is not None: + url += f"&baselineId={cb_config.baseline_id}" data = query_cb_single(cb_config, url) if len(data) != 4: raise MismatchException( From 9b3a90ba122fb569b630da4b50e75313dd07f10b Mon Sep 17 00:00:00 2001 From: Matthias Eismann Date: Tue, 9 Jun 2026 08:45:41 +0200 Subject: [PATCH 5/8] fix(codebeamer): address PR #587 review - error on invalid baseline_id combos - Raise KeyError when baseline_id is combined with import_tagged or numeric import_query (instead of silently ignoring) - Change baseline_id type validation from KeyError to ValueError - Catch KeyError in _run_impl for clean exit code 1 - Remove 'silently ignored' behavioral note from docs - Update CHANGELOG.md with baseline_id feature entry - Add system test for baseline_id validation (test_baseline_id.py) - Extend test runner ConfigFileData with import_tagged and baseline_id --- CHANGELOG.md | 7 + documentation/manual-lobster_codebeamer.md | 6 +- lobster/tools/codebeamer/codebeamer.py | 18 ++- manual/tools/codebeamer.rst | 9 +- .../lobster_codebeamer_test_runner.py | 4 + .../lobster_codebeamer/test_baseline_id.py | 124 ++++++++++++++++++ 6 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 tests_system/lobster_codebeamer/test_baseline_id.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 79da4604..bd7cabcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ ### 1.0.4-dev +* `lobster-codebeamer`: + - Added `baseline_id` support for cbQL queries. When set, the tool queries + items at the specified Codebeamer baseline revision. + - `baseline_id` combined with `import_tagged` or a numeric `import_query` + now raises an error instead of being silently ignored (ISO 26262 TCL3 + qualification support). + ### 1.0.3 diff --git a/documentation/manual-lobster_codebeamer.md b/documentation/manual-lobster_codebeamer.md index ee48ddee..8d09c4e6 100644 --- a/documentation/manual-lobster_codebeamer.md +++ b/documentation/manual-lobster_codebeamer.md @@ -134,8 +134,10 @@ baseline_id: 407126303 ``` > **Important:** `baseline_id` is only applied when `import_query` is a **cbQL query -> string**. When `import_query` is a numeric report ID, the parameter is silently -> ignored and the current HEAD state is returned instead. +> string**. When `import_query` is a numeric report ID or `import_tagged` (a path to +> an existing LOBSTER artifact for tag-based import, see +> [Importing only tagged requirements](#importing-only-tagged-requirements)) is set, +> `lobster-codebeamer` exits with an error (exit code 1). ### Importing only tagged requirements diff --git a/lobster/tools/codebeamer/codebeamer.py b/lobster/tools/codebeamer/codebeamer.py index d19dac07..171fea44 100755 --- a/lobster/tools/codebeamer/codebeamer.py +++ b/lobster/tools/codebeamer/codebeamer.py @@ -537,14 +537,26 @@ def parse_config_data(data: dict) -> Config: f"but value is {config.cb_auth_conf.root}.") if config.baseline_id is not None: + if config.import_tagged: + raise KeyError( + f"The keys {SupportedConfigKeys.BASELINE_ID.value} and " + f"{SupportedConfigKeys.IMPORT_TAGGED.value} are both present " + f"in the configuration, but they are mutually exclusive!" + ) + if config.import_query and not isinstance(config.import_query, str): + raise KeyError( + f"The key {SupportedConfigKeys.BASELINE_ID.value} is only " + f"allowed if {SupportedConfigKeys.IMPORT_QUERY.value} is a " + f"cbQL query string, not a numeric report ID!" + ) try: config.baseline_id = int(config.baseline_id) except (TypeError, ValueError) as exc: - raise KeyError( + raise ValueError( f"{SupportedConfigKeys.BASELINE_ID.value} must be a positive integer." ) from exc if config.baseline_id <= 0: - raise KeyError( + raise ValueError( f"{SupportedConfigKeys.BASELINE_ID.value} must be a positive integer." ) @@ -587,6 +599,8 @@ def _run_impl(self, options: argparse.Namespace) -> int: ) except ValueError as value_error: self._print_error(value_error) + except KeyError as key_error: + self._print_error(key_error) except LOBSTER_Error as lobster_error: self._print_error(lobster_error) diff --git a/manual/tools/codebeamer.rst b/manual/tools/codebeamer.rst index c9513146..fde29a20 100644 --- a/manual/tools/codebeamer.rst +++ b/manual/tools/codebeamer.rst @@ -44,7 +44,7 @@ Config references=[], import_tagged=None, import_query=1234, # report ID or cbQL query string - baseline_id=None, # only used when import_query is a cbQL string + baseline_id=None, verify_ssl=True, page_size=100, schema="requirement", # or "implementation" / "activity" @@ -58,7 +58,7 @@ Config - ``references`` (List[str]): Names of Codebeamer fields whose referenced items should be traced (converted to ``req`` tags). - ``import_tagged`` (str | None): Path to an existing LOBSTER artifact whose unresolved ``req`` references define item IDs to import. - ``import_query`` (int | str | None): Report ID (int) or cbQL query string used to fetch items directly. -- ``baseline_id`` (int | None): Codebeamer baseline ID to query against. **Only effective when** ``import_query`` **is a cbQL query string**; ignored when ``import_query`` is a numeric report ID. +- ``baseline_id`` (int | None): Codebeamer baseline ID to query against. **Only allowed when** ``import_query`` **is a cbQL query string**. Raises an error if combined with ``import_tagged`` or a numeric ``import_query``. - ``verify_ssl`` (bool): Whether to verify TLS certificates; set ``True`` in production for security. - ``page_size`` (int): Pagination size for REST queries; a trade-off between round trips and response size (default typically 100). - ``schema`` (str): Target schema type (``requirement``, ``implementation``, ``activity``) controlling class/namespace mapping. @@ -114,7 +114,6 @@ Core Goals Behavioral Notes ---------------- - ``import_tagged`` overrides ``import_query`` if both provided. -- ``baseline_id`` is silently ignored when ``import_query`` is a numeric report ID; use a cbQL string instead. - ``schema`` maps to namespace class: requirement→``req`` / implementation→``imp`` / activity→``act``. - Exponential backoff is implicit (1s, 2s, 4s...) based on ``num_request_retry``. - Missing ``token`` triggers user/password or ``~/.netrc`` resolution. @@ -125,4 +124,6 @@ Error Conditions - Absent both ``import_query`` & ``import_tagged`` → ``KeyError``. - ``num_request_retry <= 0`` → ``ValueError``. - Unrecognised ``schema`` → ``KeyError``. -- ``baseline_id`` is not a positive integer → ``KeyError``. +- ``baseline_id`` is not a positive integer → ``ValueError``. +- ``baseline_id`` combined with ``import_tagged`` → ``KeyError``. +- ``baseline_id`` combined with numeric ``import_query`` → ``KeyError``. diff --git a/tests_system/lobster_codebeamer/lobster_codebeamer_test_runner.py b/tests_system/lobster_codebeamer/lobster_codebeamer_test_runner.py index 229d469a..13f24a03 100644 --- a/tests_system/lobster_codebeamer/lobster_codebeamer_test_runner.py +++ b/tests_system/lobster_codebeamer/lobster_codebeamer_test_runner.py @@ -9,6 +9,8 @@ @dataclass class ConfigFileData: import_query: Optional[Union[int, str]] = None + import_tagged: Optional[str] = None + baseline_id: Optional[int] = None root: Optional[str] = None token: Optional[str] = None out: Optional[str] = None @@ -31,6 +33,8 @@ def append_if_not_none(key, value): data[key] = value append_if_not_none("import_query", self.import_query) + append_if_not_none("import_tagged", self.import_tagged) + append_if_not_none("baseline_id", self.baseline_id) append_if_not_none("root", self.root) append_if_not_none("token", self.token) append_if_not_none("out", self.out) diff --git a/tests_system/lobster_codebeamer/test_baseline_id.py b/tests_system/lobster_codebeamer/test_baseline_id.py new file mode 100644 index 00000000..a4de1279 --- /dev/null +++ b/tests_system/lobster_codebeamer/test_baseline_id.py @@ -0,0 +1,124 @@ +import json +import unittest +from flask import Response +from tests_system.lobster_codebeamer.lobster_codebeamer_system_test_case_base import ( + LobsterCodebeamerSystemTestCaseBase) +from tests_system.asserter import Asserter +from tests_system.lobster_codebeamer.lobster_codebeamer_asserter import ( + LobsterCodebeamerAsserter) +from tests_system.lobster_codebeamer.mock_server_setup import get_mock_app + + +class LobsterCodebeamerBaselineIdTest(LobsterCodebeamerSystemTestCaseBase): + """System tests for baseline_id validation and usage.""" + + @classmethod + def setUpClass(cls): + cls.codebeamer_flask = get_mock_app() + + def setUp(self): + super().setUp() + self.codebeamer_flask.reset() + self._test_runner = self.create_test_runner() + self._test_runner.config_file_data.verify_ssl = False + + def test_baseline_id_with_import_tagged_raises_error(self): + """Ensure baseline_id combined with import_tagged exits with error.""" + cfg = self._test_runner.config_file_data + cfg.set_default_root_token_out(self.codebeamer_flask.port) + cfg.import_tagged = "some_file.lobster" + cfg.baseline_id = 12345 + + completed_process = self._test_runner.run_tool_test() + asserter = Asserter(self, completed_process, self._test_runner) + asserter.assertExitCode(1) + self.assertIn("mutually exclusive", completed_process.stderr) + + def test_baseline_id_with_numeric_import_query_raises_error(self): + """Ensure baseline_id combined with a numeric import_query exits + with error.""" + cfg = self._test_runner.config_file_data + cfg.set_default_root_token_out(self.codebeamer_flask.port) + cfg.import_query = 9999 + cfg.baseline_id = 12345 + + completed_process = self._test_runner.run_tool_test() + asserter = Asserter(self, completed_process, self._test_runner) + asserter.assertExitCode(1) + self.assertIn("cbQL query string", completed_process.stderr) + + def test_baseline_id_negative_raises_error(self): + """Ensure a negative baseline_id exits with error.""" + cfg = self._test_runner.config_file_data + cfg.set_default_root_token_out(self.codebeamer_flask.port) + cfg.import_query = "tracker.id IN (123)" + cfg.baseline_id = -1 + + completed_process = self._test_runner.run_tool_test() + asserter = Asserter(self, completed_process, self._test_runner) + asserter.assertExitCode(1) + self.assertIn("must be a positive integer", completed_process.stderr) + + def test_baseline_id_zero_raises_error(self): + """Ensure baseline_id of 0 exits with error.""" + cfg = self._test_runner.config_file_data + cfg.set_default_root_token_out(self.codebeamer_flask.port) + cfg.import_query = "tracker.id IN (123)" + cfg.baseline_id = 0 + + completed_process = self._test_runner.run_tool_test() + asserter = Asserter(self, completed_process, self._test_runner) + asserter.assertExitCode(1) + self.assertIn("must be a positive integer", completed_process.stderr) + + def test_baseline_id_with_cbql_query_succeeds(self): + """Ensure baseline_id with a cbQL string query works and appends + baselineId to the URL.""" + cfg = self._test_runner.config_file_data + cfg.set_default_root_token_out(self.codebeamer_flask.port) + cfg.import_query = "tracker.id IN (123)" + cfg.baseline_id = 407126303 + + response_data = { + 'page': 1, + 'pageSize': 1, + 'total': 1, + 'items': [ + { + 'item': { + 'id': 5, + 'name': 'Requirement 5: Dynamic name', + 'description': 'Dynamic description', + 'status': { + 'id': 5, + 'name': 'Status 5', + 'type': 'ChoiceOptionReference' + }, + 'tracker': { + 'id': 5, + 'name': 'Tracker_Name_5', + 'type': 'TrackerReference', + }, + 'version': 1, + } + } + ] + } + self.codebeamer_flask.responses = [ + Response(json.dumps(response_data), status=200), + ] + self._test_runner.declare_output_file( + self._data_directory / cfg.out) + + completed_process = self._test_runner.run_tool_test() + asserter = Asserter(self, completed_process, self._test_runner) + asserter.assertExitCode(0) + + # Verify the baselineId parameter was included in the request URL + self.assertEqual(len(self.codebeamer_flask.received_requests), 1) + request_url = self.codebeamer_flask.received_requests[0]["url"] + self.assertIn("baselineId=407126303", request_url) + + +if __name__ == "__main__": + unittest.main() From c6e5389af51b4dca2004b04dba9841398b33708b Mon Sep 17 00:00:00 2001 From: Matthias Eismann Date: Tue, 9 Jun 2026 08:59:25 +0200 Subject: [PATCH 6/8] fix(test): use cbQL response format (unwrapped items) in baseline_id test The cbQL query endpoint returns items directly in the 'items' array, not wrapped in {'item': {...}} like the report-ID endpoint does. --- .../lobster_codebeamer/test_baseline_id.py | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/tests_system/lobster_codebeamer/test_baseline_id.py b/tests_system/lobster_codebeamer/test_baseline_id.py index a4de1279..e35379cb 100644 --- a/tests_system/lobster_codebeamer/test_baseline_id.py +++ b/tests_system/lobster_codebeamer/test_baseline_id.py @@ -85,22 +85,20 @@ def test_baseline_id_with_cbql_query_succeeds(self): 'total': 1, 'items': [ { - 'item': { + 'id': 5, + 'name': 'Requirement 5: Dynamic name', + 'description': 'Dynamic description', + 'status': { 'id': 5, - 'name': 'Requirement 5: Dynamic name', - 'description': 'Dynamic description', - 'status': { - 'id': 5, - 'name': 'Status 5', - 'type': 'ChoiceOptionReference' - }, - 'tracker': { - 'id': 5, - 'name': 'Tracker_Name_5', - 'type': 'TrackerReference', - }, - 'version': 1, - } + 'name': 'Status 5', + 'type': 'ChoiceOptionReference' + }, + 'tracker': { + 'id': 5, + 'name': 'Tracker_Name_5', + 'type': 'TrackerReference', + }, + 'version': 1, } ] } From 1dc7054dc3ba1450ef96ff335bb262672957b560 Mon Sep 17 00:00:00 2001 From: Matthias Eismann Date: Tue, 9 Jun 2026 09:46:20 +0200 Subject: [PATCH 7/8] ci: retrigger checks From 1f40db38cbad57189556240e1b9840926fe5ad44 Mon Sep 17 00:00:00 2001 From: Matthias Eismann Date: Mon, 22 Jun 2026 13:46:28 +0200 Subject: [PATCH 8/8] test(codebeamer): assert full error messages in baseline_id system tests Address PR review: compare the complete stderr output instead of only a substring (assertIn), since different errors may share the same words. --- .../lobster_codebeamer/test_baseline_id.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests_system/lobster_codebeamer/test_baseline_id.py b/tests_system/lobster_codebeamer/test_baseline_id.py index e35379cb..f4a27d51 100644 --- a/tests_system/lobster_codebeamer/test_baseline_id.py +++ b/tests_system/lobster_codebeamer/test_baseline_id.py @@ -32,7 +32,11 @@ def test_baseline_id_with_import_tagged_raises_error(self): completed_process = self._test_runner.run_tool_test() asserter = Asserter(self, completed_process, self._test_runner) asserter.assertExitCode(1) - self.assertIn("mutually exclusive", completed_process.stderr) + asserter.assertStdErrText( + "lobster-codebeamer: 'The keys baseline_id and import_tagged " + "are both present in the configuration, but they are mutually " + "exclusive!'\n" + ) def test_baseline_id_with_numeric_import_query_raises_error(self): """Ensure baseline_id combined with a numeric import_query exits @@ -45,7 +49,10 @@ def test_baseline_id_with_numeric_import_query_raises_error(self): completed_process = self._test_runner.run_tool_test() asserter = Asserter(self, completed_process, self._test_runner) asserter.assertExitCode(1) - self.assertIn("cbQL query string", completed_process.stderr) + asserter.assertStdErrText( + "lobster-codebeamer: 'The key baseline_id is only allowed if " + "import_query is a cbQL query string, not a numeric report ID!'\n" + ) def test_baseline_id_negative_raises_error(self): """Ensure a negative baseline_id exits with error.""" @@ -57,7 +64,9 @@ def test_baseline_id_negative_raises_error(self): completed_process = self._test_runner.run_tool_test() asserter = Asserter(self, completed_process, self._test_runner) asserter.assertExitCode(1) - self.assertIn("must be a positive integer", completed_process.stderr) + asserter.assertStdErrText( + "lobster-codebeamer: baseline_id must be a positive integer.\n" + ) def test_baseline_id_zero_raises_error(self): """Ensure baseline_id of 0 exits with error.""" @@ -69,7 +78,9 @@ def test_baseline_id_zero_raises_error(self): completed_process = self._test_runner.run_tool_test() asserter = Asserter(self, completed_process, self._test_runner) asserter.assertExitCode(1) - self.assertIn("must be a positive integer", completed_process.stderr) + asserter.assertStdErrText( + "lobster-codebeamer: baseline_id must be a positive integer.\n" + ) def test_baseline_id_with_cbql_query_succeeds(self): """Ensure baseline_id with a cbQL string query works and appends