Skip to content

🧪 [testing improvement] Untested Exception Path in firebase_utils.reference#135

Closed
DaTiC0 wants to merge 8 commits into
mainfrom
test-firebase-utils-exception-4205446835027658903
Closed

🧪 [testing improvement] Untested Exception Path in firebase_utils.reference#135
DaTiC0 wants to merge 8 commits into
mainfrom
test-firebase-utils-exception-4205446835027658903

Conversation

@DaTiC0

@DaTiC0 DaTiC0 commented Jun 6, 2026

Copy link
Copy Markdown
Owner

🎯 What: The testing gap addressed (Exception path in firebase_utils.reference).
📊 Coverage: Scenarios now tested (Firebase availability, exception handling, user scope normalization, mock data implementation).
Result: Improved reliability and coverage for Firebase utility functions.


PR created automatically by Jules for task 4205446835027658903 started by @DaTiC0

Summary by Sourcery

Add comprehensive tests for Firebase utility reference behavior and mock data handling.

Tests:

  • Add unit tests covering firebase_utils.reference for root and user paths, Firebase availability, and exception fallback to mock data.
  • Add tests for user scope normalization and validation of user and device IDs in Firebase references.
  • Add tests verifying MockRef and MockChild behavior, including nested child access, non-existent paths, and state updates.

…erence

- Added comprehensive tests for firebase_utils.py.
- Verified exception handling path in reference() function.
- Tested user scope normalization and device state reference building.
- Added tests for MockRef and MockChild classes.

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@sourcery-ai

sourcery-ai Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a comprehensive unittest suite for firebase_utils, covering the firebase reference behavior (including exception paths), user scope normalization, mock references, and user device state reference helpers using firebase_admin mocks.

File-Level Changes

Change Details Files
Introduce unit tests for firebase_utils using firebase_admin mocks to cover normal, error, and fallback paths.
  • Set up global firebase_admin and db mocks in sys.modules prior to importing firebase_utils so FIREBASE_AVAILABLE is True by default in tests.
  • Add tests for reference() covering root and user-specific paths when Firebase is available, when it raises initialization exceptions (logging and MockRef fallback), and when Firebase is marked unavailable.
  • Add tests for _normalize_user_scope() validating trimming, type coercion, and rejection of unsafe user IDs.
  • Add tests for _get_user_device_states_ref() to verify correct Firebase path traversal and input validation for invalid user/device IDs.
  • Add tests for MockRef and MockChild behavior, including nested child lookups, missing paths, and update() on shared MOCK_DEVICES with state reset after mutation.
tests/test_firebase_utils.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new test suite tests/test_firebase_utils.py to verify the functionality of Firebase utility methods, including mock reference behaviors, normalization, and exception handling. The review feedback highlights a potential issue with test pollution due to global modifications of sys.modules at the module level. It is recommended to save and restore the original state of sys.modules using a tearDownModule function to ensure proper test isolation.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread tests/test_firebase_utils.py Outdated
Comment on lines +6 to +17
# Setup mocks for firebase_admin before importing firebase_utils
mock_db = MagicMock()
mock_firebase_admin = MagicMock()
mock_firebase_admin.db = mock_db
sys.modules['firebase_admin'] = mock_firebase_admin
sys.modules['firebase_admin.db'] = mock_db

# Clear firebase_utils from sys.modules to ensure a fresh import with our mocks
if 'firebase_utils' in sys.modules:
del sys.modules['firebase_utils']

import firebase_utils

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Modifying sys.modules globally at the module level without restoring it can lead to test pollution, where other test files in the suite are affected by these mocks. This can cause flaky tests or unexpected failures depending on the order in which tests are executed.

To ensure proper test isolation, we should save the original state of sys.modules and restore it in a tearDownModule function.

# Save original modules to prevent test pollution
_original_modules = {
    name: sys.modules.get(name)
    for name in ['firebase_admin', 'firebase_admin.db', 'firebase_utils']
}

# Setup mocks for firebase_admin before importing firebase_utils
mock_db = MagicMock()
mock_firebase_admin = MagicMock()
mock_firebase_admin.db = mock_db
sys.modules['firebase_admin'] = mock_firebase_admin
sys.modules['firebase_admin.db'] = mock_db

# Clear firebase_utils from sys.modules to ensure a fresh import with our mocks
if 'firebase_utils' in sys.modules:
    del sys.modules['firebase_utils']

import firebase_utils

def tearDownModule():
    # Restore original sys.modules state to prevent test pollution
    for name, module in _original_modules.items():
        if module is None:
            sys.modules.pop(name, None)
        else:
            sys.modules[name] = module

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The manual manipulation of sys.modules to inject firebase_admin and firebase_admin.db could leak into other tests; consider capturing and restoring the original sys.modules entries in a module-level setup/teardown or context to avoid cross-test pollution.
  • The if __name__ == '__main__': unittest.main() block is not needed when the test suite is run by the project's test runner and can be removed to keep the test file focused on its test definitions.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The manual manipulation of `sys.modules` to inject `firebase_admin` and `firebase_admin.db` could leak into other tests; consider capturing and restoring the original `sys.modules` entries in a module-level setup/teardown or context to avoid cross-test pollution.
- The `if __name__ == '__main__': unittest.main()` block is not needed when the test suite is run by the project's test runner and can be removed to keep the test file focused on its test definitions.

## Individual Comments

### Comment 1
<location path="tests/test_firebase_utils.py" line_range="64-75" />
<code_context>
+            self.assertIsInstance(ref, firebase_utils.MockRef)
+            mock_db.reference.assert_not_called()
+
+    def test_normalize_user_scope(self):
+        self.assertEqual(firebase_utils._normalize_user_scope("user123"), "user123")
+        self.assertEqual(firebase_utils._normalize_user_scope(123), "123")
+        self.assertEqual(firebase_utils._normalize_user_scope("  user123  "), "user123")
+        self.assertIsNone(firebase_utils._normalize_user_scope(None))
+        self.assertIsNone(firebase_utils._normalize_user_scope(""))
+        self.assertIsNone(firebase_utils._normalize_user_scope("   "))
+        self.assertIsNone(firebase_utils._normalize_user_scope("user/123"))
+        self.assertIsNone(firebase_utils._normalize_user_scope("user\\123"))
+        self.assertIsNone(firebase_utils._normalize_user_scope("user..123"))
+
+    def test_get_user_device_states_ref_valid(self):
</code_context>
<issue_to_address>
**suggestion (testing):** Add integration tests that exercise `_normalize_user_scope` via `reference()` to prove the normalized value is actually used in paths.

Currently `_normalize_user_scope` is only tested in isolation. Please add an integration-style test for `reference(user_id=...)` that asserts the Firebase path uses the normalized value, e.g.:
- `reference(user_id="  user123  ")` calls `mock_db.reference("/users/user123/devices")`
- `reference(user_id=123)` calls `mock_db.reference("/users/123/devices")`
This guards against regressions where `reference` stops using `_normalize_user_scope` correctly.

```suggestion
    def test_normalize_user_scope(self):
        self.assertEqual(firebase_utils._normalize_user_scope("user123"), "user123")
        self.assertEqual(firebase_utils._normalize_user_scope(123), "123")
        self.assertEqual(firebase_utils._normalize_user_scope("  user123  "), "user123")
        self.assertIsNone(firebase_utils._normalize_user_scope(None))
        self.assertIsNone(firebase_utils._normalize_user_scope(""))
        self.assertIsNone(firebase_utils._normalize_user_scope("   "))
        self.assertIsNone(firebase_utils._normalize_user_scope("user/123"))
        self.assertIsNone(firebase_utils._normalize_user_scope("user\\123"))
        self.assertIsNone(firebase_utils._normalize_user_scope("user..123"))

    def test_reference_uses_normalized_user_scope_str_input(self):
        with patch('firebase_utils.FIREBASE_AVAILABLE', True):
            # Ensure reference() routes to the real db mock and uses the normalized user_id in the path
            firebase_utils.db = mock_db
            mock_db.reference.reset_mock()

            firebase_utils.reference(user_id="  user123  ")

            mock_db.reference.assert_called_once_with("/users/user123/devices")

    def test_reference_uses_normalized_user_scope_int_input(self):
        with patch('firebase_utils.FIREBASE_AVAILABLE', True):
            # Ensure integer user_id is normalized to its string representation in the path
            firebase_utils.db = mock_db
            mock_db.reference.reset_mock()

            firebase_utils.reference(user_id=123)

            mock_db.reference.assert_called_once_with("/users/123/devices")

    def test_get_user_device_states_ref_valid(self):
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +64 to +75
def test_normalize_user_scope(self):
self.assertEqual(firebase_utils._normalize_user_scope("user123"), "user123")
self.assertEqual(firebase_utils._normalize_user_scope(123), "123")
self.assertEqual(firebase_utils._normalize_user_scope(" user123 "), "user123")
self.assertIsNone(firebase_utils._normalize_user_scope(None))
self.assertIsNone(firebase_utils._normalize_user_scope(""))
self.assertIsNone(firebase_utils._normalize_user_scope(" "))
self.assertIsNone(firebase_utils._normalize_user_scope("user/123"))
self.assertIsNone(firebase_utils._normalize_user_scope("user\\123"))
self.assertIsNone(firebase_utils._normalize_user_scope("user..123"))

def test_get_user_device_states_ref_valid(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Add integration tests that exercise _normalize_user_scope via reference() to prove the normalized value is actually used in paths.

Currently _normalize_user_scope is only tested in isolation. Please add an integration-style test for reference(user_id=...) that asserts the Firebase path uses the normalized value, e.g.:

  • reference(user_id=" user123 ") calls mock_db.reference("/users/user123/devices")
  • reference(user_id=123) calls mock_db.reference("/users/123/devices")
    This guards against regressions where reference stops using _normalize_user_scope correctly.
Suggested change
def test_normalize_user_scope(self):
self.assertEqual(firebase_utils._normalize_user_scope("user123"), "user123")
self.assertEqual(firebase_utils._normalize_user_scope(123), "123")
self.assertEqual(firebase_utils._normalize_user_scope(" user123 "), "user123")
self.assertIsNone(firebase_utils._normalize_user_scope(None))
self.assertIsNone(firebase_utils._normalize_user_scope(""))
self.assertIsNone(firebase_utils._normalize_user_scope(" "))
self.assertIsNone(firebase_utils._normalize_user_scope("user/123"))
self.assertIsNone(firebase_utils._normalize_user_scope("user\\123"))
self.assertIsNone(firebase_utils._normalize_user_scope("user..123"))
def test_get_user_device_states_ref_valid(self):
def test_normalize_user_scope(self):
self.assertEqual(firebase_utils._normalize_user_scope("user123"), "user123")
self.assertEqual(firebase_utils._normalize_user_scope(123), "123")
self.assertEqual(firebase_utils._normalize_user_scope(" user123 "), "user123")
self.assertIsNone(firebase_utils._normalize_user_scope(None))
self.assertIsNone(firebase_utils._normalize_user_scope(""))
self.assertIsNone(firebase_utils._normalize_user_scope(" "))
self.assertIsNone(firebase_utils._normalize_user_scope("user/123"))
self.assertIsNone(firebase_utils._normalize_user_scope("user\\123"))
self.assertIsNone(firebase_utils._normalize_user_scope("user..123"))
def test_reference_uses_normalized_user_scope_str_input(self):
with patch('firebase_utils.FIREBASE_AVAILABLE', True):
# Ensure reference() routes to the real db mock and uses the normalized user_id in the path
firebase_utils.db = mock_db
mock_db.reference.reset_mock()
firebase_utils.reference(user_id=" user123 ")
mock_db.reference.assert_called_once_with("/users/user123/devices")
def test_reference_uses_normalized_user_scope_int_input(self):
with patch('firebase_utils.FIREBASE_AVAILABLE', True):
# Ensure integer user_id is normalized to its string representation in the path
firebase_utils.db = mock_db
mock_db.reference.reset_mock()
firebase_utils.reference(user_id=123)
mock_db.reference.assert_called_once_with("/users/123/devices")
def test_get_user_device_states_ref_valid(self):

google-labs-jules Bot and others added 7 commits June 6, 2026 10:37
…erence

- Added comprehensive tests for firebase_utils.py.
- Verified exception handling path in reference() function.
- Tested user scope normalization and device state reference building.
- Added tests for MockRef and MockChild classes.
- Removed unused logging import in test file.

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>
…erence

- Added comprehensive tests for firebase_utils.py.
- Verified exception handling path in reference() function.
- Tested user scope normalization and device state reference building.
- Added tests for MockRef and MockChild classes.
- Removed unused logging import in test file.

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>
…erence

- Added comprehensive tests for firebase_utils.py.
- Verified exception handling path in reference() function.
- Tested user scope normalization and device state reference building.
- Added tests for MockRef and MockChild classes.
- Removed unused logging import in test file.

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>
…erence

- Added comprehensive tests for firebase_utils.py.
- Verified exception handling path in reference() function.
- Tested user scope normalization and device state reference building.
- Added tests for MockRef and MockChild classes.
- Removed unused logging import in test file.

Co-authored-by: DaTiC0 <13198638+DaTiC0@users.noreply.github.com>
…erence

- Added comprehensive tests for firebase_utils.py.
- Verified exception handling path in reference() function.
- Improved test isolation for exception path to ensure it passes in CI environment.
- Fixed lint issues in test files.
- Tested user scope normalization and device state reference building.
- Added tests for MockRef and MockChild classes.
…erence

- Added comprehensive tests for firebase_utils.py.
- Verified exception handling path in reference() function.
- Improved test isolation for exception path to ensure it passes in CI environment.
- Fixed lint issues in test files.
- Tested user scope normalization and device state reference building.
- Added tests for MockRef and MockChild classes.
@DaTiC0 DaTiC0 closed this Jun 8, 2026
@DaTiC0 DaTiC0 deleted the test-firebase-utils-exception-4205446835027658903 branch June 8, 2026 14:21
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