[feature] Added generic solution for cache invalidation in config models #1095#1418
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📜 Recent review details⏰ Context from checks skipped due to timeout. (13)
🧰 Additional context used📓 Path-based instructions (2)**/*.py📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{py,html}📄 CodeRabbit inference engine (Custom checks)
Files:
🧠 Learnings (8)📚 Learning: 2026-01-15T15:05:49.557ZApplied to files:
📚 Learning: 2026-02-17T19:13:10.088ZApplied to files:
📚 Learning: 2026-01-15T15:07:17.354ZApplied to files:
📚 Learning: 2026-06-07T12:07:08.468ZApplied to files:
📚 Learning: 2026-06-07T12:07:24.608ZApplied to files:
📚 Learning: 2026-06-25T12:20:18.414ZApplied to files:
📚 Learning: 2026-06-07T12:07:25.164ZApplied to files:
📚 Learning: 2026-06-25T12:20:45.387ZApplied to files:
🔇 Additional comments (6)
📝 WalkthroughWalkthroughThis PR adds declarative cache dependencies with signal tracking, deferred invalidation, field snapshots, registry introspection, and a management command. Config and VPN models declare related dependencies, application wiring handles controller and device-group caches, and legacy save-based invalidation is removed. Tests, subnet checksum invalidation, PKI transaction coverage, provisioning order, and documentation are updated. Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant ModelSignal
participant CacheDependency
participant Config
participant Cache
ModelSignal->>CacheDependency: detect related model change
CacheDependency->>Config: resolve affected objects
CacheDependency->>Config: defer invalidation until commit
Config->>Cache: invalidate checksum or view cache
Possibly related PRs
Suggested labels: Suggested reviewers: Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error)
✅ Passed checks (7 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@openwisp_controller/config/apps.py`:
- Line 82: The inline comment in apps.py exceeds the repository’s 88-character
flake8 limit and needs to be wrapped. Update the comment near the
DeviceChecksumView cache invalidation logic in the relevant module so it fits
within max-line-length while preserving the same meaning.
- Around line 90-95: The delete-path cache invalidation in CacheDependency is
happening too early because it uses pre_delete with on_commit=False. Update the
dependency in apps.py to use DeviceChecksumView.invalidate_get_device_cache with
post_delete and commit-time invalidation so the cache is cleared only after the
delete transaction is committed.
In `@openwisp_controller/config/base/config.py`:
- Around line 225-240: Defer the checksum invalidation work for the DeviceGroup
and OrganizationConfigSettings CacheDependency entries until after the
transaction commits, because on_commit=False can let the async recomputation run
before the saved context is visible. Update the CacheDependency registrations in
the config.Config-related dependency list to use on_commit=True so
cls._invalidate_configs_in_group and cls._invalidate_configs_in_org run only
after the save is committed.
In `@openwisp_controller/config/base/vpn.py`:
- Around line 351-356: The VPN view cache invalidation in the CacheDependency
registration for config.Vpn is happening too early, before the delete is
committed. Update the signal from pre_delete to post_delete and keep on_commit
handling so cls._invalidate_vpn_view_cache runs only after the transaction
commits, preventing GetVpnView.get_vpn from caching a row that is already
deleted.
In `@openwisp_controller/config/handlers.py`:
- Around line 72-74: The cache invalidation task in the post-save handler is
enqueued too early and may run before the surrounding transaction is committed.
Update the handler that calls tasks.invalidate_devicegroup_cache_change.delay to
wrap the enqueue in transaction.on_commit so the task is scheduled only after a
successful commit. Use the existing post-save callback logic and keep the
instance.id and instance._meta.model_name arguments unchanged.
In `@openwisp_controller/config/tests/test_config.py`:
- Around line 1025-1099: Add test coverage for the default on_commit=True
behavior in TestCacheDependency, since the current tests only exercise
on_commit=False and miss the transaction.on_commit deferral path used by real
CacheDependency wiring. Add a test around a save-triggered dependency using
self.captureOnCommitCallbacks(execute=True) so the callback is actually executed
under TestCase, and verify the target is invoked only after commit; reference
CacheDependency, _connect, and the existing post_save save flow when adding the
new test.
🪄 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: 5ff567e8-9b77-4f24-bac9-2d85ca9c32ee
📒 Files selected for processing (9)
openwisp_controller/config/apps.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/device_group.pyopenwisp_controller/config/base/multitenancy.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_controller.py
💤 Files with no reviewable changes (2)
- openwisp_controller/config/base/device_group.py
- openwisp_controller/config/base/multitenancy.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{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/tests/test_controller.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/apps.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/tests/test_controller.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/apps.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/tests/test_controller.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/apps.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/tests/test_controller.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/apps.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/apps.py
🧠 Learnings (8)
📚 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/tests/test_controller.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/apps.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/tests/test_controller.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/apps.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/tests/test_controller.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/apps.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:18.414Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/base/models.py:571-572
Timestamp: 2026-06-25T12:20:18.414Z
Learning: When writing or reviewing tests that override pagination behavior via OpenWispPagination.paginate_queryset(), patch `view.pagination_page_size` (not `page_size`). The method uses `getattr(view, "pagination_page_size", self.page_size)`, so tests must set the attribute on the view to affect pagination. If the view class does not define `pagination_page_size`, using `unittest.mock.patch(..., create=True)` is intentional and correct because the attribute may not exist until patched.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:45.387Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/tests/test_api.py:916-932
Timestamp: 2026-06-25T12:20:45.387Z
Learning: When reviewing API pagination behavior in openwisp-controller, assume `OpenWispPagination.paginate_queryset()` allows a per-view page-size override via `getattr(view, "pagination_page_size", self.page_size)` (so `view.pagination_page_size`, if present, should affect pagination). In Python tests, it is valid to patch `pagination_page_size` on a view class even if the attribute isn’t declared on the class by default, by using `unittest.mock.patch.object(..., "pagination_page_size", ..., create=True)` so the override is available for the pagination logic during the test.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_config.py
🪛 ast-grep (0.44.0)
openwisp_controller/config/tests/test_config.py
[warning] 26-26: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model("config", "Template")
Note: [CWE-502] Deserialization of Untrusted Data.
(keras-load-model-python)
openwisp_controller/config/base/base.py
[warning] 197-197: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model(app_label, model_name)
Note: [CWE-502] Deserialization of Untrusted Data.
(keras-load-model-python)
🔇 Additional comments (7)
openwisp_controller/config/tests/test_config.py (1)
2-2: LGTM!Also applies to: 14-14, 26-26
openwisp_controller/config/tests/test_controller.py (1)
478-489: 🩺 Stability & AvailabilityCheck the cache invalidation path in this test
Ifvpn.delete()still defersGetVpnViewinvalidation withtransaction.on_commit(),TestCasewon’t run that callback automatically; wrap the delete inself.captureOnCommitCallbacks(execute=True)or use a test case that commits transactions.openwisp_controller/config/base/base.py (1)
9-35: LGTM!Also applies to: 112-329
openwisp_controller/config/apps.py (1)
5-5: LGTM!Also applies to: 45-56, 57-89, 96-151, 352-383
openwisp_controller/config/base/config.py (1)
29-35: LGTM!Also applies to: 49-49, 177-222
openwisp_controller/config/base/vpn.py (1)
36-41: LGTM!Also applies to: 51-56, 329-350, 357-365
openwisp_controller/config/handlers.py (1)
44-71: LGTM!
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@openwisp_controller/config/tests/test_config.py`:
- Around line 578-584: The test is updating cert.revoked via cert.save(), but
the transaction.on_commit cache invalidation callback never runs under TestCase,
so the assertion may not exercise _resolve_cert_dependency() as intended. In
test_config.py, wrap the cert.save() call in
captureOnCommitCallbacks(execute=True) so the deferred callback is executed
before config.refresh_from_db(), and keep the existing assertions around
_resolve_cert_dependency() and the VpnClient deletion path.
🪄 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: e656bc4e-934a-4c2e-b5ae-599ad2e1e2ab
📒 Files selected for processing (2)
openwisp_controller/config/apps.pyopenwisp_controller/config/tests/test_config.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{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/tests/test_config.pyopenwisp_controller/config/apps.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/tests/test_config.pyopenwisp_controller/config/apps.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/tests/test_config.pyopenwisp_controller/config/apps.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/tests/test_config.pyopenwisp_controller/config/apps.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.py
🧠 Learnings (8)
📚 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/tests/test_config.pyopenwisp_controller/config/apps.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/tests/test_config.pyopenwisp_controller/config/apps.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/tests/test_config.pyopenwisp_controller/config/apps.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:18.414Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/base/models.py:571-572
Timestamp: 2026-06-25T12:20:18.414Z
Learning: When writing or reviewing tests that override pagination behavior via OpenWispPagination.paginate_queryset(), patch `view.pagination_page_size` (not `page_size`). The method uses `getattr(view, "pagination_page_size", self.page_size)`, so tests must set the attribute on the view to affect pagination. If the view class does not define `pagination_page_size`, using `unittest.mock.patch(..., create=True)` is intentional and correct because the attribute may not exist until patched.
Applied to files:
openwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:45.387Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/tests/test_api.py:916-932
Timestamp: 2026-06-25T12:20:45.387Z
Learning: When reviewing API pagination behavior in openwisp-controller, assume `OpenWispPagination.paginate_queryset()` allows a per-view page-size override via `getattr(view, "pagination_page_size", self.page_size)` (so `view.pagination_page_size`, if present, should affect pagination). In Python tests, it is valid to patch `pagination_page_size` on a view class even if the attribute isn’t declared on the class by default, by using `unittest.mock.patch.object(..., "pagination_page_size", ..., create=True)` so the override is available for the pagination logic during the test.
Applied to files:
openwisp_controller/config/tests/test_config.py
🔇 Additional comments (4)
openwisp_controller/config/apps.py (3)
91-96: Previously flagged: delete invalidation still runs before commit.This matches the existing review comment:
pre_deletewithon_commit=Falsecan clear the device cache before the delete transaction commits, allowing stale cache repopulation before commit.
45-56: LGTM!
57-90: LGTM!Also applies to: 97-150, 351-382
openwisp_controller/config/tests/test_config.py (1)
1032-1036: LGTM!
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@openwisp_controller/config/base/vpn.py`:
- Around line 1111-1115: The loop in vpn.vpnclient_set.iterator() is causing
per-client N+1 queries because client.config and its render path fetch related
objects repeatedly. Update the queryset used in this block to preload config
plus config__device and the device’s organization/group relations before
iterating, so update_status_if_checksum_changed() can run without extra database
hits while preserving the _config_modified_action assignment.
🪄 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: 89cff386-fcf6-47ad-aa46-3e736cf376f7
📒 Files selected for processing (6)
openwisp_controller/config/base/config.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/subnet_division/admin.py
📜 Review details
⏰ Context from checks skipped due to timeout. (11)
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.12 | 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.13 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{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/subnet_division/admin.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/config.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/subnet_division/admin.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/config.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/subnet_division/admin.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/config.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/subnet_division/admin.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/config.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/subnet_division/admin.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/config.py
🧠 Learnings (8)
📚 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/subnet_division/admin.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/config.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/subnet_division/admin.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/config.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/subnet_division/admin.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/base/config.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:18.414Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/base/models.py:571-572
Timestamp: 2026-06-25T12:20:18.414Z
Learning: When writing or reviewing tests that override pagination behavior via OpenWispPagination.paginate_queryset(), patch `view.pagination_page_size` (not `page_size`). The method uses `getattr(view, "pagination_page_size", self.page_size)`, so tests must set the attribute on the view to affect pagination. If the view class does not define `pagination_page_size`, using `unittest.mock.patch(..., create=True)` is intentional and correct because the attribute may not exist until patched.
Applied to files:
openwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:45.387Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/tests/test_api.py:916-932
Timestamp: 2026-06-25T12:20:45.387Z
Learning: When reviewing API pagination behavior in openwisp-controller, assume `OpenWispPagination.paginate_queryset()` allows a per-view page-size override via `getattr(view, "pagination_page_size", self.page_size)` (so `view.pagination_page_size`, if present, should affect pagination). In Python tests, it is valid to patch `pagination_page_size` on a view class even if the attribute isn’t declared on the class by default, by using `unittest.mock.patch.object(..., "pagination_page_size", ..., create=True)` so the override is available for the pagination logic during the test.
Applied to files:
openwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
🪛 ast-grep (0.44.0)
openwisp_controller/config/tests/test_config.py
[warning] 27-27: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model("config", "Template")
Note: [CWE-502] Deserialization of Untrusted Data.
(keras-load-model-python)
🔇 Additional comments (7)
openwisp_controller/subnet_division/admin.py (1)
28-38: LGTM!openwisp_controller/config/tests/test_config.py (3)
579-584: 🩺 Stability & AvailabilityUnaddressed: deferred
on_commitcallback never executed in this test.
cert.save()schedules the cache-invalidation callback viatransaction.on_commit(perCacheDependency), but under plainTestCasethis callback never runs unless wrapped incaptureOnCommitCallbacks(execute=True). This was already flagged in a previous review round and remains unresolved in this version.Proposed fix
- cert.revoked = False - cert.save() + with self.captureOnCommitCallbacks(execute=True): + cert.revoked = False + cert.save()
1084-1160: 🩺 Stability & AvailabilityStill missing coverage for the default
on_commit=Truedeferral path.All four
TestCacheDependencytests passon_commit=False, so the default deferred-to-transaction.on_commit()behavior used by all real production wiring (Config/Vpn) is still never exercised. This was already raised in a prior review and remains open.Add at least one test using
self.captureOnCommitCallbacks(execute=True)around a save that triggers a dependency with the defaulton_commit=True.
1014-1073: LGTM!openwisp_controller/config/tests/test_vpn.py (1)
541-559: LGTM!openwisp_controller/config/tests/utils.py (1)
180-180: 🎯 Functional CorrectnessNo follow-up needed. The WireGuard tests that use this helper only assert VPN/client behavior; none rely on the template carrying interface entries.
> Likely an incorrect or invalid review comment.openwisp_controller/config/base/config.py (1)
212-240: LGTM!
Test Failures in Query CountsHello @pandafy, There are multiple test failures related to unexpected query counts:
These failures suggest that the code being tested is performing more database operations than the tests are configured to expect. You should review the test files mentioned and the corresponding code to identify and resolve the N+1 query issues or optimize the database interactions. |
nemesifier
left a comment
There was a problem hiding this comment.
Good progress, see my comments below.
| ), | ||
| CacheDependency( | ||
| source=self.device_model, | ||
| signal="pre_delete", |
There was a problem hiding this comment.
I would move this to post_delete and let it run on commit. With pre_delete plus on_commit=False, the cache is cleared while the row is still visible. A controller request during the delete transaction can repopulate DeviceChecksumView.get_device and leave a deleted device cached after commit. The smallest fix is to use post_delete with the default on_commit=True.
| source="config.DeviceGroup", | ||
| signal="post_save", | ||
| track_fields=["context"], | ||
| on_commit=False, |
There was a problem hiding this comment.
I would not enqueue this Celery recomputation before commit. If a group or organization context is saved inside a transaction, the worker can run against the old context and decide the checksums did not change. Please use the default on_commit=True for both this dependency and the OrganizationConfigSettings dependency below.
| ), | ||
| CacheDependency( | ||
| source="config.Vpn", | ||
| signal="pre_delete", |
There was a problem hiding this comment.
Same issue as the device view cache: this should be post_delete and deferred to commit. Otherwise a request can repopulate GetVpnView.get_vpn between the early invalidation and the transaction commit, leaving the deleted VPN cached.
There was a problem hiding this comment.
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/base/vpn.py (1)
366-372: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winDefer VPN view-cache invalidation to commit
post_saveand thevpn_server_modifiedpath emitted fromVpn.save()both run before the outer transaction commits, so a concurrentget_vpn()can repopulate the cache from the old row. Switch these cache dependencies toon_commit=True; the related-object cascade already runs after commit.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openwisp_controller/config/base/vpn.py` around lines 366 - 372, The VPN view-cache invalidation for the Vpn model is still happening before the surrounding transaction commits, which allows stale cache repopulation. Update the CacheDependency registration in Vpn-related cache setup to use on_commit=True for the post_save path, and ensure the vpn_server_modified flow from Vpn.save() also defers invalidation until commit; keep the related-object cascade behavior aligned with the commit-time invalidation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 75-76: The CI workaround in the workflow step that installs
django-x509 from the issues/236-renew-signals tarball is using a mutable branch
ref, which can break reproducibility. Update the pip install in the CI job to
pin django-x509 to a specific commit SHA instead of the branch tarball, and keep
the existing TODO/removal tracking tied to the upstream django-x509 release. Use
the workflow step containing the temporary workaround install comment to locate
it.
---
Outside diff comments:
In `@openwisp_controller/config/base/vpn.py`:
- Around line 366-372: The VPN view-cache invalidation for the Vpn model is
still happening before the surrounding transaction commits, which allows stale
cache repopulation. Update the CacheDependency registration in Vpn-related cache
setup to use on_commit=True for the post_save path, and ensure the
vpn_server_modified flow from Vpn.save() also defers invalidation until commit;
keep the related-object cascade behavior aligned with the commit-time
invalidation.
🪄 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: 04131af7-bc5d-49d4-936a-f06ffe009c06
📒 Files selected for processing (4)
.github/workflows/ci.ymlopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/pki/tests/test_api.py
📜 Review details
⏰ Context from checks skipped due to timeout. (9)
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{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/pki/tests/test_api.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.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/pki/tests/test_api.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.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/pki/tests/test_api.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.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/pki/tests/test_api.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.py
🧠 Learnings (10)
📚 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/pki/tests/test_api.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.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/pki/tests/test_api.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.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/pki/tests/test_api.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:18.414Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/base/models.py:571-572
Timestamp: 2026-06-25T12:20:18.414Z
Learning: When writing or reviewing tests that override pagination behavior via OpenWispPagination.paginate_queryset(), patch `view.pagination_page_size` (not `page_size`). The method uses `getattr(view, "pagination_page_size", self.page_size)`, so tests must set the attribute on the view to affect pagination. If the view class does not define `pagination_page_size`, using `unittest.mock.patch(..., create=True)` is intentional and correct because the attribute may not exist until patched.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:45.387Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/tests/test_api.py:916-932
Timestamp: 2026-06-25T12:20:45.387Z
Learning: When reviewing API pagination behavior in openwisp-controller, assume `OpenWispPagination.paginate_queryset()` allows a per-view page-size override via `getattr(view, "pagination_page_size", self.page_size)` (so `view.pagination_page_size`, if present, should affect pagination). In Python tests, it is valid to patch `pagination_page_size` on a view class even if the attribute isn’t declared on the class by default, by using `unittest.mock.patch.object(..., "pagination_page_size", ..., create=True)` so the override is available for the pagination logic during the test.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-02-24T16:24:55.443Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1233
File: .github/workflows/backport.yml:22-22
Timestamp: 2026-02-24T16:24:55.443Z
Learning: In repositories within the OpenWISP organization, it is acceptable to reference reusable workflows from other OpenWISP-controlled repos using mutable refs (e.g., master) in .github/workflows. This is permissible due to the shared trust boundary within the organization. If applying this pattern, ensure the target repos are under the same organization and maintain awareness of potential breakages from upstream mutable refs; consider pinning to a tagged version for longer-term stability when appropriate.
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-02-24T16:25:20.080Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1233
File: .github/workflows/backport.yml:35-35
Timestamp: 2026-02-24T16:25:20.080Z
Learning: In .github/workflows/backport.yml, enforce that backport-on-comment triggers only for users with author_association MEMBE R or OWNER (COLLABORATOR excluded), reflecting maintainer feedback. Update the trigger condition to check author_association and restrict to MEMBERS/OWNERS; document rationale and PR `#1233` reference in code comments.
Applied to files:
.github/workflows/ci.yml
🔇 Additional comments (9)
openwisp_controller/config/tests/test_config.py (4)
1117-1166: Missing coverage for the defaulton_commit=Truedeferral path.All three tests here pass
on_commit=False, so thetransaction.on_commit()deferral — the default and the mode used by all production wiring (Config/Vpn) — is never exercised. This was already flagged in a previous review of this file.
1086-1101: LGTM!
1168-1295: LGTM!
1104-1116: 🩺 Stability & AvailabilityShared test uid is safe here
disconnect()unwinds both the main receiver and the.snapshotpre_save receiver, so this helper does not leak handlers across tests.> Likely an incorrect or invalid review comment.openwisp_controller/config/base/vpn.py (4)
373-378:pre_delete+on_commit=Falseinvalidates the view cache before the row is deleted, allowing a concurrent request to repopulateGetVpnView.get_vpnwith the soon-to-be-deleted VPN. Usepost_deletewithon_commit=True. Already raised in prior review.
1141-1154:vpn.vpnclient_set.iterator()triggers per-client N+1 queries:client.configand the checksum recompute path load related objects inside the loop. Preloadconfig,config__device, and org/group relations. Already raised in prior review.
387-403: 🎯 Functional Correctness
x509_renewedisn’t a django-x509 signal, so this sender check doesn’t apply. Wire the cache invalidation to the actual renewal hook instead.> Likely an incorrect or invalid review comment.
52-57: 📐 Maintainability & Code QualityNo MRO issue. Only
clean()is shared, and the current order keepsShareableOrgMixinUniqueName.clean()beforeBaseConfig.clean().save,get_context,get_config, andchecksumaren’t impacted.> Likely an incorrect or invalid review comment.openwisp_controller/pki/tests/test_api.py (1)
164-164: LGTM!Also applies to: 292-292
|
The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3). |
Test Failures in OpenWISP Controller CIHello @pandafy, There are test failures in the CI pipeline. 1. Django Test Failures: The CI logs indicate a failure during the Django test execution. The traceback points to an Fix: 2. Coverage Warning: A Fix: |
| old_serial_num = ca1.serial_number | ||
| path = reverse("pki_api:ca_renew", args=[ca1.pk]) | ||
| with self.assertNumQueries(4): | ||
| with self.assertNumQueries(5): |
There was a problem hiding this comment.
Number of queries has increased because renewing a CA also invalidates the VPN checksum. In order to find related Vpn, we need to perform another query.
| old_serial_num = cert1.serial_number | ||
| path = reverse("pki_api:cert_renew", args=[cert1.pk]) | ||
| with self.assertNumQueries(5): | ||
| with self.assertNumQueries(6): |
There was a problem hiding this comment.
Number of queries has increased because renewing a Cert also invalidates the VPN and Config checksum. In order to find related Vpn, we need to perform another query.
[
{"line": 2026, "message": "Could not find pki.Ca!"}
]
|
CI Failure: Test Failures and Configuration IssuesHello @pandafy, There are two main issues identified in the CI logs:
|
There was a problem hiding this comment.
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/base/vpn.py (1)
366-371: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winDefer VPN cache invalidation until commit.
post_save,pre_delete, andvpn_server_modifiedall clearGetVpnViewbefore commit; another request can repopulate the cache with pre-commit VPN data and leave stale responses after the transaction finishes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openwisp_controller/config/base/vpn.py` around lines 366 - 371, The VPN cache invalidation hooks are running before the database transaction commits, which can let stale pre-commit data be cached again by another request. Update the cache invalidation setup in the Vpn class so the CacheDependency entries for post_save, pre_delete, and vpn_server_modified defer clearing GetVpnView until commit by using the existing cls._invalidate_vpn_view_cache target with on_commit enabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@openwisp_controller/config/base/vpn.py`:
- Around line 366-371: The VPN cache invalidation hooks are running before the
database transaction commits, which can let stale pre-commit data be cached
again by another request. Update the cache invalidation setup in the Vpn class
so the CacheDependency entries for post_save, pre_delete, and
vpn_server_modified defer clearing GetVpnView until commit by using the existing
cls._invalidate_vpn_view_cache target with on_commit enabled.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c7e5a077-bdc0-4cc0-a2c3-1c3d7bc7cba6
📒 Files selected for processing (5)
.github/workflows/ci.ymlopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/pki/tests/test_api.py
There was a problem hiding this comment.
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/apps.py (1)
139-149: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winDefer these
post_deleteinvalidations to commit. Running them inside the transaction leaves a window where a concurrent request can repopulateDeviceGroupCommonNamecache with stale data before the delete commits. The handler only needsorganization_id/common_name, so this can safely be deferred.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openwisp_controller/config/apps.py` around lines 139 - 149, The post_delete cache invalidations for the CacheDependency entries using devicegroup_delete_handler are running too early. Update the CacheDependency configuration for self.devicegroup_model and self.cert_model to defer the invalidation until commit by enabling on_commit, so the handler runs after the transaction is finalized. Keep the change focused on the CacheDependency definitions in apps.py and preserve the existing handler target.
♻️ Duplicate comments (1)
openwisp_controller/config/base/vpn.py (1)
1147-1160: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winN+1 queries per client still present.
Same structural issue flagged in a previous review (then targeting
_send_config_modified_signal, nowupdate_status_if_checksum_changed()):vpn.vpnclient_set.iterator()has noselect_related, soclient.configand the checksum recompute path will hit the DB per client.♻️ Proposed fix
- for client in vpn.vpnclient_set.iterator(): + for client in vpn.vpnclient_set.select_related( + "config", "config__device", "config__device__group" + ).iterator(): config = client.config # keep the historical signal action for this related change config._config_modified_action = "related_template_changed" config.update_status_if_checksum_changed()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openwisp_controller/config/base/vpn.py` around lines 1147 - 1160, Repeated per-client database hits remain in invalidate_clients_cache because vpn.vpnclient_set.iterator() does not prefetch related config data, so client.config and update_status_if_checksum_changed() trigger N+1 queries. Update the queryset used in invalidate_clients_cache to fetch the related config up front (for example via select_related on the client relation), then keep the existing loop and _config_modified_action assignment so the checksum/status update still happens per client without extra queries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@openwisp_controller/config/apps.py`:
- Around line 139-149: The post_delete cache invalidations for the
CacheDependency entries using devicegroup_delete_handler are running too early.
Update the CacheDependency configuration for self.devicegroup_model and
self.cert_model to defer the invalidation until commit by enabling on_commit, so
the handler runs after the transaction is finalized. Keep the change focused on
the CacheDependency definitions in apps.py and preserve the existing handler
target.
---
Duplicate comments:
In `@openwisp_controller/config/base/vpn.py`:
- Around line 1147-1160: Repeated per-client database hits remain in
invalidate_clients_cache because vpn.vpnclient_set.iterator() does not prefetch
related config data, so client.config and update_status_if_checksum_changed()
trigger N+1 queries. Update the queryset used in invalidate_clients_cache to
fetch the related config up front (for example via select_related on the client
relation), then keep the existing loop and _config_modified_action assignment so
the checksum/status update still happens per client without extra queries.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 67c8d211-2a4d-4ef0-aecd-340045ec772f
📒 Files selected for processing (7)
openwisp_controller/config/apps.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_controller.py
💤 Files with no reviewable changes (1)
- openwisp_controller/config/base/config.py
📜 Review details
⏰ Context from checks skipped due to timeout. (9)
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/apps.pyopenwisp_controller/config/tests/test_config.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/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/apps.pyopenwisp_controller/config/tests/test_config.py
🧠 Learnings (8)
📚 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/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/apps.pyopenwisp_controller/config/tests/test_config.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/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/apps.pyopenwisp_controller/config/tests/test_config.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/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/apps.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:18.414Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/base/models.py:571-572
Timestamp: 2026-06-25T12:20:18.414Z
Learning: When writing or reviewing tests that override pagination behavior via OpenWispPagination.paginate_queryset(), patch `view.pagination_page_size` (not `page_size`). The method uses `getattr(view, "pagination_page_size", self.page_size)`, so tests must set the attribute on the view to affect pagination. If the view class does not define `pagination_page_size`, using `unittest.mock.patch(..., create=True)` is intentional and correct because the attribute may not exist until patched.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:45.387Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/tests/test_api.py:916-932
Timestamp: 2026-06-25T12:20:45.387Z
Learning: When reviewing API pagination behavior in openwisp-controller, assume `OpenWispPagination.paginate_queryset()` allows a per-view page-size override via `getattr(view, "pagination_page_size", self.page_size)` (so `view.pagination_page_size`, if present, should affect pagination). In Python tests, it is valid to patch `pagination_page_size` on a view class even if the attribute isn’t declared on the class by default, by using `unittest.mock.patch.object(..., "pagination_page_size", ..., create=True)` so the override is available for the pagination logic during the test.
Applied to files:
openwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/tests/test_config.py
🔇 Additional comments (10)
openwisp_controller/config/tests/test_config.py (5)
571-587: Still missingcaptureOnCommitCallbacks— assertion doesn't exercise_resolve_cert_dependency().Only the comments changed here;
cert.save()still isn't wrapped inself.captureOnCommitCallbacks(execute=True). SinceTestConfigextendsTestCase, thetransaction.on_commitcallback that would invoke_resolve_cert_dependency()never fires, so this test passes trivially regardless of whether the "returns early because VpnClient was deleted" logic is correct. This was already raised in a previous review of this test.
932-975: LGTM!
1059-1197: LGTM!
1365-1391: LGTM!
1207-1211: 🎯 Functional CorrectnessNo issue here Each test method calls
_connect()only once, so the fixeddispatch_uiddoes not create a collision in this class.> Likely an incorrect or invalid review comment.openwisp_controller/config/base/base.py (1)
5-50: LGTM!Also applies to: 263-283, 341-355, 377-387, 407-415
openwisp_controller/config/apps.py (1)
45-155: LGTM!Also applies to: 168-253, 356-387
openwisp_controller/config/base/vpn.py (1)
340-410: LGTM!openwisp_controller/config/tests/test_api.py (1)
6-6: LGTM!Also applies to: 20-20, 1492-1517
openwisp_controller/config/tests/test_controller.py (1)
187-188: LGTM!Also applies to: 479-491
|
@coderabbitai summary |
✅ Action performedSummary regeneration triggered. |
86f5e0c to
d32576a
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@openwisp_controller/config/apps.py`:
- Around line 139-149: The post_delete cache invalidations for the device group
and cert dependencies are running before the transaction commits, which can race
with concurrent reads. Update the CacheDependency entries in apps.py to use
on_commit for the delete handlers, and adjust devicegroup_delete_handler so it
receives the needed snapshot data (organization_id, common_name, and model name)
instead of relying on the soon-to-be-deleted object. Make sure the delete path
still targets devicegroup_delete_handler and defers the invalidation until
commit.
- Around line 84-108: The cache invalidations wired in `CacheDependency` for
`DeviceChecksumView.invalidate_get_device_cache` and
`DeviceChecksumView.invalidate_get_device_cache_on_config_deactivated` should
run after transaction commit instead of immediately. Update the
`CacheDependency` entries for `post_save` and `config_deactivated` in the app
config setup so they defer to commit consistently with the existing
`post_delete` handling, preventing stale `DeviceChecksumView` data from being
repopulated before the transaction is finalized.
In `@openwisp_controller/config/base/base.py`:
- Around line 263-365: The tracked-field snapshot is being reused across later
saves, so `_snapshot_handler` should consume and clear the stored snapshot after
each post-save comparison. Update the snapshot lifecycle around
`_snapshot_handler`, `_tracked_fields_changed`, and `_should_skip` so a fresh
snapshot is taken per save and any old entry in
`instance.__dict__[self._SNAPSHOT_ATTR]` for `self._uid` is removed once it has
been evaluated. This prevents stale snapshot objects from affecting unrelated
future `save(update_fields=...)` calls.
In `@openwisp_controller/config/base/vpn.py`:
- Around line 367-391: Update the CacheDependency entries in Vpn view-cache
invalidation so that both the post_save path and the vpn_server_modified signal
defer invalidation until transaction commit. In the Vpn cache dependency setup,
change the on_commit behavior for cls._invalidate_vpn_view_cache so
GetVpnView.get_vpn is not invalidated/repopulated before the transaction is
committed.
In `@openwisp_controller/config/tests/test_vpn.py`:
- Around line 573-600: The two renew-cascade tests duplicate the same
VPN/template/device setup and initial config checksum assertions, so factor that
shared logic into a small helper in test_vpn.py and keep each test focused on
its specific action (`vpn.ca.renew()` vs `vpn.cert.renew()`). Use the existing
test methods `test_ca_renew_cascades_to_client_config` and
`test_cert_renew_cascades_to_client_config`, plus the helper builders like
`_create_vpn`, `_create_template`, and `_create_device_config`, to reduce
repetition while preserving the distinct post-renew assertions and signal check.
🪄 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: bfba3d59-70c2-4c62-8109-c22c977554d8
📒 Files selected for processing (18)
.github/workflows/ci.ymlopenwisp_controller/config/apps.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/device_group.pyopenwisp_controller/config/base/multitenancy.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/pki/tests/test_api.pyopenwisp_controller/subnet_division/admin.pyopenwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/subnet_division/tests/test_models.py
💤 Files with no reviewable changes (2)
- openwisp_controller/config/base/multitenancy.py
- openwisp_controller/config/base/device_group.py
📜 Review details
⏰ Context from checks skipped due to timeout. (11)
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/subnet_division/admin.pyopenwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.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/subnet_division/admin.pyopenwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.py
🧠 Learnings (10)
📚 Learning: 2026-02-24T16:24:55.443Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1233
File: .github/workflows/backport.yml:22-22
Timestamp: 2026-02-24T16:24:55.443Z
Learning: In repositories within the OpenWISP organization, it is acceptable to reference reusable workflows from other OpenWISP-controlled repos using mutable refs (e.g., master) in .github/workflows. This is permissible due to the shared trust boundary within the organization. If applying this pattern, ensure the target repos are under the same organization and maintain awareness of potential breakages from upstream mutable refs; consider pinning to a tagged version for longer-term stability when appropriate.
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-02-24T16:25:20.080Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1233
File: .github/workflows/backport.yml:35-35
Timestamp: 2026-02-24T16:25:20.080Z
Learning: In .github/workflows/backport.yml, enforce that backport-on-comment triggers only for users with author_association MEMBE R or OWNER (COLLABORATOR excluded), reflecting maintainer feedback. Update the trigger condition to check author_association and restrict to MEMBERS/OWNERS; document rationale and PR `#1233` reference in code comments.
Applied to files:
.github/workflows/ci.yml
📚 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/subnet_division/admin.pyopenwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.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/subnet_division/admin.pyopenwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.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/subnet_division/admin.pyopenwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/base.pyopenwisp_controller/config/handlers.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:18.414Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/base/models.py:571-572
Timestamp: 2026-06-25T12:20:18.414Z
Learning: When writing or reviewing tests that override pagination behavior via OpenWispPagination.paginate_queryset(), patch `view.pagination_page_size` (not `page_size`). The method uses `getattr(view, "pagination_page_size", self.page_size)`, so tests must set the attribute on the view to affect pagination. If the view class does not define `pagination_page_size`, using `unittest.mock.patch(..., create=True)` is intentional and correct because the attribute may not exist until patched.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:45.387Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/tests/test_api.py:916-932
Timestamp: 2026-06-25T12:20:45.387Z
Learning: When reviewing API pagination behavior in openwisp-controller, assume `OpenWispPagination.paginate_queryset()` allows a per-view page-size override via `getattr(view, "pagination_page_size", self.page_size)` (so `view.pagination_page_size`, if present, should affect pagination). In Python tests, it is valid to patch `pagination_page_size` on a view class even if the attribute isn’t declared on the class by default, by using `unittest.mock.patch.object(..., "pagination_page_size", ..., create=True)` so the override is available for the pagination logic during the test.
Applied to files:
openwisp_controller/pki/tests/test_api.pyopenwisp_controller/config/tests/test_controller.pyopenwisp_controller/config/tests/utils.pyopenwisp_controller/config/tests/test_device.pyopenwisp_controller/config/tests/test_api.pyopenwisp_controller/subnet_division/tests/test_models.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/tests/test_config.py
🪛 ast-grep (0.44.0)
openwisp_controller/subnet_division/tests/test_models.py
[warning] 26-26: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model("config", "VpnClient")
Note: [CWE-502] Deserialization of Untrusted Data.
(keras-load-model-python)
openwisp_controller/config/base/config.py
[warning] 241-241: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model("openwisp_users", "Organization")
Note: [CWE-502] Deserialization of Untrusted Data.
(keras-load-model-python)
openwisp_controller/config/base/base.py
[warning] 212-212: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model(app_label, model_name)
Note: [CWE-502] Deserialization of Untrusted Data.
(keras-load-model-python)
openwisp_controller/config/tests/test_config.py
[warning] 27-27: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model("config", "OrganizationConfigSettings")
Note: [CWE-502] Deserialization of Untrusted Data.
(keras-load-model-python)
[warning] 28-28: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model("config", "Template")
Note: [CWE-502] Deserialization of Untrusted Data.
(keras-load-model-python)
🔇 Additional comments (17)
.github/workflows/ci.yml (1)
75-76: Same concern already raised.This TODO workaround using a mutable branch tarball was already flagged in a previous review requesting a pinned commit SHA for reproducibility.
openwisp_controller/subnet_division/admin.py (1)
28-38: LGTM!openwisp_controller/subnet_division/rule_types/vpn.py (1)
44-58: LGTM! Reordering correctly ensuresinstance.ipis persisted beforesuper().post_provision_handler()caches the checksum, resolving theip_address_<vpn_pk>template variable at the right time — validated by the new regression test intest_models.py.openwisp_controller/subnet_division/tests/test_models.py (1)
26-26: LGTM! Regression test correctly exercises thepost_provision_handlerordering fix by asserting theip_address_<vpn_pk>context resolves and thatchecksum_dbmatches the freshly computed checksum after a DB refresh.Also applies to: 66-94
openwisp_controller/config/tests/test_device.py (1)
606-663: LGTM! Moving these cache-invalidation tests intoTestTransactionDevice(aTransactionTestCase) is necessary sinceCacheDependencydefers invalidation viatransaction.on_commit, which never fires under the defaultTestCasetransaction wrapping.openwisp_controller/config/tests/test_config.py (1)
571-975: LGTM!Also applies to: 1059-1391
openwisp_controller/config/tests/test_api.py (1)
1492-1517: LGTM!openwisp_controller/config/tests/test_controller.py (1)
187-188: LGTM!Also applies to: 479-491
openwisp_controller/config/tests/test_vpn.py (1)
519-557: LGTM!Also applies to: 601-2008
openwisp_controller/pki/tests/test_api.py (1)
164-164: LGTM!Based on learnings, "treat changes to the expected query count in
assertNumQueries(...)calls as routine test maintenance" and avoid requiring justification comments for such deltas.Also applies to: 292-292
Source: Learnings
openwisp_controller/config/tests/utils.py (1)
174-181: 🎯 Functional CorrectnessNo issue with the shared WireGuard test fixture config override
openwisp_controller/config/handlers.py (2)
72-74: Duplicate: schedule this cache invalidation after commit.The direct
.delay()call can run before the surrounding transaction commits; this was already flagged in a previous review comment for this handler.
44-60: LGTM!Also applies to: 62-71
openwisp_controller/config/base/base.py (1)
5-51: LGTM!Also applies to: 127-262, 368-416
openwisp_controller/config/apps.py (1)
5-5: LGTM!Also applies to: 50-83, 110-138, 152-155, 356-387
openwisp_controller/config/base/config.py (1)
29-35: LGTM!Also applies to: 49-49, 177-299
openwisp_controller/config/base/vpn.py (1)
18-18: LGTM!Also applies to: 37-58, 331-360, 393-410
|
The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3). |
Flake8 E501 Line Too LongHello @pandafy, The CI build failed because of a To fix this:
After fixing the line, please commit the changes and push again. |
Migrations Check FailedHello @pandafy, There are multiple CI jobs failing due to a "Migrations check failed" error. This indicates that the database migrations have not been applied correctly. Fix: ./manage.py makemigrations
./manage.py migrateAfter applying the migrations locally, commit the changes and push again. |
fef2d66 to
75c59a7
Compare
|
The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3). |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/developer/utils.rst (1)
477-485: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMake the documented dispatch UIDs match
build_dispatch_uid().The output lists the resolver and tracked fields at Lines 479 and 484, but the corresponding UIDs omit them.
CacheDependency.build_dispatch_uid()appends both components, so these examples cannot match the command’s actual output.Proposed correction
- uid: cache_invalidation.config.config.config.device.post_save.update_status_if_checksum_changed + uid: cache_invalidation.config.config.config.device.post_save.update_status_if_checksum_changed._resolve_device_dependency.os+organization_id ... - uid: cache_invalidation.config.config.config.template.pre_delete.update_status_if_checksum_changed + uid: cache_invalidation.config.config.config.template.pre_delete.update_status_if_checksum_changed._resolve_template_dependency🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/developer/utils.rst` around lines 477 - 485, Update the documented UIDs for the config.device and config.template examples to include the resolver and tracked-field components produced by CacheDependency.build_dispatch_uid(). Ensure both examples exactly match the dispatch UID format generated by build_dispatch_uid(), including the applicable fields shown in each resolve configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@docs/developer/utils.rst`:
- Around line 477-485: Update the documented UIDs for the config.device and
config.template examples to include the resolver and tracked-field components
produced by CacheDependency.build_dispatch_uid(). Ensure both examples exactly
match the dispatch UID format generated by build_dispatch_uid(), including the
applicable fields shown in each resolve configuration.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 16af539d-ace5-47f6-9078-f4e59965dff1
📒 Files selected for processing (7)
docs/developer/utils.rstopenwisp_controller/config/apps.pyopenwisp_controller/config/base/cache.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/management/commands/print_cache_dependencies.pyopenwisp_controller/config/tests/test_config.py
📜 Review details
⏰ Context from checks skipped due to timeout. (10)
- GitHub Check: CodeRabbit
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Kilo Code Review
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/config/management/commands/print_cache_dependencies.pyopenwisp_controller/config/base/cache.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.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/management/commands/print_cache_dependencies.pyopenwisp_controller/config/base/cache.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.py
🧠 Learnings (8)
📚 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/management/commands/print_cache_dependencies.pyopenwisp_controller/config/base/cache.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.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/management/commands/print_cache_dependencies.pyopenwisp_controller/config/base/cache.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.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/management/commands/print_cache_dependencies.pyopenwisp_controller/config/base/cache.pyopenwisp_controller/config/base/vpn.pyopenwisp_controller/config/base/config.pyopenwisp_controller/config/tests/test_config.pyopenwisp_controller/config/apps.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:18.414Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/base/models.py:571-572
Timestamp: 2026-06-25T12:20:18.414Z
Learning: When writing or reviewing tests that override pagination behavior via OpenWispPagination.paginate_queryset(), patch `view.pagination_page_size` (not `page_size`). The method uses `getattr(view, "pagination_page_size", self.page_size)`, so tests must set the attribute on the view to affect pagination. If the view class does not define `pagination_page_size`, using `unittest.mock.patch(..., create=True)` is intentional and correct because the attribute may not exist until patched.
Applied to files:
openwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/config/tests/test_config.py
📚 Learning: 2026-06-25T12:20:45.387Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/tests/test_api.py:916-932
Timestamp: 2026-06-25T12:20:45.387Z
Learning: When reviewing API pagination behavior in openwisp-controller, assume `OpenWispPagination.paginate_queryset()` allows a per-view page-size override via `getattr(view, "pagination_page_size", self.page_size)` (so `view.pagination_page_size`, if present, should affect pagination). In Python tests, it is valid to patch `pagination_page_size` on a view class even if the attribute isn’t declared on the class by default, by using `unittest.mock.patch.object(..., "pagination_page_size", ..., create=True)` so the override is available for the pagination logic during the test.
Applied to files:
openwisp_controller/config/tests/test_config.py
🪛 ast-grep (0.44.1)
openwisp_controller/config/base/cache.py
[warning] 129-129: Loading a Keras model from an untrusted file can execute arbitrary code via Lambda layers or custom objects. Load only trusted models and avoid deserializing custom objects from untrusted sources.
Context: load_model(app_label, model_name)
Note: [CWE-502] Deserialization of Untrusted Data.
(keras-load-model-python)
[info] 234-234: use jsonify instead of json.dumps for JSON output
Context: json.dumps([dep.describe() for dep in dependencies], indent=2)
Note: [CWE-116] Improper Encoding or Escaping of Output.
(use-jsonify)
🔇 Additional comments (19)
openwisp_controller/config/management/commands/print_cache_dependencies.py (1)
2-4: LGTM!Also applies to: 7-18, 21-22
docs/developer/utils.rst (1)
433-457: LGTM!Also applies to: 461-476, 487-499
openwisp_controller/config/base/cache.py (6)
19-35: LGTM!
96-164: LGTM!
166-198: LGTM!
200-259: LGTM!
261-342: LGTM!
344-402: LGTM!openwisp_controller/config/apps.py (4)
45-55: LGTM!
110-115: 🗄️ Data Integrity & Integration | ⚡ Quick winConfirm the immediate (
on_commit=False) org-disable invalidation is intentional.Unlike the device/config handlers in this same list (which were deliberately moved to commit-time to avoid a concurrent request repopulating a stale entry), this one invalidates on
pre_saveimmediately. If a controller request reads a device of this org during the disable transaction, it can repopulate the controller-view cache with still-enabled data that persists after commit. This preserves the previously-wiredOrganization.pre_savebehavior, so please confirm the timing asymmetry is deliberate (e.g. the handler must observe the old DB state atpre_save).
357-388: LGTM!
84-151: 🩺 Stability & AvailabilityNo change needed. The handlers already take a single
instanceargument, and both custom signals sendinstance=in their payloads.> Likely an incorrect or invalid review comment.openwisp_controller/config/base/vpn.py (3)
1157-1159: 🚀 Performance & ScalabilityConfirm the
select_relatedcovers every relation touched during checksum recompute.
update_status_if_checksum_changed()regenerates the config viaget_context(), which for device configs reads the device's organization/config_settings(not justgroup). The currentselect_relatedpreloadsconfig__device__groupbut not the organization relation, so a VPN with many clients may still incur a per-client query for org context — leaving the previously flagged N+1 only partially resolved.#!/bin/bash set -euo pipefail # Inspect whether get_context / config generation traverses device.organization(.config_settings) rg -nP -C4 'def get_context' openwisp_controller/config/base/device.py openwisp_controller/config/base/config.py rg -nP -C3 'organization(__|\.)config_settings|self\.device\.organization|_get_organization' openwisp_controller/config/base -g '*.py'Suggested direction if org context is traversed
for client in vpn.vpnclient_set.select_related( - "config", "config__device", "config__device__group" + "config", + "config__device", + "config__device__group", + "config__device__organization__config_settings", ).iterator():
37-38: LGTM!
329-406: LGTM!openwisp_controller/config/base/config.py (3)
29-30: LGTM!
269-293: LGTM!
246-266: 🚀 Performance & ScalabilityKeep the
django_x509.Certdependency broad._resolve_cert_dependency()already filters revoked or unlinked certs, and the currentpost_savehook is what catches cert renewals and other state changes that can alter the rendered config.> Likely an incorrect or invalid review comment.openwisp_controller/config/tests/test_config.py (1)
21-21: LGTM!
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3). |
|
@coderabbitai any remaining issue? |
|
Tip For best results, initiate chat on the files or code changes.
ᕦ(ò_óˇ)ᕤ |
|
Proposed change log entry: |
|
@coderabbitai resolve |
✅ Action performedComments resolved and changes approved. |
Checklist
Reference to Existing Issue
Closes #1095
Blockers