Skip to content

[tests] Add test for CommandTimeoutException in launch_command task#1261

Open
iamavichal-geek wants to merge 1 commit intoopenwisp:masterfrom
iamavichal-geek:tests/launch-command-timeout-exception
Open

[tests] Add test for CommandTimeoutException in launch_command task#1261
iamavichal-geek wants to merge 1 commit intoopenwisp:masterfrom
iamavichal-geek:tests/launch-command-timeout-exception

Conversation

@iamavichal-geek
Copy link
Copy Markdown

@iamavichal-geek iamavichal-geek commented Mar 7, 2026

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

No existing issue.

While exploring the command execution flow, I noticed that the
CommandTimeoutException branch in the launch_command task
was not covered by tests.

Description of Changes

The launch_command task in connection/tasks.py handles multiple
exception paths during command execution.

The CommandTimeoutException case represents a device-side timeout
raised by the SSH connector when a command exceeds the configured
timeout. This is different from SoftTimeLimitExceeded, which is
triggered by Celery when the background task itself exceeds its time limit.

This PR adds a test (test_launch_command_ssh_timeout) in
tests/test_tasks.py to verify that when CommandTimeoutException
is raised:

  • the command status is set to failed
  • the output contains the timeout message and exception details

The test follows the structure of the existing
test_launch_command_timeout test to keep the testing style consistent
with the current codebase.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 36bd6567-fb3d-4630-9499-c65b3352d9dc

📥 Commits

Reviewing files that changed from the base of the PR and between ffbefa5 and ec2828c.

📒 Files selected for processing (1)
  • openwisp_controller/connection/tests/test_tasks.py
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}

📄 CodeRabbit inference engine (Custom checks)

**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously

Files:

  • openwisp_controller/connection/tests/test_tasks.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}

📄 CodeRabbit inference engine (Custom checks)

Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries

Files:

  • openwisp_controller/connection/tests/test_tasks.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_controller/connection/tests/test_tasks.py
**/*.{py,html}

📄 CodeRabbit inference engine (Custom checks)

For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework

Files:

  • openwisp_controller/connection/tests/test_tasks.py
🧠 Learnings (3)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.

Applied to files:

  • openwisp_controller/connection/tests/test_tasks.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.

Applied to files:

  • openwisp_controller/connection/tests/test_tasks.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.

Applied to files:

  • openwisp_controller/connection/tests/test_tasks.py
🔇 Additional comments (1)
openwisp_controller/connection/tests/test_tasks.py (1)

71-93: LGTM!

The new test cleanly mirrors test_launch_command_timeout and correctly validates the CommandTimeoutException branch in launch_command — the exact assertEqual on command.output matches the production format string f"The command took longer than expected: {e}" (with trailing newline appended by _add_output), which locks in formatting regressions. Prior feedback on tightening the assertion has been addressed.


📝 Walkthrough

Walkthrough

Adds a new test method test_launch_command_ssh_timeout to openwisp_controller/connection/tests/test_tasks.py. The test imports CommandTimeoutException from openwisp_controller.connection.connectors.exceptions, patches AbstractCommand.execute to raise that exception while allowing the connection to succeed, invokes tasks.launch_command.delay(command.pk), and asserts the Command model is marked as failed with command.output set to The command took longer than expected: connection timed out after 30s\n. The existing timeout test using SoftTimeLimitExceeded is unchanged. Changes are limited to test code; no production code or public API signatures were modified.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the required [type] format with 'tests' prefix and clearly describes the change: adding a test for CommandTimeoutException handling.
Description check ✅ Passed The description covers all required sections with sufficient detail: includes completed checklist, addresses the missing test coverage gap, explains the distinction between exception types, and describes the testing approach.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Bug Fixes ✅ Passed This PR is a test enhancement adding coverage for an existing exception handler, not a bug fix to core functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented Mar 7, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Overview

This PR adds a regression test for the CommandTimeoutException handling in the launch_command task. The test properly verifies that when an SSH connection timeout occurs, the command status is set to failed and the output contains an appropriate error message.

What was reviewed:

  • Test addition follows the existing codebase patterns
  • Import of CommandTimeoutException is correct
  • Test assertions match the expected behavior in tasks.py (lines 80-83)
  • Test correctly mocks the execute method to simulate the timeout scenario
Files Reviewed (1 file)
  • openwisp_controller/connection/tests/test_tasks.py - Clean test addition, no issues

Copy link
Copy Markdown
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openwisp_controller/connection/tests/test_tasks.py`:
- Around line 89-91: Replace the two loose substring checks on command.output
with a single exact assertion to lock the formatting: keep the status check on
command.status == "failed" and change the output assertions to assert that
command.output (use .strip() if trailing newline possible) equals the exact
deterministic line written by the branch, e.g. compare command.output to "The
command took longer than expected: connection timed out after 30s" so formatting
regressions are caught; update the test in test_tasks.py where command.status
and command.output are asserted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 81afb611-0d5a-4273-88ea-a45990116ba5

📥 Commits

Reviewing files that changed from the base of the PR and between ffbefa5 and b9c2c4c.

📒 Files selected for processing (1)
  • openwisp_controller/connection/tests/test_tasks.py
📜 Review details
⏰ 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). (11)
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.

Applied to files:

  • openwisp_controller/connection/tests/test_tasks.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.

Applied to files:

  • openwisp_controller/connection/tests/test_tasks.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.

Applied to files:

  • openwisp_controller/connection/tests/test_tasks.py
🔇 Additional comments (1)
openwisp_controller/connection/tests/test_tasks.py (1)

71-88: Good coverage of the dedicated SSH-timeout branch.

This mirrors the existing timeout test closely and exercises the CommandTimeoutException path in launch_command without broadening the test scope.

Comment thread openwisp_controller/connection/tests/test_tasks.py Outdated
@openwisp-companion
Copy link
Copy Markdown

::warning::API Error (Max retries reached or fatal error): 429 RESOURCE_EXHAUSTED. {'error': {'code': 429, 'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_input_token_count, limit: 250000, model: gemini-2.5-flash-lite\nPlease retry in 6.658183856s.', 'status': 'RESOURCE_EXHAUSTED', 'details': [{'@type': 'type.googleapis.com/google.rpc.Help', 'links': [{'description': 'Learn more about Gemini API quotas', 'url': 'https://ai.google.dev/gemini-api/docs/rate-limits'}]}, {'@type': 'type.googleapis.com/google.rpc.QuotaFailure', 'violations': [{'quotaMetric': 'generativelanguage.googleapis.com/generate_content_free_tier_input_token_count', 'quotaId': 'GenerateContentInputTokensPerModelPerMinute-FreeTier', 'quotaDimensions': {'location': 'global', 'model': 'gemini-2.5-flash-lite'}, 'quotaValue': '250000'}]}, {'@type': 'type.googleapis.com/google.rpc.RetryInfo', 'retryDelay': '6s'}]}}

@iamavichal-geek iamavichal-geek force-pushed the tests/launch-command-timeout-exception branch from b9c2c4c to 7eb924b Compare March 7, 2026 04:31
@openwisp-companion
Copy link
Copy Markdown

::warning::API Error (Max retries reached or fatal error): 429 RESOURCE_EXHAUSTED. {'error': {'code': 429, 'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_input_token_count, limit: 250000, model: gemini-2.5-flash-lite\nPlease retry in 57.594596374s.', 'status': 'RESOURCE_EXHAUSTED', 'details': [{'@type': 'type.googleapis.com/google.rpc.Help', 'links': [{'description': 'Learn more about Gemini API quotas', 'url': 'https://ai.google.dev/gemini-api/docs/rate-limits'}]}, {'@type': 'type.googleapis.com/google.rpc.QuotaFailure', 'violations': [{'quotaMetric': 'generativelanguage.googleapis.com/generate_content_free_tier_input_token_count', 'quotaId': 'GenerateContentInputTokensPerModelPerMinute-FreeTier', 'quotaDimensions': {'model': 'gemini-2.5-flash-lite', 'location': 'global'}, 'quotaValue': '250000'}]}, {'@type': 'type.googleapis.com/google.rpc.RetryInfo', 'retryDelay': '57s'}]}}

@iamavichal-geek iamavichal-geek force-pushed the tests/launch-command-timeout-exception branch from 7eb924b to 7cac0e1 Compare March 7, 2026 05:00
@openwisp-companion
Copy link
Copy Markdown

::warning::API Error (Max retries reached or fatal error): 429 RESOURCE_EXHAUSTED. {'error': {'code': 429, 'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_input_token_count, limit: 250000, model: gemini-2.5-flash-lite\nPlease retry in 26.951940311s.', 'status': 'RESOURCE_EXHAUSTED', 'details': [{'@type': 'type.googleapis.com/google.rpc.Help', 'links': [{'description': 'Learn more about Gemini API quotas', 'url': 'https://ai.google.dev/gemini-api/docs/rate-limits'}]}, {'@type': 'type.googleapis.com/google.rpc.QuotaFailure', 'violations': [{'quotaMetric': 'generativelanguage.googleapis.com/generate_content_free_tier_input_token_count', 'quotaId': 'GenerateContentInputTokensPerModelPerMinute-FreeTier', 'quotaDimensions': {'model': 'gemini-2.5-flash-lite', 'location': 'global'}, 'quotaValue': '250000'}]}, {'@type': 'type.googleapis.com/google.rpc.RetryInfo', 'retryDelay': '26s'}]}}

@iamavichal-geek iamavichal-geek force-pushed the tests/launch-command-timeout-exception branch 2 times, most recently from 5f8ccf2 to 14751ed Compare March 7, 2026 05:52
@openwisp-companion
Copy link
Copy Markdown

::warning::API Error (Max retries reached or fatal error): 429 RESOURCE_EXHAUSTED. {'error': {'code': 429, 'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_input_token_count, limit: 250000, model: gemini-2.5-flash-lite\nPlease retry in 35.919468138s.', 'status': 'RESOURCE_EXHAUSTED', 'details': [{'@type': 'type.googleapis.com/google.rpc.Help', 'links': [{'description': 'Learn more about Gemini API quotas', 'url': 'https://ai.google.dev/gemini-api/docs/rate-limits'}]}, {'@type': 'type.googleapis.com/google.rpc.QuotaFailure', 'violations': [{'quotaMetric': 'generativelanguage.googleapis.com/generate_content_free_tier_input_token_count', 'quotaId': 'GenerateContentInputTokensPerModelPerMinute-FreeTier', 'quotaDimensions': {'model': 'gemini-2.5-flash-lite', 'location': 'global'}, 'quotaValue': '250000'}]}, {'@type': 'type.googleapis.com/google.rpc.RetryInfo', 'retryDelay': '35s'}]}}

Added test_launch_command_ssh_timeout to verify that when
CommandTimeoutException is raised during command execution,
the command status is set to 'failed' and the output contains
the expected timeout message.
@iamavichal-geek iamavichal-geek force-pushed the tests/launch-command-timeout-exception branch from 14751ed to ec2828c Compare March 7, 2026 19:25
@iamavichal-geek iamavichal-geek changed the title [connection] add test for CommandTimeoutException in launch_command task [tests] Add test for CommandTimeoutException in launch_command task Mar 7, 2026
@coveralls
Copy link
Copy Markdown

Coverage Status

coverage: 98.712% (+0.04%) from 98.672%
when pulling ec2828c on iamavichal-geek:tests/launch-command-timeout-exception
into ffbefa5 on openwisp:master.

@iamavichal-geek
Copy link
Copy Markdown
Author

Hi @nemesifier, Avichal (Avi) this side. I have been going through the codebaseI particularly AbstractCommand, the launch_command Celery task, the DeviceAdmin action pattern, and the WebSocket pipeline in commands.js.

I noticed the CommandTimeoutException branch in launch_command had no test coverage hence opening a small PR to address it : https://github.com/openwisp/openwisp-controller/pull/1261

I opened the PR directly since it was a small, self-contained test addition. Happy to open an issue first for future contributions if that is preferred. All CI checks are passing. Would appreciate any feedback from your side.

@asmodehn
Copy link
Copy Markdown
Member

asmodehn commented Mar 9, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@asmodehn
Copy link
Copy Markdown
Member

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

✅ Actions performed

Full review triggered.

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.

3 participants