Skip to content

Conversation

@tisnik
Copy link
Contributor

@tisnik tisnik commented Sep 11, 2025

Description

LCORE-657: unit tests for configuration.py
Removed dead code

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement

Related Tickets & Documents

  • Related Issue #LCORE-657

Summary by CodeRabbit

  • Bug Fixes

    • Avoids unnecessary errors when authentication settings are missing, allowing configuration access to proceed.
    • Ensures consistent "configuration is not loaded" error behavior when accessing configuration sections before load.
  • Tests

    • Expanded coverage for authentication, authorization, database, and inference configuration handling.
    • Verified these sections are populated when loading configuration from dict or YAML.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 11, 2025

Warning

Rate limit exceeded

@tisnik has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 8 minutes and 21 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between bd708c9 and d30ed3a.

📒 Files selected for processing (1)
  • tests/unit/test_configuration.py (5 hunks)

Walkthrough

Removed the explicit None-guard from AppConfig.authentication_configuration so the accessor returns the stored authentication value after verifying the overall configuration is loaded. Tests were expanded to reset AppConfig between tests, check "not loaded" behavior for more subsections, and assert presence of authentication/authorization/database/inference after loading.

Changes

Cohort / File(s) Summary of changes
Config accessor logic
src/configuration.py
Removed runtime None check in authentication_configuration accessor; method now returns self._configuration.authentication after verifying configuration is loaded (no explicit guard raising LogicError if authentication is None).
Unit tests
tests/unit/test_configuration.py
Added autouse fixture to reset AppConfig between tests. Replaced unused direct reads with assignments to silence lint. Added "not-loaded" checks for authentication_configuration, authorization_configuration, inference, and database_configuration. Extended init-from-dict/YAML tests to include and assert authentication, authorization, database, and inference sections; added a test for authorization_configuration initialization.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant AppConfig

  rect rgba(230,245,255,0.6)
  note right of AppConfig: Caller requests authentication_configuration
  Caller->>AppConfig: get authentication_configuration()
  alt Config not loaded
    AppConfig-->>Caller: raise LogicError("configuration is not loaded")
  else Config loaded
    par Old behavior (previous)
      AppConfig-->>Caller: raise LogicError if authentication is None
    and New behavior (current)
      AppConfig-->>Caller: return authentication (may be None)
    end
  end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Pre-merge checks (3 passed)

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "LCORE-657: unit tests for configuration.py" clearly and concisely describes the primary purpose of the changeset—adding unit tests for configuration.py—and aligns with the PR description and objectives which emphasize a "Unit tests improvement". While the PR also removes dead code and tweaks runtime validation, those appear secondary to the stated goal and do not make the title misleading.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.

Poem

In a burrow of bytes I hop and pry,
I unhook a guard and watch values fly.
Tests tidy the lair—auth, authz, DB, and more,
YAML seeds sprout on the configuration floor.
A twitch of whiskers, a joyful clap—code ready to explore! 🐇✨

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tisnik tisnik force-pushed the lcore-657-unit-tests-for-configuration.py branch from 61a0c3c to 527beb4 Compare September 11, 2025 07:44
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
tests/unit/test_configuration.py (4)

16-16: Tighten exception type: use LogicError instead of broad Exception in pytest.raises.

This makes tests stricter and consistent with the later tests that already expect LogicError.

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.service_configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.llama_stack_configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = (
             cfg.user_data_collection_configuration
         )  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.mcp_servers  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.authentication_configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.customization  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.authorization_configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.inference  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.database_configuration  # pylint: disable=pointless-statement

Also applies to: 20-20, 24-24, 28-30, 34-34, 38-38, 42-42, 44-46, 48-50, 52-54


86-88: Add negative case for authentication omitted (should return None after load).

Since authentication_configuration now returns the stored value (possibly None) post-load, cover that explicitly.

def test_authentication_configuration_absent_returns_none() -> None:
    cfg = AppConfig()
    cfg.init_from_dict({
        "name": "foo",
        "service": {"host": "localhost", "port": 8080, "auth_enabled": False, "workers": 1, "color_log": True, "access_log": True},
        "llama_stack": {"api_key": "xyzzy", "url": "http://x.y.com:1234", "use_as_library_client": False},
        "user_data_collection": {"feedback_enabled": False},
        # no "authentication" key
        "mcp_servers": [],
        "customization": None,
    })
    assert cfg.authentication_configuration is None

118-130: Broaden post-load assertions to check types/defaults, not just non-None.

Optional, but asserting types or representative defaults gives stronger guarantees than existence checks.

  • For authentication: also assert that module == "noop" is the expected default behavior for the provided dict (already done).
  • For authorization/database/inference: assert a type or a sentinel field if available (e.g., isinstance(..., AuthorizationConfiguration)).

Example:

# if available to import
from configuration import AuthorizationConfiguration, DatabaseConfiguration, InferenceConfiguration

assert isinstance(cfg.authorization_configuration, AuthorizationConfiguration)
assert isinstance(cfg.database_configuration, DatabaseConfiguration)
assert isinstance(cfg.inference, InferenceConfiguration)

177-204: Fix docstring wording and consider asserting defaults for explicit empty authorization.

Minor wording nit and optional stronger check that {} maps to a valid default object.

-def test_init_from_dict_with_authorization_configuration() -> None:
-    """Test initialization with authorization configuration configuration."""
+def test_init_from_dict_with_authorization_configuration() -> None:
+    """Test initialization with explicit empty authorization configuration."""

Optionally, add:

# if type is importable
from configuration import AuthorizationConfiguration
assert isinstance(cfg.authorization_configuration, AuthorizationConfiguration)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68a6dd1 and 527beb4.

📒 Files selected for processing (2)
  • src/configuration.py (0 hunks)
  • tests/unit/test_configuration.py (4 hunks)
💤 Files with no reviewable changes (1)
  • src/configuration.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/unit/test_configuration.py (1)
src/configuration.py (6)
  • configuration (60-64)
  • authentication_configuration (95-100)
  • authorization_configuration (103-111)
  • inference (121-125)
  • database_configuration (128-132)
  • init_from_dict (55-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-pr

@tisnik tisnik force-pushed the lcore-657-unit-tests-for-configuration.py branch from 527beb4 to bd708c9 Compare September 11, 2025 07:56
@tisnik tisnik force-pushed the lcore-657-unit-tests-for-configuration.py branch from bd708c9 to d30ed3a Compare September 11, 2025 08:03
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
tests/unit/test_configuration.py (5)

8-20: Tighten the autouse reset fixture (fix linter warnings, narrow exception scope).

Good call adding the isolation fixture. Two polish items:

  • Remove broad Exception catching (W0718) and use hasattr instead.
  • Silence protected-access locally to avoid W0212 without affecting the module.

Apply:

 @pytest.fixture(autouse=True)
 def _reset_app_config_between_tests():
-    # ensure clean state before each test
-    try:
-        AppConfig()._configuration = None  # type: ignore[attr-defined]
-    except Exception:
-        pass
-    yield
-    # ensure clean state after each test
-    try:
-        AppConfig()._configuration = None  # type: ignore[attr-defined]
-    except Exception:
-        pass
+    # ensure clean state before each test
+    cfg = AppConfig()
+    # pylint: disable=protected-access
+    if hasattr(cfg, "_configuration"):
+        cfg._configuration = None  # type: ignore[attr-defined]
+    yield
+    # ensure clean state after each test
+    # pylint: disable=protected-access
+    if hasattr(cfg, "_configuration"):
+        cfg._configuration = None  # type: ignore[attr-defined]

29-70: Assert the precise exception type (LogicError) for “not loaded” checks.

These guards currently expect Exception; tighten to LogicError for accuracy and stronger guarantees.

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.service_configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.llama_stack_configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = (
             cfg.user_data_collection_configuration
         )  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.mcp_servers  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.authentication_configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.customization  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.authorization_configuration  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.inference  # pylint: disable=pointless-statement

-    with pytest.raises(Exception, match="logic error: configuration is not loaded"):
+    with pytest.raises(LogicError, match="logic error: configuration is not loaded"):
         _ = cfg.database_configuration  # pylint: disable=pointless-statement

Optional: parametrize the properties to de-duplicate the repetitive blocks. I can draft that if you want.


133-145: Add type assertions for stronger guarantees on new subsections.

Great to check presence. Assert expected types too for clearer failure messages and future refactors.

     # check authentication_configuration
     assert cfg.authentication_configuration is not None
     assert cfg.authentication_configuration.module == "noop"

     # check authorization configuration - default value
     assert cfg.authorization_configuration is not None

     # check database configuration
-    assert cfg.database_configuration is not None
+    assert cfg.database_configuration is not None
+    # optional: validate type if exported in public API
+    # from models.config import DatabaseConfiguration
+    # assert isinstance(cfg.database_configuration, DatabaseConfiguration)

     # check inference configuration
-    assert cfg.inference is not None
+    assert cfg.inference is not None
+    # optional: validate type if exported in public API
+    # from models.config import InferenceConfiguration
+    # assert isinstance(cfg.inference, InferenceConfiguration)

192-219: Fix docstring typo and add a minimal behavior assertion.

Minor wording nit; also consider a minimal semantic check to ensure an object is actually returned.

-def test_init_from_dict_with_authorization_configuration() -> None:
-    """Test initialization with authorization configuration configuration."""
+def test_init_from_dict_with_authorization_configuration() -> None:
+    """Test initialization with authorization configuration."""
@@
     cfg.init_from_dict(config_dict)

-    assert cfg.authorization_configuration is not None
+    assert cfg.authorization_configuration is not None
+    # optional: check that defaults are applied when empty dict provided
+    # e.g., assert getattr(cfg.authorization_configuration, "rules", None) is not None

1-498: Potential singleton pitfall: re-instantiation may reset configuration.

Unrelated to test logic but worth verifying: if AppConfig.init sets self._configuration = None unconditionally, calling AppConfig() elsewhere could wipe loaded state. Add a regression test ensuring a second instantiation does not reset configuration, or guard init in src with hasattr checks.

I can open a follow-up issue and supply the test if desired.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 527beb4 and bd708c9.

📒 Files selected for processing (1)
  • tests/unit/test_configuration.py (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/unit/test_configuration.py (1)
src/configuration.py (12)
  • AppConfig (32-132)
  • configuration (60-64)
  • service_configuration (67-71)
  • llama_stack_configuration (74-78)
  • user_data_collection_configuration (81-85)
  • mcp_servers (88-92)
  • authentication_configuration (95-100)
  • customization (114-118)
  • authorization_configuration (103-111)
  • inference (121-125)
  • database_configuration (128-132)
  • init_from_dict (55-57)
🪛 GitHub Actions: Python linter
tests/unit/test_configuration.py

[warning] 12-12: W0212: Access to a protected member '_configuration' of a client class (protected-access).


[warning] 13-13: W0718: Catching too general exception 'Exception' (broad-exception-caught).


[warning] 18-18: W0212: Access to a protected member '_configuration' of a client class (protected-access).


[warning] 19-19: W0718: Catching too general exception 'Exception' (broad-exception-caught).

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-pr
  • GitHub Check: e2e_tests

@tisnik tisnik merged commit d5e2440 into lightspeed-core:main Sep 11, 2025
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant