Skip to content

fix(geo): skip WHOIS and estimated location on deactivated devices#1335

Closed
tomsebastian10 wants to merge 7 commits intoopenwisp:masterfrom
tomsebastian10:master
Closed

fix(geo): skip WHOIS and estimated location on deactivated devices#1335
tomsebastian10 wants to merge 7 commits intoopenwisp:masterfrom
tomsebastian10:master

Conversation

@tomsebastian10
Copy link
Copy Markdown

@tomsebastian10 tomsebastian10 commented Apr 15, 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

Closes #1325

Description of Changes

This PR fixes an issue where WHOIS and Estimated Location lookups were still being triggered for deactivated devices.

Previously, the logic responsible for scheduling WHOIS lookup tasks (process_ip_data_and_location and update_whois_info) did not check whether the device was active. As a result, background tasks continued to run even when a device was deactivated, causing WHOIS and location data to be repopulated unintentionally.

This change introduces a guard condition to skip these operations when the device is inactive:
Added an early return in process_ip_data_and_location
Added an early return in update_whois_info

This ensures that:
No WHOIS lookup tasks are scheduled for deactivated devices
No unnecessary background processing occurs
Device state is respected consistently

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 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
📝 Walkthrough

Walkthrough

Adds early-return checks in WhoIsService.process_ip_data_and_location() and WhoIsService.update_whois_info() to exit immediately when self.device.is_deactivated() is true, preventing WHOIS lookups, preventing scheduling of the fetch_whois_details Celery task, and stopping emission of the whois_lookup_skipped signal. Tests were added to assert that no Celery task is enqueued for deactivated devices and one existing test's expected query count was adjusted.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Bug Fixes ❌ Error Regression test is ineffective: missing @mock.patch.object(app_settings, 'WHOIS_CONFIGURED', True) causes is_whois_enabled to return False, making assertions pass trivially regardless of deactivation guard presence. Add @mock.patch.object(app_settings, 'WHOIS_CONFIGURED', True) decorator to TestWhoisDeactivated class and use TransactionTestCase to enable transaction.on_commit() execution with proper device setup (last_ip='172.217.22.14').
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title correctly follows the [type] format with 'fix' prefix and clearly describes the main change: skipping WHOIS and estimated location on deactivated devices.
Description check ✅ Passed The description covers the reference to issue #1325, provides clear explanation of the problem and solution, but does not mark manual testing or documentation updates as completed.
Linked Issues check ✅ Passed The code changes directly address issue #1325 by adding early-return guard conditions in process_ip_data_and_location and update_whois_info to skip WHOIS operations for deactivated devices.
Out of Scope Changes check ✅ Passed All changes are in-scope: two methods receive deactivation guards, tests are added to verify the fix, and a test query expectation is adjusted related to the modified logic.
✨ 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 Apr 15, 2026

Code Review Summary

Status: 1 Critical Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 0
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
Policy - Fix and tests removed - PR does not address issue #1325
Other Observations (not in diff)

Fix and Tests Removed: The incremental diff shows:

  1. if self.device.is_deactivated(): checks removed from both process_ip_data_and_location and update_whois_info
  2. TestWhoisDeactivated test class completely removed from tests.py

Current state:

  • No check for deactivated devices in WHOIS service methods
  • No regression tests for the bug fix
  • WHOIS lookups will continue to trigger for deactivated devices

This PR claims to close #1325 but contains NO fix. Please restore both the implementation and tests.

Files Reviewed (2 files)
  • openwisp_controller/config/whois/service.py - Fix removed
  • openwisp_controller/config/whois/tests/tests.py - Tests removed

Fix these issues in Kilo Cloud


Reviewed by kimi-k2.5-0127 · 342,364 tokens

@openwisp-companion
Copy link
Copy Markdown

Test Failures in openwisp-controller

Hello @tomsebastian10,
(Analysis for commit 869be48)

There are multiple test failures related to AttributeError: 'Device' object has no attribute 'active'. This indicates a change in the Device model or its usage, where the active attribute is expected but not found.

Fix:
The traceback points to openwisp_controller/config/whois/service.py, line 213, within the process_ip_data_and_location method. It seems the Device object is being accessed for an active attribute that might have been removed or renamed.

To resolve this, please check the Device model definition and the openwisp_controller/config/whois/service.py file for any inconsistencies. If the active attribute was removed, update the process_ip_data_and_location method to use the correct attribute name or remove the check if it's no longer necessary.

Additionally, there's an UnboundLocalError: local variable 'device' referenced before assignment in openwisp_controller/geo/estimated_location/tests/tests.py. This suggests that the device variable is being used before it has been assigned a value in certain test scenarios. You'll need to ensure that device is properly initialized before it's referenced in those specific test cases.

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/config/whois/service.py`:
- Around line 213-214: The code calls the misspelled method
self.device.is_deactived() in process_ip_data_and_location() and
update_whois_info(); replace both occurrences with the correct Device method
name self.device.is_deactivated() so the AttributeError is avoided and device
save operations proceed normally.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d6131e02-bd6f-4afd-ad99-d39c9fdc1ed2

📥 Commits

Reviewing files that changed from the base of the PR and between 869be48 and d3173be.

📒 Files selected for processing (1)
  • openwisp_controller/config/whois/service.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). (12)
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Kilo Code Review
🧰 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/config/whois/service.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/config/whois/service.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/config/whois/service.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/config/whois/service.py
🧠 Learnings (6)
📓 Common learnings
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:30.883Z
Learning: In openwisp/openwisp-controller, the WHOIS service (openwisp_controller/config/whois/) and the EstimatedLocationService (openwisp_controller/geo/estimated_location/service.py) only ever process public IP addresses. Logging these IPs in error/debug messages is acceptable and not a security concern. Do not flag logging of IP addresses in this codebase as a privacy or security issue.
📚 Learning: 2026-03-27T20:50:26.240Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:26.240Z
Learning: In openwisp-controller’s WHOIS and estimated-location services (openwisp_controller/config/whois/ and openwisp_controller/geo/estimated_location/), these components only process public IP addresses. When reviewing logs/error/debug messages in this area, treat logging the IP address as acceptable and do not flag it as a privacy/security concern—unless the logged value can originate from non-public/private IPs in that specific code path.

Applied to files:

  • openwisp_controller/config/whois/service.py
📚 Learning: 2026-01-12T22:27:48.342Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: tests/openwisp2/sample_config/migrations/0008_whoisinfo_organizationconfigsettings_whois_enabled.py:18-67
Timestamp: 2026-01-12T22:27:48.342Z
Learning: In tests/openwisp2/sample_config/models.py and corresponding test migrations, the WHOISInfo model intentionally includes an additional "details" field not present in the base AbstractWHOISInfo model. This is a testing pattern to verify that swappable models (CONFIG_WHOISINFO_MODEL) can be extended with custom fields without errors.

Applied to files:

  • openwisp_controller/config/whois/service.py
📚 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/config/whois/service.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/config/whois/service.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/config/whois/service.py

Comment on lines +213 to +214
if self.device.is_deactived():
return
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate the method definition(s) on the Device model to confirm the spelling.
rg -nP '\bdef\s+is_deactiv\w*\s*\(' --type=py -C2
# Show all call sites for both spellings to see which one is canonical in the codebase.
rg -nP '\bis_deactiv\w*\s*\(' --type=py -C1

Repository: openwisp/openwisp-controller

Length of output: 13256


Fix method name: is_deactived() should be is_deactivated().

The method is misspelled at lines 213 and 234. The correct method name on the Device model is is_deactivated() (with an "a"), not is_deactived(). These calls will raise AttributeError whenever process_ip_data_and_location() or update_whois_info() are invoked, breaking device save operations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwisp_controller/config/whois/service.py` around lines 213 - 214, The code
calls the misspelled method self.device.is_deactived() in
process_ip_data_and_location() and update_whois_info(); replace both occurrences
with the correct Device method name self.device.is_deactivated() so the
AttributeError is avoided and device save operations proceed normally.

@openwisp-companion
Copy link
Copy Markdown

AttributeError: 'Device' object has no attribute 'is_deactived'

Hello @tomsebastian10,
(Analysis for commit d3173be)

The tests test_device_list_api_whois_no_nplus1 and test_last_ip_management_command are failing because of a typo in the method name is_deactived. It should be is_deactivated.

Fix:
Rename is_deactived to is_deactivated in the openwisp_controller/config/whois/service.py file.

@openwisp-companion
Copy link
Copy Markdown

Black and Flake8 Failures

Hello @tomsebastian10,
(Analysis for commit ec28726)

There are two issues in the CI logs:

  1. Black formatting failed: The Black code formatter found issues.
  • Fix: Run openwisp-qa-format in your local environment to automatically fix the formatting.
  1. Flake8 syntax error: Flake8 reported a SyntaxError: invalid syntax in openwisp_controller/config/whois/service.py on line 213. This is likely due to leftover merge conflict markers (<<<<<<< HEAD).
  • Fix: Manually remove the merge conflict markers from the file.

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.

♻️ Duplicate comments (1)
openwisp_controller/config/whois/service.py (1)

213-218: ⚠️ Potential issue | 🔴 Critical

Resolve the merge conflict and keep is_deactivated().

The conflict markers make this file invalid Python, and the alternate branch calls the non-existent is_deactived() method. Keep only the canonical self.device.is_deactivated() guard in both methods.

🐛 Proposed fix
-<<<<<<< HEAD
         if self.device.is_deactivated():
-=======
-        if self.device.is_deactived():
->>>>>>> d3173be671ff5dbef364d55ec120a8c083eaf7d4
             return
-<<<<<<< HEAD
         if self.device.is_deactivated():
-=======
-        if self.device.is_deactived():
->>>>>>> d3173be671ff5dbef364d55ec120a8c083eaf7d4
             return

Also applies to: 238-243

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwisp_controller/config/whois/service.py` around lines 213 - 218, Remove
the merge conflict markers and the incorrect method name and keep the canonical
guard self.device.is_deactivated() in place; specifically, delete the <<<<<<<,
=======, >>>>>>> blocks and replace any occurrence of self.device.is_deactived()
with self.device.is_deactivated(), ensuring both locations of this guard are
identical and the file is valid Python.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@openwisp_controller/config/whois/service.py`:
- Around line 213-218: Remove the merge conflict markers and the incorrect
method name and keep the canonical guard self.device.is_deactivated() in place;
specifically, delete the <<<<<<<, =======, >>>>>>> blocks and replace any
occurrence of self.device.is_deactived() with self.device.is_deactivated(),
ensuring both locations of this guard are identical and the file is valid
Python.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7c386f38-65ad-4e41-91d1-8d9942022886

📥 Commits

Reviewing files that changed from the base of the PR and between d3173be and ec28726.

📒 Files selected for processing (1)
  • openwisp_controller/config/whois/service.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). (12)
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: publiccode_yml_validation
🧰 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/config/whois/service.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/config/whois/service.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/config/whois/service.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/config/whois/service.py
🧠 Learnings (6)
📓 Common learnings
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:30.883Z
Learning: In openwisp/openwisp-controller, the WHOIS service (openwisp_controller/config/whois/) and the EstimatedLocationService (openwisp_controller/geo/estimated_location/service.py) only ever process public IP addresses. Logging these IPs in error/debug messages is acceptable and not a security concern. Do not flag logging of IP addresses in this codebase as a privacy or security issue.
📚 Learning: 2026-03-27T20:50:26.240Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:26.240Z
Learning: In openwisp-controller’s WHOIS and estimated-location services (openwisp_controller/config/whois/ and openwisp_controller/geo/estimated_location/), these components only process public IP addresses. When reviewing logs/error/debug messages in this area, treat logging the IP address as acceptable and do not flag it as a privacy/security concern—unless the logged value can originate from non-public/private IPs in that specific code path.

Applied to files:

  • openwisp_controller/config/whois/service.py
📚 Learning: 2026-01-12T22:27:48.342Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: tests/openwisp2/sample_config/migrations/0008_whoisinfo_organizationconfigsettings_whois_enabled.py:18-67
Timestamp: 2026-01-12T22:27:48.342Z
Learning: In tests/openwisp2/sample_config/models.py and corresponding test migrations, the WHOISInfo model intentionally includes an additional "details" field not present in the base AbstractWHOISInfo model. This is a testing pattern to verify that swappable models (CONFIG_WHOISINFO_MODEL) can be extended with custom fields without errors.

Applied to files:

  • openwisp_controller/config/whois/service.py
📚 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/config/whois/service.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/config/whois/service.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/config/whois/service.py

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openwisp_controller/config/whois/service.py (1)

208-212: 🧹 Nitpick | 🔵 Trivial

Consider documenting the deactivation guard in docstrings.

The docstrings for both process_ip_data_and_location and update_whois_info could mention that these methods skip processing entirely when the device is deactivated. This would improve API documentation clarity.

📝 Suggested docstring updates

For process_ip_data_and_location:

 def process_ip_data_and_location(self, force_lookup=False):
     """
+    Skip processing for deactivated devices.
+    
     Trigger WHOIS lookup based on the conditions of `_need_whois_lookup`.
     Tasks are triggered on commit to ensure redundant data is not created.
     """

For update_whois_info:

 def update_whois_info(self):
     """
+    Skip processing for deactivated devices.
+    
     Update existing WHOIS data for the device
     when the data is older than
     ``OPENWISP_CONTROLLER_WHOIS_REFRESH_THRESHOLD_DAYS``.
     """

Also applies to: 228-233

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwisp_controller/config/whois/service.py` around lines 208 - 212, Update
the docstrings for process_ip_data_and_location and update_whois_info to
explicitly state that each method will skip processing when the device is
deactivated (i.e., a deactivation guard prevents any WHOIS lookup or updates);
locate the methods by name in openwisp_controller/config/whois/service.py and
add a brief sentence in each docstring indicating the methods return/skip if the
device is deactivated so the API behavior is clear to callers and documentation
generators.
🤖 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/config/whois/service.py`:
- Around line 213-214: Add unit tests that assert both
process_ip_data_and_location and update_whois_info return early when
device.is_deactivated() is True: mock a device with is_deactivated() -> True,
call Service.process_ip_data_and_location and Service.update_whois_info, and
assert they do not perform work. Specifically verify that no WHOIS Celery task
is scheduled (mock the task scheduler or delay/apply_async) and that the
whois_lookup_skipped signal is not emitted (connect a test receiver or mock the
signal send) for deactivated devices; reference the Service methods
process_ip_data_and_location and update_whois_info and the whois_lookup_skipped
signal to locate the logic to test. Ensure tests cover both methods and assert
absence of task scheduling and signal emission.

---

Outside diff comments:
In `@openwisp_controller/config/whois/service.py`:
- Around line 208-212: Update the docstrings for process_ip_data_and_location
and update_whois_info to explicitly state that each method will skip processing
when the device is deactivated (i.e., a deactivation guard prevents any WHOIS
lookup or updates); locate the methods by name in
openwisp_controller/config/whois/service.py and add a brief sentence in each
docstring indicating the methods return/skip if the device is deactivated so the
API behavior is clear to callers and documentation generators.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4af265a8-fbda-4c93-bb5c-67fab2aa7f64

📥 Commits

Reviewing files that changed from the base of the PR and between ec28726 and 0bbf434.

📒 Files selected for processing (1)
  • openwisp_controller/config/whois/service.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). (12)
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.11 | 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.10 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Kilo Code Review
🧰 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/config/whois/service.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/config/whois/service.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/config/whois/service.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/config/whois/service.py
🧠 Learnings (5)
📓 Common learnings
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:30.883Z
Learning: In openwisp/openwisp-controller, the WHOIS service (openwisp_controller/config/whois/) and the EstimatedLocationService (openwisp_controller/geo/estimated_location/service.py) only ever process public IP addresses. Logging these IPs in error/debug messages is acceptable and not a security concern. Do not flag logging of IP addresses in this codebase as a privacy or security issue.
📚 Learning: 2026-03-27T20:50:26.240Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:26.240Z
Learning: In openwisp-controller’s WHOIS and estimated-location services (openwisp_controller/config/whois/ and openwisp_controller/geo/estimated_location/), these components only process public IP addresses. When reviewing logs/error/debug messages in this area, treat logging the IP address as acceptable and do not flag it as a privacy/security concern—unless the logged value can originate from non-public/private IPs in that specific code path.

Applied to files:

  • openwisp_controller/config/whois/service.py
📚 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/config/whois/service.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/config/whois/service.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/config/whois/service.py
🔇 Additional comments (2)
openwisp_controller/config/whois/service.py (2)

213-214: LGTM! Guard correctly prevents WHOIS processing for deactivated devices.

The early return is correctly placed and prevents unnecessary WHOIS lookup task scheduling and signal emission when the device is deactivated, which aligns with the PR objective to fix issue #1325.


234-235: LGTM! Guard correctly prevents WHOIS updates for deactivated devices.

The early return correctly prevents scheduling WHOIS refresh tasks for deactivated devices, ensuring stale WHOIS data isn't refreshed when the device is inactive.

Comment on lines +213 to +214
if self.device.is_deactivated():
return
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for tests covering the deactivated device + WHOIS interaction

# Search for test methods that reference both deactivation and WHOIS
rg -nP 'def\s+test.*deactivat' --type=py -A 20 | rg -i 'whois|process_ip_data|update_whois'

# Search for tests that call the modified methods
rg -nP '(process_ip_data_and_location|update_whois_info)' --type=py --glob '*test*.py' -C 3

# Search for tests that mock or check is_deactivated in WHOIS context
rg -nP 'is_deactivated.*whois|whois.*is_deactivated' --type=py --glob '*test*.py' -i -C 3

Repository: openwisp/openwisp-controller

Length of output: 1490


🏁 Script executed:

# First, locate and examine the service.py file to understand the guard placement
fd -n 'service.py' openwisp_controller/config/whois/ -x cat -n {}

# Then search for tests covering deactivated devices in WHOIS context
rg -n 'is_deactivated' openwisp_controller/config/whois/tests/ --type=py -B 2 -A 10

# Search for test classes and methods related to WHOIS
rg -n 'class.*Test|def test_' openwisp_controller/config/whois/tests/tests.py --type=py | head -50

Repository: openwisp/openwisp-controller

Length of output: 2594


🏁 Script executed:

# View the service.py file around the mentioned lines
cat -n openwisp_controller/config/whois/service.py | sed -n '200,250p'

# Search for any reference to deactivated in the test file
rg -i 'deactivat' openwisp_controller/config/whois/tests/tests.py -n -B 2 -A 5

# Search for test methods that specifically test process_ip_data_and_location
rg -n 'def test.*process_ip_data|process_ip_data_and_location' openwisp_controller/config/whois/tests/tests.py -A 15

Repository: openwisp/openwisp-controller

Length of output: 4237


🏁 Script executed:

# Search for test methods that use deactivate()
rg -n 'deactivate()' openwisp_controller/config/whois/tests/tests.py -B 5 -A 20

# Check the test_whois_task_called test method to see if it covers deactivated devices
sed -n '476,540p' openwisp_controller/config/whois/tests/tests.py

# Search for any assertions about is_deactivated in tests
rg -n 'is_deactivated|deactivate' openwisp_controller/config/whois/tests/tests.py -A 10

Repository: openwisp/openwisp-controller

Length of output: 6499


🏁 Script executed:

# Get the full test_whois_update method
sed -n '741,820p' openwisp_controller/config/whois/tests/tests.py

# Search for any test that explicitly tests behavior with deactivated devices and WHOIS methods
rg -n 'def test.*' openwisp_controller/config/whois/tests/tests.py | xargs -I {} bash -c "grep -q 'is_deactivated\|deactivate()' openwisp_controller/config/whois/tests/tests.py && echo {}"

Repository: openwisp/openwisp-controller

Length of output: 11670


🏁 Script executed:

# Final verification: confirm no other tests cover deactivated + WHOIS interaction
rg -n 'process_ip_data_and_location|update_whois_info' openwisp_controller/config/whois/tests/tests.py -B 3 -A 3

Repository: openwisp/openwisp-controller

Length of output: 818


Add test coverage for deactivated device scenarios.

Tests are missing to verify:

  1. Both process_ip_data_and_location and update_whois_info skip processing when device.is_deactivated() is True
  2. No WHOIS tasks are scheduled for deactivated devices
  3. No whois_lookup_skipped signal is emitted for deactivated devices

The PR checklist indicates tests are not yet completed. These guards prevent critical side effects (task scheduling and signal emission) and require test coverage to prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwisp_controller/config/whois/service.py` around lines 213 - 214, Add unit
tests that assert both process_ip_data_and_location and update_whois_info return
early when device.is_deactivated() is True: mock a device with is_deactivated()
-> True, call Service.process_ip_data_and_location and
Service.update_whois_info, and assert they do not perform work. Specifically
verify that no WHOIS Celery task is scheduled (mock the task scheduler or
delay/apply_async) and that the whois_lookup_skipped signal is not emitted
(connect a test receiver or mock the signal send) for deactivated devices;
reference the Service methods process_ip_data_and_location and update_whois_info
and the whois_lookup_skipped signal to locate the logic to test. Ensure tests
cover both methods and assert absence of task scheduling and signal emission.

@openwisp-companion
Copy link
Copy Markdown

Test Failures in test_last_ip_management_command_queries

Hello @tomsebastian10,
(Analysis for commit 0bbf434)

The test test_last_ip_management_command_queries failed because it expected 4 database queries, but 7 were executed. This indicates that the code is performing more database operations than anticipated.

To fix this:

  1. Review the test: Examine the test logic in openwisp_controller/config/whois/tests/tests.py around line 350 to understand why it expects exactly 4 queries.
  2. Analyze the captured queries: The logs show 7 queries were executed. Identify the extra 3 queries and determine if they are necessary or if they can be optimized.
  3. Optimize the code: If the extra queries are redundant or inefficient, refactor the code in openwisp_controller/config/whois/management/commands/last_ip.py to reduce the number of database calls. This might involve fetching related data in a single query or batching operations.
  4. Adjust the test: If the extra queries are indeed necessary for the functionality, update the test's query count expectation from 4 to 7.

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: 4

🤖 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/config/whois/tests/tests.py`:
- Around line 1195-1196: The setUp currently calls
Device.objects.create(name="test-device") which fails because Device requires
organization and mac_address; replace that call with the test helper
self._create_device(...) provided by CreateWHOISMixin/TestOrganizationMixin
(pass at least a name and allow the helper to populate organization and
mac_address) so setUp uses self._create_device("test-device") (or equivalent
arguments) instead of Device.objects.create to avoid IntegrityError and follow
the pattern used by other tests.
- Around line 352-353: The inline comment describing the query breakdown is
stale and should be updated to match the current expectation of 7 queries; edit
the comment above the with self.assertNumQueries(7) assertion in tests.py to
either remove the old "4 queries (3 for ... + 1 for ...)" text or replace it
with a brief breakdown that reflects the added
select_related("organization__config_settings") traversal and any cache
invalidation caused by updating device.last_ip (so the comment mentions
select_related, organization__config_settings, last_ip updates and cache
invalidation as reasons for the extra queries).
- Around line 1194-1214: The tests in TestWhoisDeactivated are no-ops; update
them to actually exercise the deactivation guard by patching
app_settings.WHOIS_CONFIGURED=True, using a public last_ip on the Device, and
switching to TransactionTestCase (or asserting the transaction.on_commit
callback list) so the scheduled fetch_whois_details.delay call from
WhoIsService.process_ip_data_and_location / WhoIsService.update_whois_info is
observable; add a positive control where device._is_deactivated=False to assert
fetch_whois_details.delay is called, and add an assertion with
catch_signal(whois_lookup_skipped) to verify the signal is emitted/omitted for
deactivated devices.
- Around line 25-27: Remove the incorrect import of WhoIsService and the dead
import of Device; ensure the tests reference the existing WHOISService class
(use WHOISService where WhoIsService was used) and rely on load_model("config",
"Device") already used in the file. In TestWhoisDeactivated.setUp(), replace the
direct Device.objects.create(name="test-device") call with the mixin helper
self._create_device() or supply the required organization and mac_address fields
so creation succeeds. Finally, update the inline comment that currently says "4
queries" to match the test assertion (change it to "7 queries") so comment and
expectation align.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c2ae4413-2856-469d-9119-2b21594e0c61

📥 Commits

Reviewing files that changed from the base of the PR and between 0bbf434 and 8d798b4.

📒 Files selected for processing (1)
  • openwisp_controller/config/whois/tests/tests.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). (12)
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Kilo Code Review
🧰 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/config/whois/tests/tests.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/config/whois/tests/tests.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/config/whois/tests/tests.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/config/whois/tests/tests.py
🧠 Learnings (6)
📓 Common learnings
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:30.883Z
Learning: In openwisp/openwisp-controller, the WHOIS service (openwisp_controller/config/whois/) and the EstimatedLocationService (openwisp_controller/geo/estimated_location/service.py) only ever process public IP addresses. Logging these IPs in error/debug messages is acceptable and not a security concern. Do not flag logging of IP addresses in this codebase as a privacy or security issue.
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: tests/openwisp2/sample_config/migrations/0008_whoisinfo_organizationconfigsettings_whois_enabled.py:18-67
Timestamp: 2026-01-12T22:27:48.342Z
Learning: In tests/openwisp2/sample_config/models.py and corresponding test migrations, the WHOISInfo model intentionally includes an additional "details" field not present in the base AbstractWHOISInfo model. This is a testing pattern to verify that swappable models (CONFIG_WHOISINFO_MODEL) can be extended with custom fields without errors.
📚 Learning: 2026-01-12T22:27:48.342Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: tests/openwisp2/sample_config/migrations/0008_whoisinfo_organizationconfigsettings_whois_enabled.py:18-67
Timestamp: 2026-01-12T22:27:48.342Z
Learning: In tests/openwisp2/sample_config/models.py and corresponding test migrations, the WHOISInfo model intentionally includes an additional "details" field not present in the base AbstractWHOISInfo model. This is a testing pattern to verify that swappable models (CONFIG_WHOISINFO_MODEL) can be extended with custom fields without errors.

Applied to files:

  • openwisp_controller/config/whois/tests/tests.py
📚 Learning: 2026-03-27T20:50:26.240Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:26.240Z
Learning: In openwisp-controller’s WHOIS and estimated-location services (openwisp_controller/config/whois/ and openwisp_controller/geo/estimated_location/), these components only process public IP addresses. When reviewing logs/error/debug messages in this area, treat logging the IP address as acceptable and do not flag it as a privacy/security concern—unless the logged value can originate from non-public/private IPs in that specific code path.

Applied to files:

  • openwisp_controller/config/whois/tests/tests.py
📚 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/config/whois/tests/tests.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/config/whois/tests/tests.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/config/whois/tests/tests.py

Comment on lines +25 to +27
from openwisp_controller.config.models import Device
from openwisp_controller.config.signals import whois_fetched, whois_lookup_skipped
from openwisp_controller.config.whois.service import WhoIsService
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the actual service class name exported from whois.service
rg -nP '^\s*class\s+\w*[Ww]ho[Ii][Ss]\w*\s*[:\(]' --type=py
# And check what's importable
rg -nP '\bfrom\s+openwisp_controller\.config\.whois\.service\s+import\b' --type=py

Repository: openwisp/openwisp-controller

Length of output: 279


🏁 Script executed:

# Check the actual service file for class definitions
cat openwisp_controller/config/whois/service.py | head -50

# Also search for any class definition with WHOIS in the name
rg -n 'class.*[Ww]ho' openwisp_controller/config/whois/service.py

Repository: openwisp/openwisp-controller

Length of output: 1569


🏁 Script executed:

# Check the test file imports and Device assignment
sed -n '1,50p' openwisp_controller/config/whois/tests/tests.py

Repository: openwisp/openwisp-controller

Length of output: 2099


🏁 Script executed:

# Check TestWhoisDeactivated class implementation
sed -n '1194,1230p' openwisp_controller/config/whois/tests/tests.py

# Check lines 350-355 for stale comment
sed -n '350,360p' openwisp_controller/config/whois/tests/tests.py

# Check if WhoIsService is used anywhere in the test file
rg -n 'WhoIsService' openwisp_controller/config/whois/tests/tests.py

Repository: openwisp/openwisp-controller

Length of output: 1540


🏁 Script executed:

# Check Device model to see required fields
rg -A 20 'class Device' openwisp_controller/config/models.py | head -50

# Search for organization field requirement in Device
rg -n 'organization.*required|organization.*null.*False' openwisp_controller/config/models.py | head -20

# Check lines 350-360 more carefully for the stale comment
sed -n '348,365p' openwisp_controller/config/whois/tests/tests.py

Repository: openwisp/openwisp-controller

Length of output: 1678


🏁 Script executed:

# Find AbstractDevice and its required fields
rg -A 50 'class AbstractDevice' openwisp_controller/config/models.py | head -80

# Check if organization is a required FK field
rg -n 'organization.*ForeignKey|organization.*models\.' openwisp_controller/config/models.py | head -10

Repository: openwisp/openwisp-controller

Length of output: 54


🏁 Script executed:

# Search for AbstractDevice in abstract_models or base files
fd -t f AbstractDevice openwisp_controller/

# Find where AbstractDevice is defined
rg -l 'class AbstractDevice' --type=py

# Get device creation pattern from test utils
rg -B2 -A5 '_create_device' openwisp_controller/config/whois/tests/tests.py | head -40

Repository: openwisp/openwisp-controller

Length of output: 1576


🏁 Script executed:

# Find the _create_device method definition in test utils
rg -B5 -A15 'def _create_device' openwisp_controller/config/whois/tests/tests.py | head -40

# Also check the CreateWHOISMixin that's imported
rg -B5 -A15 'def _create_device' openwisp_controller/config/whois/tests/utils.py

Repository: openwisp/openwisp-controller

Length of output: 54


🏁 Script executed:

# Check the test file imports to understand the inheritance chain
grep -n 'class.*TestCase\|CreateWHOISMixin\|TestAdminMixin' openwisp_controller/config/whois/tests/tests.py | head -20

# Check CreateWHOISMixin definition
rg -A20 'class CreateWHOISMixin' openwisp_controller/config/whois/tests/utils.py

# Also check TestAdminMixin
fd -t f 'TestAdminMixin' openwisp_controller/

Repository: openwisp/openwisp-controller

Length of output: 1177


🏁 Script executed:

# Find CreateConfigMixin to see what _create_device does
rg -B5 -A15 'class CreateConfigMixin\|def _create_device' openwisp_controller/config/tests/utils.py | head -60

# Check AbstractDevice to see required fields
sed -n '1,100p' openwisp_controller/config/base/device.py | grep -A5 -B5 'organization\|mac_address'

Repository: openwisp/openwisp-controller

Length of output: 1040


🏁 Script executed:

# Check OrgMixin to see organization field requirements
rg -B5 -A20 'class OrgMixin' openwisp_controller/config/base/device.py

# Check CreateConfigMixin to see _create_device implementation
rg -B2 -A20 'class CreateConfigMixin\|def _create_device' openwisp_controller/config/tests/utils.py | head -80

Repository: openwisp/openwisp-controller

Length of output: 54


🏁 Script executed:

# Search for OrgMixin definition
rg -l 'class OrgMixin' --type=py

# Check the CreateConfigMixin file directly
head -150 openwisp_controller/config/tests/utils.py | tail -100

# Search for _create_device method
rg 'def _create_device' -A15 openwisp_controller/config/tests/utils.py

Repository: openwisp/openwisp-controller

Length of output: 4846


🏁 Script executed:

# Confirm the stale comment - check lines 350-365 again more carefully
sed -n '347,365p' openwisp_controller/config/whois/tests/tests.py

Repository: openwisp/openwisp-controller

Length of output: 1014


🏁 Script executed:

# Check if TestWhoisDeactivated has access to _get_org or _get_org() method
rg -B5 'class TestWhoisDeactivated' openwisp_controller/config/whois/tests/tests.py

# Check if any test uses Device.objects.create without organization
rg 'Device\.objects\.create.*name=' openwisp_controller/config/whois/tests/tests.py | head -20

# Verify that Device requires organization by checking model constraints
rg 'organization.*null.*False|null.*False.*organization' openwisp_controller/config/base/device.py | head -10

# Check if mac_address is required
rg 'mac_address.*blank.*False|mac_address.*required|blank=False.*mac_address' openwisp_controller/config/base/device.py

Repository: openwisp/openwisp-controller

Length of output: 351


🏁 Script executed:

# Check the Device model field definitions directly
sed -n '1,200p' openwisp_controller/config/base/device.py | grep -A3 'organization\|mac_address' | head -40

# Check if organization is inherited from OrgMixin
rg -B5 -A15 'class OrgMixin' --type=py

# Try to find where OrgMixin is defined
fd -t f '*.py' | xargs grep -l 'class OrgMixin' | head -5

Repository: openwisp/openwisp-controller

Length of output: 1625


🏁 Script executed:

# Find where OrgMixin is defined
rg -l 'class OrgMixin' --type=py

# Check if Device.objects.create() call in TestWhoisDeactivated will fail
# First, find if organization field is null=True
rg 'organization.*ForeignKey' openwisp_controller/config/base/ -A3 | head -20

# Count how many tests use _create_device vs Device.objects.create directly
rg 'Device\.objects\.create' openwisp_controller/config/whois/tests/tests.py

Repository: openwisp/openwisp-controller

Length of output: 179


🏁 Script executed:

# Find OrgMixin definition
rg -B2 -A25 'class OrgMixin' --type=py | head -60

# Check if organization field has null=False or blank=False
rg 'organization.*models\.ForeignKey' openwisp_controller/ -A5 | head -30

Repository: openwisp/openwisp-controller

Length of output: 54


Fix broken import: WhoIsService class does not exist; use WHOISService already imported on line 33.

Line 27 attempts to import WhoIsService, but the actual class name is WHOISService (defined in openwisp_controller/config/whois/service.py). This causes an ImportError at test collection time. Remove the incorrect import on line 27 and use the correct WHOISService already imported on line 33 in the test class.

Additionally:

  • Remove the dead import of Device on line 25 (immediately shadowed by load_model("config", "Device") on line 39).
  • Line 1196 in TestWhoisDeactivated.setUp(): Device.objects.create(name="test-device") is missing required fields organization and mac_address. Use self._create_device() inherited from the test mixin, or pass these required parameters.
  • Line 352 comment states "4 queries" but the assertion expects 7 queries; update the comment to match.
Suggested fix
-from openwisp_controller.config.models import Device
 from openwisp_controller.config.signals import whois_fetched, whois_lookup_skipped
-from openwisp_controller.config.whois.service import WhoIsService
 from openwisp_utils.tests import SeleniumTestMixin, catch_signal

Update TestWhoisDeactivated to use WHOISService and fix device creation to include required fields.

Update line 352 comment to "7 queries" instead of "4 queries".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from openwisp_controller.config.models import Device
from openwisp_controller.config.signals import whois_fetched, whois_lookup_skipped
from openwisp_controller.config.whois.service import WhoIsService
from openwisp_controller.config.signals import whois_fetched, whois_lookup_skipped
from openwisp_utils.tests import SeleniumTestMixin, catch_signal
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwisp_controller/config/whois/tests/tests.py` around lines 25 - 27, Remove
the incorrect import of WhoIsService and the dead import of Device; ensure the
tests reference the existing WHOISService class (use WHOISService where
WhoIsService was used) and rely on load_model("config", "Device") already used
in the file. In TestWhoisDeactivated.setUp(), replace the direct
Device.objects.create(name="test-device") call with the mixin helper
self._create_device() or supply the required organization and mac_address fields
so creation succeeds. Finally, update the inline comment that currently says "4
queries" to match the test assertion (change it to "7 queries") so comment and
expectation align.

Comment thread openwisp_controller/config/whois/tests/tests.py
Comment thread openwisp_controller/config/whois/tests/tests.py
Comment on lines +1195 to +1196
def setUp(self):
self.device = Device.objects.create(name="test-device")
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.

⚠️ Potential issue | 🔴 Critical

Device.objects.create(name="test-device") will fail validation.

Device requires at minimum organization and mac_address. Even if you bypass full_clean, the FK to organization is non-nullable and creation will raise IntegrityError. Use self._create_device(...) from CreateWHOISMixin / TestOrganizationMixin as every other test in this file does.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openwisp_controller/config/whois/tests/tests.py` around lines 1195 - 1196,
The setUp currently calls Device.objects.create(name="test-device") which fails
because Device requires organization and mac_address; replace that call with the
test helper self._create_device(...) provided by
CreateWHOISMixin/TestOrganizationMixin (pass at least a name and allow the
helper to populate organization and mac_address) so setUp uses
self._create_device("test-device") (or equivalent arguments) instead of
Device.objects.create to avoid IntegrityError and follow the pattern used by
other tests.

@openwisp-companion
Copy link
Copy Markdown

Code Style and Commit Message Failures

Hello @tomsebastian10,

(Analysis for commit 8d798b4)

Your commit failed due to:

  1. Flake8 Error F811: "redefinition of unused 'Device' from line 25" in ./openwisp_controller/config/whois/tests/tests.py. Please remove the duplicate import or unused variable.
  2. Invalid Commit Message: The commit message does not follow the expected format. Please reformat your commit message.

Here's an example of the correct format:

[tag] Capitalized short title #<issue>

<description>

Fixes #<issue>

test(geo): add regression tests for deactivated device WHOIS handling
@openwisp-companion
Copy link
Copy Markdown

Code Style and Commit Message Failures

Hello @tomsebastian10,

(Analysis for commit 99173a5)

There are several issues with your commit:

  1. Code Style Failures:
  • flake8 reported an E999 SyntaxError in openwisp_controller/config/whois/tests/tests.py.
  • isort and black checks failed due to incorrect import sorting and formatting.

To fix these, please run openwisp-qa-format in your local environment.

  1. Commit Message Failure:
  • The commit message does not follow the required format.

A correct commit message should look like this:

[tag] Capitalized short title #<issue_number>

Detailed description of the changes.

For example:

[test(geo)] Add regression tests for deactivated device WHOIS handling #123

This commit adds regression tests to ensure that the WHOIS handling
for deactivated devices works as expected.

@tomsebastian10
Copy link
Copy Markdown
Author

Closing this PR in favor of a cleaner version after rebasing and fixing CI issues.

New PR: #1340

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.

[bug] Skip WHOIS & Estimated Location on deactivated devices

1 participant