Skip to content

test(resilience): add CircuitBreaker tests and fix HALF_OPEN bug#77

Closed
JuanCS-Dev wants to merge 2 commits intomainfrom
test-circuit-breaker-improvement-7174230857590657879
Closed

test(resilience): add CircuitBreaker tests and fix HALF_OPEN bug#77
JuanCS-Dev wants to merge 2 commits intomainfrom
test-circuit-breaker-improvement-7174230857590657879

Conversation

@JuanCS-Dev
Copy link
Owner

Added comprehensive unit tests for CircuitBreaker covering state transitions (CLOSED, OPEN, HALF_OPEN) and error handling. Fixed a bug where HALF_OPEN state would deadlock if success_threshold > 1.Verified with new tests passing. Existing resilience tests (e.g., tests/test_recovery_enhancements.py) also pass. Note: tests/providers/test_resilience.py is currently broken due to unrelated import errors and API mismatches.


PR created automatically by Jules for task 7174230857590657879 started by @JuanCS-Dev

…HALF_OPEN bug

- Added `tests/unit/core/resilience/test_circuit_breaker.py` to cover state transitions and error handling.
- Fixed a bug in `packages/vertice-core/src/vertice_core/resilience/circuit_breaker.py` where the circuit would deadlock in `HALF_OPEN` state if `success_threshold` was greater than 1. Now `_half_open_pending` is reset on success if the threshold is not yet reached, allowing subsequent probes.

Co-authored-by: JuanCS-Dev <227056558+JuanCS-Dev@users.noreply.github.com>
@google-labs-jules
Copy link
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.

@gemini-code-assist
Copy link

Summary of Changes

Hello @JuanCS-Dev, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical bug within the CircuitBreaker implementation that could lead to deadlocks in the HALF_OPEN state under specific conditions. Concurrently, it significantly enhances the test coverage for the CircuitBreaker by adding a new suite of unit tests, ensuring the robustness and correct behavior of its state management and error handling mechanisms.

Highlights

  • Bug Fix: Resolved a deadlock issue in the CircuitBreaker's HALF_OPEN state when success_threshold > 1 by ensuring the _half_open_pending flag is correctly reset.
  • Testing: Introduced comprehensive unit tests for the CircuitBreaker to cover state transitions (CLOSED, OPEN, HALF_OPEN), failure/success counting, request blocking, and the protect decorator.
Changelog
  • packages/vertice-core/src/vertice_core/resilience/circuit_breaker.py
    • Reset _half_open_pending flag in _record_success when in HALF_OPEN state but success_threshold is not yet met, preventing deadlocks.
  • tests/unit/core/resilience/test_circuit_breaker.py
    • Added new test file for CircuitBreaker unit tests.
    • Included tests for initial state, successful/failed executions, and transitions between CLOSED, OPEN, and HALF_OPEN states.
    • Verified blocking behavior in OPEN and HALF_OPEN (with pending probe) states.
    • Tested the protect decorator and reset functionality.
Activity
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added the testing Testing related label Feb 12, 2026
Copy link

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

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 crucial fix for a potential deadlock in the HALF_OPEN state of the CircuitBreaker and adds a comprehensive suite of unit tests. The fix correctly allows for multiple probe requests when the success threshold is greater than one. The new tests are well-structured and cover all state transitions, significantly improving the robustness of the resilience module. My feedback focuses on enhancing the new tests by making assertions more specific and avoiding direct manipulation of internal state, which will improve their clarity and long-term maintainability.

Comment on lines +94 to +110
assert circuit_breaker.state == CircuitState.HALF_OPEN or circuit_breaker.state == CircuitState.CLOSED
# Note: If execution succeeds immediately, it might transition to CLOSED depending on success_threshold.
# In our config, success_threshold=2, so one success keeps it HALF_OPEN or moves towards CLOSED.
# Wait, execute calls _record_success. If success_threshold is 2, one success means 1/2.
# So state should still be HALF_OPEN *before* the second success?
# Actually, `execute` checks state *before* running.
# If it was OPEN, and time passed, `_check_state` transitions to HALF_OPEN.
# Then it runs. Then `_record_success` runs.

# Let's check logic:
# 1. execute -> _check_state -> (OPEN & time passed) -> _transition_to(HALF_OPEN)
# 2. execute runs func -> success
# 3. execute -> _record_success -> consecutive_successes=1.
# If 1 < 2, remains HALF_OPEN.

# However, since we mock datetime, let's verify exact behavior step by step if needed.
# But here we just want to verify it entered HALF_OPEN and executed.

Choose a reason for hiding this comment

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

medium

This test contains a lot of commented-out developer notes that should be removed to improve readability. Additionally, the assertion assert circuit_breaker.state == CircuitState.HALF_OPEN or circuit_breaker.state == CircuitState.CLOSED is too broad. Given the test's configuration (success_threshold=2), after a single successful call, the state should definitively be HALF_OPEN. A more precise assertion would make the test stronger. It's also good practice to assert that the mocked function was called to ensure the probe request was executed.

Suggested change
assert circuit_breaker.state == CircuitState.HALF_OPEN or circuit_breaker.state == CircuitState.CLOSED
# Note: If execution succeeds immediately, it might transition to CLOSED depending on success_threshold.
# In our config, success_threshold=2, so one success keeps it HALF_OPEN or moves towards CLOSED.
# Wait, execute calls _record_success. If success_threshold is 2, one success means 1/2.
# So state should still be HALF_OPEN *before* the second success?
# Actually, `execute` checks state *before* running.
# If it was OPEN, and time passed, `_check_state` transitions to HALF_OPEN.
# Then it runs. Then `_record_success` runs.
# Let's check logic:
# 1. execute -> _check_state -> (OPEN & time passed) -> _transition_to(HALF_OPEN)
# 2. execute runs func -> success
# 3. execute -> _record_success -> consecutive_successes=1.
# If 1 < 2, remains HALF_OPEN.
# However, since we mock datetime, let's verify exact behavior step by step if needed.
# But here we just want to verify it entered HALF_OPEN and executed.
assert circuit_breaker.state == CircuitState.HALF_OPEN
mock_func.assert_called_once()

Comment on lines +115 to +116
circuit_breaker._state = CircuitState.HALF_OPEN
circuit_breaker._stats.state_changed_at = datetime.utcnow()

Choose a reason for hiding this comment

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

medium

Directly manipulating internal state like _state and _stats makes tests brittle and overly dependent on the implementation details of the CircuitBreaker class. If these internal attributes are renamed or their logic changes, these tests will break. It's better to use the public API to bring the circuit breaker into the desired state for testing. For example, you can trigger failures to open the circuit, then mock time to transition it to HALF_OPEN. This makes tests more robust and also verifies the state transitions correctly. This approach of modifying internal state is also used in test_half_open_failure_reopens and test_half_open_concurrent_calls and should be refactored there as well.

- **CI/CD:**
    - Created `requirements-dev.txt` for development dependencies (fixing `Code Quality & Security` failure).
    - Updated `.github/workflows/quality.yml` to use `radon cc --min D` instead of invalid `--fail`.
    - Updated `.github/workflows/vertice-mcp-cicd.yaml` to point to correct paths for basic validation.
    - Updated `.github/workflows/tui_e2e.yaml` imports implicitly via `measure_quality.py`.
- **Linting & Code Quality:**
    - Fixed massive `F821` (undefined name) errors in `shell_main.py` and `cli/repl_masterpiece/repl.py` by adding missing tool and UI component imports.
    - Fixed `E721` (type comparison) in `adk/tools.py`.
    - Fixed `F601` (duplicate key) in `agents/planner/coordination.py`.
    - Fixed `F401` (unused imports) in `agents/planner/coordination.py`.
    - Fixed `E741` (ambiguous variable name `l`) in `autonomy/uncertainty.py`.
    - Fixed `F821` (forward reference) in `tui/core/response/view_compactor.py`.
- **Python Compatibility:**
    - Fixed `NotRequired` import in `core/types.py` for Python 3.10 compatibility using `typing_extensions`.
- **TUI E2E:**
    - Fixed `ImportError` in `scripts/e2e/measure_quality.py` by importing from `vertice_core.tui` instead of `vertice_tui`.

Co-authored-by: JuanCS-Dev <227056558+JuanCS-Dev@users.noreply.github.com>
@JuanCS-Dev JuanCS-Dev closed this Feb 12, 2026
@JuanCS-Dev JuanCS-Dev deleted the test-circuit-breaker-improvement-7174230857590657879 branch February 12, 2026 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

testing Testing related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant