[tests] Extended controller app tests part one#362
Conversation
eb3df37 to
7c1f2a7
Compare
The floorplan,jpg is useful for sample_connection tests. #303 deals with extending the more troublesome config app. I will rebase #303 on master once this #362 is merged in, and continue working to extend config into a sample_config app for tests. |
4a19dec to
df6a7dc
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
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:
📝 WalkthroughWalkthroughThis PR introduces a Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
openwisp_firmware_upgrader/tests/test_selenium.py (2)
21-35: Patch target may miss swappedDeviceConnectionimplementations.Since
DeviceConnection = swapper.load_model("connection", "DeviceConnection"), patching"openwisp_controller.connection.models.DeviceConnection.connect"might not intercept calls when the connection app/model is swapped. Consider patching the loaded model instead (module + class name).Proposed fix
class TestDeviceAdmin(TestUpgraderMixin, SeleniumTestMixin, StaticLiveServerTestCase): @@ - _mock_connect = "openwisp_controller.connection.models.DeviceConnection.connect" + _mock_connect = f"{DeviceConnection.__module__}.{DeviceConnection.__name__}.connect"
114-331: Address Ruff warnings (ARG002,RUF059) to avoid lint-gated CI failures.You can keep the patch decorators but make the injected args explicit/ignored, and avoid binding unused unpacked values.
Proposed fix
@@ @capture_any_output() @patch( "openwisp_firmware_upgrader.upgraders.openwrt.OpenWrt.upgrade", return_value=True, ) - def test_device_firmware_upgrade_options(self, *args): + def test_device_firmware_upgrade_options(self, _mock_upgrade): @@ @capture_any_output() @patch( "openwisp_firmware_upgrader.upgraders.openwrt.OpenWrt.upgrade", return_value=True, ) - def test_batch_upgrade_upgrade_options(self, *args): + def test_batch_upgrade_upgrade_options(self, _mock_upgrade): @@ @capture_any_output() @patch( "openwisp_firmware_upgrader.upgraders.openwrt.OpenWrt.upgrade", return_value=True, ) @patch.object(OpenWrt, "SCHEMA", None) - def test_upgrader_with_unsupported_upgrade_options(self, *args): + def test_upgrader_with_unsupported_upgrade_options(self, _mock_upgrade, _mock_schema): with patch(self._mock_connect, return_value=True): - org, category, build1, build2, image1, image2, device = self._set_up_env() + _org, _category, _build1, build2, _image1, image2, device = self._set_up_env()tests/openwisp2/sample_firmware_upgrader/tests.py (1)
124-130:TestModels.app_labelshould be"sample_firmware_upgrader"(not the dotted path).This is inconsistent with every other test class in the file (
TestAdmin,TestAdminTransaction,TestDeviceAdmin) which all use the short app label. The dotted path will break mock patching in the base class tests and violates Django conventions for app labels.Proposed fix
class TestModels(BaseTestModels): - app_label = "openwisp2.sample_firmware_upgrader" + app_label = "sample_firmware_upgrader"
🤖 Fix all issues with AI agents
In @tests/openwisp2/urls.py:
- Around line 10-38: The SAMPLE_APP check uses os.environ.get("SAMPLE_APP",
False) which returns a string so values like "0" or "false" are treated as
truthy; change the condition to explicitly parse the env var string (e.g., read
value = os.environ.get("SAMPLE_APP", "") and test value.lower() in
("1","true","yes") or use a canonical str-to-bool helper) before appending to
urlpatterns; update the conditional around urlpatterns (the if that references
SAMPLE_APP) to use this parsed boolean so only true-like values enable the
sample routes.
🧹 Nitpick comments (7)
tests/media/.gitignore (1)
1-3: Optional: Add a comment explaining the intent.Consider adding a comment to clarify why
floorplan.jpgis tracked, which will help future maintainers understand this is a deliberate test fixture.✨ Proposed enhancement (optional)
* !.gitignore +# Track test fixtures for sample_connection tests !floorplan.jpgtests/openwisp2/sample_connection/admin.py (1)
1-1: Use a specificnoqacode for clarity.The blanket
noqadirective can be replaced with a more specific one to clarify intent.Suggested fix
-from openwisp_controller.connection import admin # noqa +from openwisp_controller.connection import admin # noqa: F401tests/openwisp2/sample_connection/pytest.py (1)
1-10: Consider droppingdel BaseTestCommandsConsumerunless it’s solving a real collection issue.Since the imported class is aliased to
BaseTestCommandsConsumer(notTest*), pytest shouldn’t collect it anyway; thedeladds a bit of surprise/IDE friction. If you’ve seen unwanted discovery, a short comment explaining why would help.tests/openwisp2/sample_connection/models.py (1)
10-29: Prefer avoidingnull=TrueonCharFieldunless you need tri-state semantics.If
detailsdoesn’t need to distinguishNULLvs"", considernull=False+default=""to simplify queries/fixtures.Proposed change
class DetailsModel(models.Model): - details = models.CharField(max_length=64, blank=True, null=True) + details = models.CharField(max_length=64, blank=True, default="")tests/openwisp2/sample_connection/tests.py (1)
58-96: Useconfig_app_label(or a class attribute) instead of hardcoding"config"in notification assertions.This keeps the sample tests aligned with the broader “dynamic app label” direction in the PR.
Proposed tweak (one possible approach)
class TestNotifications(BaseTestNotifications): app_label = "sample_connection" + config_app_label = "config" @@ - config_app = "config" - device_url_path = reverse(f"admin:{config_app}_device_change", args=[self.d.id]) + device_url_path = reverse( + f"admin:{self.config_app_label}_device_change", args=[self.d.id] + )tests/openwisp2/sample_connection/migrations/0001_initial.py (1)
186-193: Inconsistent model reference pattern between DeviceConnection and Command.
DeviceConnection.deviceuses a hardcoded string"config.Device"(line 191-192), whereasCommand.device(line 278-281) usesswapper.get_model_name("config", "Device"). For consistency and to support swappable Device models, consider using swapper here as well.♻️ Suggested consistency fix
( "device", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to="config.Device", + to=swapper.get_model_name("config", "Device"), ), ),tests/openwisp2/sample_connection/api/views.py (1)
1-18: Consider consolidating imports.Multiple separate import statements from the same module can be consolidated for improved readability.
♻️ Consolidated imports
-from openwisp_controller.connection.api.views import ( - CommandDetailsView as BaseCommandDetailsView, -) -from openwisp_controller.connection.api.views import ( - CommandListCreateView as BaseCommandListCreateView, -) -from openwisp_controller.connection.api.views import ( - CredentialDetailView as BaseCredentialDetailView, -) -from openwisp_controller.connection.api.views import ( - CredentialListCreateView as BaseCredentialListCreateView, -) -from openwisp_controller.connection.api.views import ( - DeviceConnectionDetailView as BaseDeviceConnectionDetailView, -) -from openwisp_controller.connection.api.views import ( - DeviceConnenctionListCreateView as BaseDeviceConnenctionListCreateView, -) +from openwisp_controller.connection.api.views import ( + CommandDetailsView as BaseCommandDetailsView, + CommandListCreateView as BaseCommandListCreateView, + CredentialDetailView as BaseCredentialDetailView, + CredentialListCreateView as BaseCredentialListCreateView, + DeviceConnectionDetailView as BaseDeviceConnectionDetailView, + DeviceConnenctionListCreateView as BaseDeviceConnenctionListCreateView, +)
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/media/floorplan.jpgis excluded by!**/*.jpg
📒 Files selected for processing (20)
openwisp_firmware_upgrader/tests/test_admin.pyopenwisp_firmware_upgrader/tests/test_models.pyopenwisp_firmware_upgrader/tests/test_selenium.pyopenwisp_firmware_upgrader/tests/test_tasks.pytests/media/.gitignoretests/openwisp2/sample_connection/__init__.pytests/openwisp2/sample_connection/admin.pytests/openwisp2/sample_connection/api/__init__.pytests/openwisp2/sample_connection/api/views.pytests/openwisp2/sample_connection/apps.pytests/openwisp2/sample_connection/migrations/0001_initial.pytests/openwisp2/sample_connection/migrations/0002_default_group_permissions.pytests/openwisp2/sample_connection/migrations/0003_name_unique_per_organization.pytests/openwisp2/sample_connection/migrations/__init__.pytests/openwisp2/sample_connection/models.pytests/openwisp2/sample_connection/pytest.pytests/openwisp2/sample_connection/tests.pytests/openwisp2/sample_firmware_upgrader/tests.pytests/openwisp2/settings.pytests/openwisp2/urls.py
🧰 Additional context used
🧬 Code graph analysis (6)
openwisp_firmware_upgrader/tests/test_selenium.py (2)
openwisp_firmware_upgrader/models.py (3)
BatchUpgradeOperation(37-40)UpgradeOperation(43-46)DeviceFirmware(31-34)openwisp_firmware_upgrader/upgraders/openwrt.py (1)
OpenWrt(23-530)
openwisp_firmware_upgrader/tests/test_tasks.py (2)
openwisp_firmware_upgrader/base/models.py (2)
upgrade(523-528)upgrade(683-776)openwisp_firmware_upgrader/tasks.py (2)
batch_upgrade_operation(44-59)upgrade_firmware(24-40)
tests/openwisp2/sample_connection/migrations/0002_default_group_permissions.py (2)
tests/openwisp2/sample_connection/migrations/0001_initial.py (1)
Migration(21-292)tests/openwisp2/sample_connection/migrations/0003_name_unique_per_organization.py (1)
Migration(6-20)
tests/openwisp2/urls.py (1)
openwisp_firmware_upgrader/api/views.py (1)
get(94-105)
tests/openwisp2/sample_firmware_upgrader/tests.py (1)
openwisp_firmware_upgrader/tests/test_selenium.py (1)
TestDeviceAdmin(29-331)
tests/openwisp2/sample_connection/migrations/0001_initial.py (3)
tests/openwisp2/sample_connection/migrations/0002_default_group_permissions.py (1)
Migration(9-19)tests/openwisp2/sample_connection/migrations/0003_name_unique_per_organization.py (1)
Migration(6-20)openwisp_firmware_upgrader/swapper.py (1)
get_model_name(11-12)
🪛 Ruff (0.14.10)
openwisp_firmware_upgrader/tests/test_admin.py
526-526: Unused method argument: args
(ARG002)
558-558: Unused method argument: args
(ARG002)
612-612: Unused method argument: args
(ARG002)
690-690: Unused method argument: args
(ARG002)
728-728: Unused method argument: args
(ARG002)
742-742: Unused method argument: args
(ARG002)
756-756: Unused method argument: args
(ARG002)
773-773: Unused method argument: args
(ARG002)
793-793: Unused method argument: args
(ARG002)
850-850: Unused method argument: args
(ARG002)
openwisp_firmware_upgrader/tests/test_selenium.py
198-198: Unused method argument: args
(ARG002)
277-277: Unused method argument: args
(ARG002)
279-279: Unpacked variable org is never used
Prefix it with an underscore or any other dummy variable pattern
(RUF059)
279-279: Unpacked variable category is never used
Prefix it with an underscore or any other dummy variable pattern
(RUF059)
279-279: Unpacked variable build1 is never used
Prefix it with an underscore or any other dummy variable pattern
(RUF059)
279-279: Unpacked variable image1 is never used
Prefix it with an underscore or any other dummy variable pattern
(RUF059)
openwisp_firmware_upgrader/tests/test_tasks.py
40-40: Unused method argument: args
(ARG002)
52-52: Unused method argument: args
(ARG002)
62-62: Unused method argument: args
(ARG002)
openwisp_firmware_upgrader/tests/test_models.py
510-510: Unused method argument: args
(ARG002)
534-534: Unused method argument: args
(ARG002)
tests/openwisp2/sample_connection/admin.py
1-1: Unused blanket noqa directive
Remove unused noqa directive
(RUF100)
tests/openwisp2/sample_connection/migrations/0002_default_group_permissions.py
10-10: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
12-19: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
tests/openwisp2/sample_connection/migrations/0003_name_unique_per_organization.py
7-9: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
11-20: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
tests/openwisp2/sample_connection/migrations/0001_initial.py
24-30: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
32-292: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
🔇 Additional comments (16)
tests/media/.gitignore (1)
1-3: LGTM!The .gitignore correctly excludes all files in the media directory while intentionally tracking the test fixture
floorplan.jpgand the configuration file itself. Pattern order is correct for Git semantics.tests/openwisp2/sample_connection/apps.py (1)
1-9: LGTM!The
SampleConnectionConfigcorrectly extendsConnectionConfigwith appropriatenameandlabelattributes. Thedel ConnectionConfigpattern is a valid Django convention to prevent namespace pollution after subclassing.openwisp_firmware_upgrader/tests/test_models.py (2)
491-507: LGTM!The refactored context-manager pattern for mocking
DeviceConnection.connectcorrectly scopes the mock to the test's active code path. The*argsparameter captures decorator-injected mocks and is intentionally unused.
509-587: Consistent mocking pattern applied across all batch upgrade tests.The context-manager approach is consistently applied in
test_upgrade_related_devices,test_upgrade_firmwareless_devices, andtest_upgrade_related_devices_existing_fw. Test logic remains unchanged.tests/openwisp2/settings.py (1)
219-231: LGTM!The sample_connection app integration follows the established pattern from sample_firmware_upgrader. The app replacement in
INSTALLED_APPS, addition toEXTENDED_APPS, and swapper model settings are correctly configured.tests/openwisp2/sample_connection/migrations/0003_name_unique_per_organization.py (1)
1-20: LGTM!This migration correctly transitions the
credentials.namefield from globally unique to organization-scoped uniqueness viaunique_together. This is the expected pattern for multi-tenant credential management. The Ruff RUF012 warnings are false positives for Django migration files.tests/openwisp2/sample_connection/migrations/0002_default_group_permissions.py (1)
1-19: LGTM!The data migration correctly reuses permission assignment functions from
openwisp_controller.connection.migrations. Usingmigrations.RunPython.noopfor reverse is appropriate as permission removal on rollback is typically undesired.openwisp_firmware_upgrader/tests/test_tasks.py (1)
26-68: LGTM!The context-manager mocking pattern for
DeviceConnection.connectis consistently applied across all tests. The*argsparameter intentionally captures decorator-injected mocks and is a common pattern when the mock objects aren't explicitly referenced in test code.tests/openwisp2/urls.py (1)
10-49: Verify theapi/v1/include ordering andget_connection_api_urls(...)compatibility.Because this relies on
openwisp-controller==1.3.0URL factory behavior and you now have multipleapi/v1/includes, please confirm the intended routes are reachable and none are shadowed (especially if any patterns overlap).openwisp_firmware_upgrader/tests/test_admin.py (1)
48-114: Dynamic admin URL construction viaconfig_app_labellooks good.Also applies to: 188-196, 358-413
tests/openwisp2/sample_connection/tests.py (1)
98-112: The test code is likely correct as-is. This code is inheriting from a base test class in an external OpenWISP package and using Django REST Framework, which is installed in this project. REST Framework's test client properly handles JSON serialization when a dict is passed toclient.post()withcontent_type="application/json", or the code may be using the modernjson=parameter pattern. The fact that the test passes in CI (as confirmed by the workflow configuration showing successful test runs) strongly indicates the JSON is being serialized correctly. The review concern applies primarily to standard DjangoTestCaseusage, but does not account for REST Framework'sAPIClientbehavior.Likely an incorrect or invalid review comment.
tests/openwisp2/sample_connection/migrations/0001_initial.py (3)
1-31: LGTM on migration setup and dependencies.The migration correctly declares dependencies using swapper for flexible model resolution, ensuring proper migration ordering across swappable apps.
246-256: Django version-conditional choices pattern is correct.The version check
django.VERSION < (5, 0)properly handles Django 5.0+'s preference for callable choices viaget_command_choicesversus the staticCOMMAND_CHOICEStuple for earlier versions.
206-291: Command model configuration looks good.The Command model correctly uses swapper for both the
connectionForeignKey and theswappablesetting, enabling proper model substitution in extended apps.tests/openwisp2/sample_connection/api/views.py (2)
21-50: LGTM on view class structure.The pass-through subclasses and
as_view()instantiation follow the standard pattern for Django REST framework view extension in sample/test apps. This allows customization points while inheriting all base functionality.
16-18: Inherited typo from upstream base class.
DeviceConnenctionListCreateViewcontains a typo ("nnection" instead of "nection") from the upstreamopenwisp_controller.connection.api.viewsmodule. This sample app correctly mirrors the upstream class name, and the variabledeviceconnection_list_create_viewuses the correct spelling.Consider filing an issue or fixing the typo upstream in
openwisp_controllerto maintain naming consistency across the codebase.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @tests/openwisp2/sample_connection/tests.py:
- Around line 68-81: TestConnectionApi is missing the class attribute app_label
= "sample_connection" which other tests in this file set; add that attribute to
the TestConnectionApi class definition (class
TestConnectionApi(BaseTestConnectionApi)) so it matches the other tests, and
re-run tests to ensure the hardcoded with self.assertNumQueries(13): still
reflects expected DB queries—update or remove the assertNumQueries invocation in
TestConnectionApi if the query count changes.
🧹 Nitpick comments (1)
tests/openwisp2/urls.py (1)
10-15: Unconditional imports used only in conditional block.The imports for
get_connection_api_urlsandconnection_api_viewson lines 10-15 are always executed, but only used whenSAMPLE_APPis set. This is fine since this is a test module andsample_connectionalways exists in the test suite, but consider moving these imports inside the conditional block for clarity and slightly faster startup whenSAMPLE_APPis not set.♻️ Suggested refactor
-from openwisp_controller.connection.api.urls import ( - get_api_urls as get_connection_api_urls, -) from openwisp_users.api.urls import get_api_urls -from .sample_connection.api import views as connection_api_views - redirect_view = RedirectView.as_view(url=reverse_lazy("admin:index")) urlpatterns = [] if os.environ.get("SAMPLE_APP", False): + from openwisp_controller.connection.api.urls import ( + get_api_urls as get_connection_api_urls, + ) + + from .sample_connection.api import views as connection_api_views + urlpatterns += [Also applies to: 21-37
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
tests/openwisp2/sample_connection/tests.pytests/openwisp2/urls.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/openwisp2/sample_connection/tests.py (2)
openwisp_firmware_upgrader/swapper.py (1)
load_model(7-8)openwisp_firmware_upgrader/api/views.py (1)
post(85-92)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=5.2.0
🔇 Additional comments (4)
tests/openwisp2/urls.py (1)
39-48: LGTM!The URL pattern structure is correct. The conditional sample app patterns are prepended, followed by the common admin, redirect, and API routes. The use of
+=to extendurlpatternsis appropriate.tests/openwisp2/sample_connection/tests.py (3)
30-52: LGTM!The test class structure properly extends base test classes with appropriate
app_labelandconfig_app_labelattributes for the sample_connection app context. The pattern of only overriding necessary attributes is clean.
84-92: Good practice: cleaning up the namespace.Deleting the base test classes from the module namespace prevents pytest/unittest from discovering and running them as separate test cases in this module.
55-55: I cannot provide a rewritten review comment because no review comment content was provided in the<review_comment>tags. Please provide:
- The review comment you want me to rewrite
- Any relevant code context or repository information needed for verification
Once you provide these details, I will rewrite the review comment in the required format and output only the rewritten comment with its classification tag between the
<!-- result-start -->and<!-- result-end -->markers.
…t_one # Conflicts: # openwisp_firmware_upgrader/tests/test_admin.py # openwisp_firmware_upgrader/tests/test_selenium.py
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
openwisp_firmware_upgrader/tests/test_admin.py (1)
659-660:⚠️ Potential issue | 🟠 MajorPatch target should resolve from the swapped
DeviceConnectionmodel, not a fixed module path.Using a literal default-model path can miss the effective model in extended/swap scenarios and let real
connect()behavior leak into tests.Proposed fix
- _mock_connect = "openwisp_controller.connection.models.DeviceConnection.connect" + _mock_connect = ( + f"{DeviceConnection.__module__}.{DeviceConnection.__name__}.connect" + )#!/bin/bash set -euo pipefail # Verify which DeviceConnection model is active in tests and whether connect() is overridden. rg -n "swapper.load_model\\(\"connection\", \"DeviceConnection\"\\)|CONNECTION_.*MODEL|SWAPPABLE" tests openwisp_firmware_upgrader -g "*.py" -C2 # Inspect sample/extended connection model definitions. fd "models.py" tests/openwisp2 | xargs -I{} sh -c 'echo "## {}"; rg -n "class .*DeviceConnection|def connect\\(" "{}" -C2' # Locate hardcoded connect patch targets in tests. rg -n "openwisp_controller\\.connection\\.models\\.DeviceConnection\\.connect|_mock_connect" openwisp_firmware_upgrader/tests -C1Also applies to: 664-665
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openwisp_firmware_upgrader/tests/test_admin.py` around lines 659 - 660, The test currently patches a hardcoded "openwisp_controller.connection.models.DeviceConnection.connect" via _mock_connect which breaks when the DeviceConnection model is swapped; instead, import swapper (or Django's apps.get_model) in the test and resolve the active model with swapper.load_model("connection", "DeviceConnection"), then build the patch target from the resolved model's __module__ and __name__ and patch "<resolved_module>.<resolved_class>.connect" (use that dynamic target everywhere _mock_connect is used, e.g., replace references to _mock_connect with the resolved target string).openwisp_firmware_upgrader/tests/test_selenium.py (1)
44-44:⚠️ Potential issue | 🟠 MajorUse a swapped-model-aware
_mock_connecttarget in this class too.The fixed default-model path is brittle under connection-app extension/swap; resolve from the loaded
DeviceConnectionclass.Proposed fix
- _mock_connect = "openwisp_controller.connection.models.DeviceConnection.connect" + _mock_connect = ( + f"{DeviceConnection.__module__}.{DeviceConnection.__name__}.connect" + )Also applies to: 130-130, 209-210, 288-289
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openwisp_firmware_upgrader/tests/test_selenium.py` at line 44, _the _mock_connect target currently uses a hard-coded model path which breaks when the connection app/model is swapped; replace the fixed string with a dynamic target built from the loaded DeviceConnection class (e.g. construct the target as f"{DeviceConnection.__module__}.{DeviceConnection.__name__}.connect" after importing or resolving DeviceConnection) and use that value for _mock_connect in this test class; apply the same change to the other occurrences of _mock_connect in this file (the other two places referenced) so the mock respects swapped models.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@openwisp_firmware_upgrader/tests/test_admin.py`:
- Line 55: Replace the hard-coded connection_app_label = "connection" with a
dynamic lookup from the model meta (e.g., connection_app_label =
Build._meta.app_label or Device._meta.app_label depending on which model the
test uses); import the referenced model (Build or Device) at the top of the test
module if not already imported and use its _meta.app_label to construct admin
URL namespaces so swapped models remain correct.
---
Duplicate comments:
In `@openwisp_firmware_upgrader/tests/test_admin.py`:
- Around line 659-660: The test currently patches a hardcoded
"openwisp_controller.connection.models.DeviceConnection.connect" via
_mock_connect which breaks when the DeviceConnection model is swapped; instead,
import swapper (or Django's apps.get_model) in the test and resolve the active
model with swapper.load_model("connection", "DeviceConnection"), then build the
patch target from the resolved model's __module__ and __name__ and patch
"<resolved_module>.<resolved_class>.connect" (use that dynamic target everywhere
_mock_connect is used, e.g., replace references to _mock_connect with the
resolved target string).
In `@openwisp_firmware_upgrader/tests/test_selenium.py`:
- Line 44: _the _mock_connect target currently uses a hard-coded model path
which breaks when the connection app/model is swapped; replace the fixed string
with a dynamic target built from the loaded DeviceConnection class (e.g.
construct the target as
f"{DeviceConnection.__module__}.{DeviceConnection.__name__}.connect" after
importing or resolving DeviceConnection) and use that value for _mock_connect in
this test class; apply the same change to the other occurrences of _mock_connect
in this file (the other two places referenced) so the mock respects swapped
models.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 25bb800c-3349-4bd5-9826-59695cc40f29
📒 Files selected for processing (4)
openwisp_firmware_upgrader/tests/test_admin.pyopenwisp_firmware_upgrader/tests/test_models.pyopenwisp_firmware_upgrader/tests/test_selenium.pytests/openwisp2/settings.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.13 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=5.1.0
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 383
File: openwisp_firmware_upgrader/tests/test_admin.py:50-50
Timestamp: 2026-02-27T19:09:01.705Z
Learning: In openwisp-firmware-upgrader tests, the BaseTestAdmin class should derive app_label from Build._meta.app_label and config_app_label from Device._meta.app_label instead of hard-coding them. This ensures tests work correctly when models are swapped.
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: tests/openwisp2/sample_firmware_upgrader/migrations/0001_initial.py:61-67
Timestamp: 2026-02-23T21:36:30.581Z
Learning: In the openwisp/openwisp-firmware-upgrader repository, the sample app (tests/openwisp2/sample_firmware_upgrader) can have a different migration path from the main app. New migration files are generally not created for small changes in the sample app since it's only used for demonstration and testing purposes. The sample app's initial migration may include fields or choices that are added incrementally in the main app's migration history.
Learnt from: pandafy
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: docs/developer/installation.rst:123-130
Timestamp: 2026-01-29T18:09:27.769Z
Learning: In openwisp-firmware-upgrader, the `./runtests` script is a wrapper that runs tests for both openwisp_firmware_upgrader and SAMPLE_APP with coverage. To run only the sample app tests, developers should use `SAMPLE_APP=1 ./runtests.py --keepdb --failfast` directly, not the `./runtests` wrapper.
📚 Learning: 2026-02-27T19:08:56.218Z
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 383
File: openwisp_firmware_upgrader/tests/test_admin.py:50-50
Timestamp: 2026-02-27T19:08:56.218Z
Learning: In tests for openwisp-firmware-upgrader, derive app_label values from model meta instead of hard-coding. Specifically, let BaseTestAdmin compute app_label from Build._meta.app_label and config_app_label from Device._meta.app_label to ensure tests remain correct if models are swapped. This pattern should apply to all test files under openwisp_firmware_upgrader/tests.
Applied to files:
openwisp_firmware_upgrader/tests/test_selenium.pyopenwisp_firmware_upgrader/tests/test_models.pyopenwisp_firmware_upgrader/tests/test_admin.py
📚 Learning: 2026-02-25T11:20:13.903Z
Learnt from: pandafy
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: runtests.py:27-38
Timestamp: 2026-02-25T11:20:13.903Z
Learning: In openwisp-firmware-upgrader, runtests.py now uses `execute_from_command_line()` to run Django tests in-process rather than via subprocess, so the `patch = subprocess` coverage directive is not needed in pyproject.toml.
Applied to files:
openwisp_firmware_upgrader/tests/test_selenium.pyopenwisp_firmware_upgrader/tests/test_models.pyopenwisp_firmware_upgrader/tests/test_admin.py
📚 Learning: 2026-01-29T18:09:27.769Z
Learnt from: pandafy
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: docs/developer/installation.rst:123-130
Timestamp: 2026-01-29T18:09:27.769Z
Learning: In openwisp-firmware-upgrader, the `./runtests` script is a wrapper that runs tests for both openwisp_firmware_upgrader and SAMPLE_APP with coverage. To run only the sample app tests, developers should use `SAMPLE_APP=1 ./runtests.py --keepdb --failfast` directly, not the `./runtests` wrapper.
Applied to files:
openwisp_firmware_upgrader/tests/test_selenium.pytests/openwisp2/settings.py
📚 Learning: 2026-02-23T21:36:30.581Z
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: tests/openwisp2/sample_firmware_upgrader/migrations/0001_initial.py:61-67
Timestamp: 2026-02-23T21:36:30.581Z
Learning: In the openwisp/openwisp-firmware-upgrader repository, the sample app (tests/openwisp2/sample_firmware_upgrader) can have a different migration path from the main app. New migration files are generally not created for small changes in the sample app since it's only used for demonstration and testing purposes. The sample app's initial migration may include fields or choices that are added incrementally in the main app's migration history.
Applied to files:
openwisp_firmware_upgrader/tests/test_selenium.pyopenwisp_firmware_upgrader/tests/test_admin.pytests/openwisp2/settings.py
📚 Learning: 2026-02-23T21:44:41.470Z
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: openwisp_firmware_upgrader/websockets.py:233-246
Timestamp: 2026-02-23T21:44:41.470Z
Learning: In openwisp-firmware-upgrader, the `upgrade_operations` attribute on `BatchUpgradeOperation` is a cached property that returns a queryset, so it should be accessed as `batch_operation.upgrade_operations.all()` rather than being called as a method.
Applied to files:
openwisp_firmware_upgrader/tests/test_selenium.pyopenwisp_firmware_upgrader/tests/test_models.pyopenwisp_firmware_upgrader/tests/test_admin.py
📚 Learning: 2026-02-25T00:17:23.479Z
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: openwisp_firmware_upgrader/static/firmware-upgrader/js/upgrade-progress.js:458-463
Timestamp: 2026-02-25T00:17:23.479Z
Learning: In the openwisp/openwisp-firmware-upgrader repository, focus review comments on real, actionable issues rather than theoretical edge cases, trivialities, or potential future problems. Avoid flagging issues that are functionally correct but could theoretically become problems only if the code were significantly refactored or used in different contexts.
Applied to files:
openwisp_firmware_upgrader/tests/test_admin.py
🔇 Additional comments (3)
tests/openwisp2/settings.py (1)
242-254: LGTM!The sample_connection app extension follows the established pattern used for sample_firmware_upgrader. The implementation correctly:
- Swaps
openwisp_controller.connectionwithopenwisp2.sample_connectioninINSTALLED_APPS- Adds the original app to
EXTENDED_APPS- Configures the swapper model settings to point to the sample_connection models
The model settings (
CONNECTION_CREDENTIALS_MODEL,CONNECTION_DEVICECONNECTION_MODEL,CONNECTION_COMMAND_MODEL) correctly reference the models defined intests/openwisp2/sample_connection/models.py.openwisp_firmware_upgrader/tests/test_models.py (1)
548-562: Good mock scoping improvement.Switching to per-test
with mock.patch(...)blocks improves isolation and avoids patch bleed between transaction tests.Also applies to: 566-587, 590-610, 622-643
openwisp_firmware_upgrader/tests/test_selenium.py (1)
113-117: Dynamic changelist URL assertion is a solid fix.Building the expected URL with
self.live_server_url + reverse(...)makes the redirect assertion environment-safe.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
openwisp_firmware_upgrader/tests/test_admin.py (2)
659-663:⚠️ Potential issue | 🟠 MajorPatch the swapped
DeviceConnectionclass, not a hard-coded module path.Line 659 hard-codes
openwisp_controller...DeviceConnection.connect, but this module already resolvesDeviceConnectionviaswapper(Line 45). In extended-app mode, this can patch the wrong class method.Proposed fix
- _mock_connect = "openwisp_controller.connection.models.DeviceConnection.connect" + _mock_connect = f"{DeviceConnection.__module__}.DeviceConnection.connect"#!/bin/bash set -euo pipefail # Verify swapped DeviceConnection usage in this test module. rg -n 'swapper\.load_model\("connection",\s*"DeviceConnection"\)' openwisp_firmware_upgrader/tests/test_admin.py -C2 # Verify test settings/model wiring for sample_connection or connection model swaps. fd -t f "settings.py" tests | while read -r f; do rg -n 'CONNECTION_.*DeviceConnection|sample_connection|swapper' "$f" || true done # Inspect DeviceConnection class/connect definitions in test apps. fd -t f "models.py" tests | while read -r f; do rg -n 'class\s+DeviceConnection\b|def\s+connect\s*\(' "$f" -C2 || true done🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openwisp_firmware_upgrader/tests/test_admin.py` around lines 659 - 663, The test currently hard-codes _mock_connect to "openwisp_controller.connection.models.DeviceConnection.connect", which can patch the wrong class when swaps are used; change the test to patch the actual swapped class instead: obtain the DeviceConnection class via swapper.load_model("connection", "DeviceConnection") (or call swapper.load_model once and reuse it) and then patch its connect method (e.g., patch the attribute on the returned class or construct the module-qualified path from its __module__ and __name__). Update references in test_upgrade_selected_action_perms and the _mock_connect setup to use the swapped class rather than the static string.
863-866: 🛠️ Refactor suggestion | 🟠 MajorAvoid calling one test method from another test method.
Line 865 (
self.test_upgrade_all()) couples tests and nests decorators/patches, which makes failures harder to localize. Extract shared setup/execution into a helper and call that helper from both tests.Refactor direction
+ def _run_upgrade_all_flow(self): + # shared flow currently inside test_upgrade_all + ... `@mock.patch`(_mock_upgrade, return_value=True) def test_upgrade_all(self, *args): - with mock.patch(self._mock_connect, return_value=True): - ... + self._run_upgrade_all_flow() `@mock.patch`(_mock_upgrade, return_value=True) def test_massive_upgrade_operation_page(self, *args): with mock.patch(self._mock_connect, return_value=True): - self.test_upgrade_all() + self._run_upgrade_all_flow() uo = UpgradeOperation.objects.first()#!/bin/bash set -euo pipefail # Verify test-to-test invocations in this module. rg -nP 'self\.test_[A-Za-z0-9_]+\(' openwisp_firmware_upgrader/tests/test_admin.py -C2🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openwisp_firmware_upgrader/tests/test_admin.py` around lines 863 - 866, The test calls another test method (self.test_upgrade_all())—remove this coupling by extracting the common setup/execution code from test_upgrade_all into a shared helper (e.g., a private method like _prepare_and_run_upgrade or _create_upgrade_operations) and have both test_upgrade_all and test_massive_upgrade_operation_page call that helper instead; ensure any mocks/patches (like the one using self._mock_connect) are applied in the individual tests or passed into the helper as parameters so you do not nest decorators/patches when invoking the shared logic, then replace the direct self.test_upgrade_all() call with a call to the new helper and use UpgradeOperation.objects.first() as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@openwisp_firmware_upgrader/tests/test_admin.py`:
- Around line 659-663: The test currently hard-codes _mock_connect to
"openwisp_controller.connection.models.DeviceConnection.connect", which can
patch the wrong class when swaps are used; change the test to patch the actual
swapped class instead: obtain the DeviceConnection class via
swapper.load_model("connection", "DeviceConnection") (or call swapper.load_model
once and reuse it) and then patch its connect method (e.g., patch the attribute
on the returned class or construct the module-qualified path from its __module__
and __name__). Update references in test_upgrade_selected_action_perms and the
_mock_connect setup to use the swapped class rather than the static string.
- Around line 863-866: The test calls another test method
(self.test_upgrade_all())—remove this coupling by extracting the common
setup/execution code from test_upgrade_all into a shared helper (e.g., a private
method like _prepare_and_run_upgrade or _create_upgrade_operations) and have
both test_upgrade_all and test_massive_upgrade_operation_page call that helper
instead; ensure any mocks/patches (like the one using self._mock_connect) are
applied in the individual tests or passed into the helper as parameters so you
do not nest decorators/patches when invoking the shared logic, then replace the
direct self.test_upgrade_all() call with a call to the new helper and use
UpgradeOperation.objects.first() as before.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: eab7bd0d-a963-42fb-8cd0-2dbbf2bbb279
📒 Files selected for processing (1)
openwisp_firmware_upgrader/tests/test_admin.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: tests/openwisp2/sample_firmware_upgrader/migrations/0001_initial.py:61-67
Timestamp: 2026-02-23T21:36:30.581Z
Learning: In the openwisp/openwisp-firmware-upgrader repository, the sample app (tests/openwisp2/sample_firmware_upgrader) can have a different migration path from the main app. New migration files are generally not created for small changes in the sample app since it's only used for demonstration and testing purposes. The sample app's initial migration may include fields or choices that are added incrementally in the main app's migration history.
Learnt from: pandafy
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: docs/developer/installation.rst:123-130
Timestamp: 2026-01-29T18:09:27.769Z
Learning: In openwisp-firmware-upgrader, the `./runtests` script is a wrapper that runs tests for both openwisp_firmware_upgrader and SAMPLE_APP with coverage. To run only the sample app tests, developers should use `SAMPLE_APP=1 ./runtests.py --keepdb --failfast` directly, not the `./runtests` wrapper.
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 383
File: openwisp_firmware_upgrader/tests/test_admin.py:50-50
Timestamp: 2026-02-27T19:09:01.705Z
Learning: In openwisp-firmware-upgrader tests, the BaseTestAdmin class should derive app_label from Build._meta.app_label and config_app_label from Device._meta.app_label instead of hard-coding them. This ensures tests work correctly when models are swapped.
Learnt from: pandafy
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: runtests.py:27-38
Timestamp: 2026-02-25T11:20:13.903Z
Learning: In openwisp-firmware-upgrader, runtests.py now uses `execute_from_command_line()` to run Django tests in-process rather than via subprocess, so the `patch = subprocess` coverage directive is not needed in pyproject.toml.
📚 Learning: 2026-02-27T19:08:56.218Z
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 383
File: openwisp_firmware_upgrader/tests/test_admin.py:50-50
Timestamp: 2026-02-27T19:08:56.218Z
Learning: In tests for openwisp-firmware-upgrader, derive app_label values from model meta instead of hard-coding. Specifically, let BaseTestAdmin compute app_label from Build._meta.app_label and config_app_label from Device._meta.app_label to ensure tests remain correct if models are swapped. This pattern should apply to all test files under openwisp_firmware_upgrader/tests.
Applied to files:
openwisp_firmware_upgrader/tests/test_admin.py
📚 Learning: 2026-02-25T11:20:13.903Z
Learnt from: pandafy
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: runtests.py:27-38
Timestamp: 2026-02-25T11:20:13.903Z
Learning: In openwisp-firmware-upgrader, runtests.py now uses `execute_from_command_line()` to run Django tests in-process rather than via subprocess, so the `patch = subprocess` coverage directive is not needed in pyproject.toml.
Applied to files:
openwisp_firmware_upgrader/tests/test_admin.py
📚 Learning: 2026-02-25T00:17:23.479Z
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: openwisp_firmware_upgrader/static/firmware-upgrader/js/upgrade-progress.js:458-463
Timestamp: 2026-02-25T00:17:23.479Z
Learning: In the openwisp/openwisp-firmware-upgrader repository, focus review comments on real, actionable issues rather than theoretical edge cases, trivialities, or potential future problems. Avoid flagging issues that are functionally correct but could theoretically become problems only if the code were significantly refactored or used in different contexts.
Applied to files:
openwisp_firmware_upgrader/tests/test_admin.py
📚 Learning: 2026-02-23T21:44:41.470Z
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: openwisp_firmware_upgrader/websockets.py:233-246
Timestamp: 2026-02-23T21:44:41.470Z
Learning: In openwisp-firmware-upgrader, the `upgrade_operations` attribute on `BatchUpgradeOperation` is a cached property that returns a queryset, so it should be accessed as `batch_operation.upgrade_operations.all()` rather than being called as a method.
Applied to files:
openwisp_firmware_upgrader/tests/test_admin.py
📚 Learning: 2026-02-23T21:36:30.581Z
Learnt from: nemesifier
Repo: openwisp/openwisp-firmware-upgrader PR: 377
File: tests/openwisp2/sample_firmware_upgrader/migrations/0001_initial.py:61-67
Timestamp: 2026-02-23T21:36:30.581Z
Learning: In the openwisp/openwisp-firmware-upgrader repository, the sample app (tests/openwisp2/sample_firmware_upgrader) can have a different migration path from the main app. New migration files are generally not created for small changes in the sample app since it's only used for demonstration and testing purposes. The sample app's initial migration may include fields or choices that are added incrementally in the main app's migration history.
Applied to files:
openwisp_firmware_upgrader/tests/test_admin.py
🔇 Additional comments (1)
openwisp_firmware_upgrader/tests/test_admin.py (1)
661-694: Per-test mock scoping is a good improvement.Using method-level
@mock.patch(_mock_upgrade, ...)and local connect patch contexts reduces cross-test mock leakage.
|
Switching to draft again, as I need to make it work again with recent changes on master, some tests are failing in non-obvious ways. |
Not sure what you mean here, all instances of |
|
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 and Slow TestsHello @asmodehn, The CI failed due to multiple slow tests. The tests in Action: Specific slow tests include (but are not limited to):
Please address these slow tests to resolve the CI failure. |
084dafa to
1680bbc
Compare
|
The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3). |
nemesifier
left a comment
There was a problem hiding this comment.
Looks good, I have a few doubts touching urls.py, I am not 100% sure but worth double checking (I aslso double checking on my side).
| urlpatterns += [ | ||
| path( | ||
| "", | ||
| include(("openwisp_controller.config.urls", "config"), namespace="config"), |
There was a problem hiding this comment.
isn't this redundant with what we have below?
| urlpatterns += [ | ||
| path("admin/", admin.site.urls), | ||
| path("", redirect_view, name="index"), | ||
| path("", include("openwisp_controller.urls")), |
There was a problem hiding this comment.
Here, doesn't this already include the controller URLs? See https://github.com/openwisp/openwisp-controller/blob/master/openwisp_controller/urls.py
| "api/v1/", | ||
| include( | ||
| ( | ||
| get_connection_api_urls(connection_api_views), |
There was a problem hiding this comment.
I understand you're doing this to supply your own views, which seems correct, but wouldn't this be overridden by path("", include("openwisp_controller.urls")), below?
|
GPT-5.5 assisted review note, focused only on the URL doubts. The URL concerns look valid:
Suggested fix: remove the explicit |
2c92922 to
75c2024
Compare
nemesifier
left a comment
There was a problem hiding this comment.
@asmodehn I pushed some minor fixes, please double check on your end when you can.
|
@coderabbitai resolve |
✅ Action performedComments resolved and changes approved. |
|
@nemesifier LGTM 👍 |
You mean shared logic for these sample apps? If it's exactly identical we can do it easily. |
Checklist
Reference to Existing Issue
This is a part of #303 which was too ambitious. Making a sample_config extend app is trickier than expected, and will be done later.
The goal here is to add a sample_connection extended app, to make sure the case where connection is an extended app is tested.
This happens when someone extends the connection app (to change the connection logic or add complex device command code for instance) and wants to work with it while also using the firmware_upgrader module, which depends on it.
Description of Changes
Added sample_connection extended app.
Based on #303 changes.
Aiming to get all tests to pass with this as a first step.
To achieve this, we need to refactor some mocks in tests to use a contextmanager for the
*TestCase._mock_connectattribute instead of a decorator (parsed at parent-class definition time)