-
-
Notifications
You must be signed in to change notification settings - Fork 84
[fix] Real time updates on visualizer view #257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 7 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
32b5c80
[fix] Real time updates on visualizer view (#250)
Dhanus3133 1839f79
[tests] Add browser test for realtime updates (#251)
Dhanus3133 425b480
[tests] Optimizations for non-parallel tests
nemesifier 6ac4f7e
[tests] Use Chromium instead of firefox
Dhanus3133 35048b2
[tests] Switched channels memory to redis and other fixes
nemesifier f4a4d6a
[chores] Added missing snooze and comment
nemesifier a22fafc
[tests] Exec realtime tests first, increase snooze time
nemesifier 26bb98e
[tests] Add test for non admin visualizer
Dhanus3133 9348856
[chores] Various improvements to be discussed
nemesifier 72cecfa
[chore] Set maxDiff to None
nemesifier 9daa329
[chores] Do not check browser logs in test_non_admin_visualizer
nemesifier 25a6ff4
[chores] Tweaks
nemesifier f8838c9
[chores] Too flaky..
nemesifier 9b4558d
[chores] Ignore library errors
nemesifier 5d6e9a6
Merge branch 'master' into bug/visualizer-view-realtime-updates
Dhanus3133 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| import json | ||
| from copy import copy | ||
| from time import sleep | ||
|
|
||
| import pytest | ||
| from asgiref.sync import sync_to_async | ||
| from channels.db import database_sync_to_async | ||
| from channels.testing import ChannelsLiveServerTestCase, WebsocketCommunicator | ||
| from django.conf import settings | ||
| from django.test import tag | ||
| from django.urls import reverse | ||
| from django.utils.module_loading import import_string | ||
| from selenium.webdriver.common.by import By | ||
| from swapper import load_model | ||
|
|
||
| from openwisp_users.tests.utils import TestOrganizationMixin | ||
| from openwisp_utils.tests import SeleniumTestMixin | ||
|
|
||
| from .utils import CreateGraphObjectsMixin, LoadMixin | ||
|
|
||
| Link = load_model("topology", "Link") | ||
| Node = load_model("topology", "Node") | ||
| Topology = load_model("topology", "Topology") | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| @pytest.mark.django_db(transaction=True) | ||
| @tag("selenium_tests") | ||
| @tag("no_parallel") | ||
| class TestRealTime( | ||
| TestOrganizationMixin, | ||
| CreateGraphObjectsMixin, | ||
| LoadMixin, | ||
| SeleniumTestMixin, | ||
| ChannelsLiveServerTestCase, | ||
| ): | ||
| app_label = "topology" | ||
| prefix = f"admin:{app_label}" | ||
| node_model = Node | ||
| link_model = Link | ||
| topology_model = Topology | ||
| application = import_string(getattr(settings, "ASGI_APPLICATION")) | ||
|
|
||
| def setUp(self): | ||
| org = self._create_org() | ||
| self.admin = self._create_admin( | ||
| username=self.admin_username, password=self.admin_password | ||
| ) | ||
| self.admin_client = self.client | ||
| self.admin_client.force_login(self.admin) | ||
|
|
||
| self.topology = self._create_topology(organization=org) | ||
| self.node1 = self._create_node( | ||
| label="node1", | ||
| addresses=["192.168.0.1"], | ||
| topology=self.topology, | ||
| organization=org, | ||
| ) | ||
| self.node2 = self._create_node( | ||
| label="node2", | ||
| addresses=["192.168.0.2"], | ||
| topology=self.topology, | ||
| organization=org, | ||
| ) | ||
| self.link = self._create_link( | ||
| source=self.node1, | ||
| target=self.node2, | ||
| topology=self.topology, | ||
| status="up", | ||
| ) | ||
|
|
||
| def _snooze(self): | ||
| """Allows a bit of time for the UI to update, reduces flakyness""" | ||
| sleep(0.25) | ||
|
|
||
| async def _get_communicator(self, admin_client, topology_id): | ||
| session_id = admin_client.cookies["sessionid"].value | ||
| communicator = WebsocketCommunicator( | ||
| self.application, | ||
| path=f"ws/network-topology/topology/{topology_id}/", | ||
| headers=[ | ||
| ( | ||
| b"cookie", | ||
| f"sessionid={session_id}".encode("ascii"), | ||
| ) | ||
| ], | ||
| ) | ||
| return communicator | ||
|
|
||
| async def test_real_time_link_status_update(self): | ||
| # preparation | ||
| self.link.status = "down" | ||
| await database_sync_to_async(self.link.save)() | ||
| communicator = await self._get_communicator(self.admin_client, self.topology.pk) | ||
| connected, _ = await communicator.connect() | ||
| assert connected is True | ||
| path = reverse(f"{self.prefix}_topology_change", args=[self.topology.pk]) | ||
| self.login() | ||
| self.open(path) | ||
| self.find_element(By.CSS_SELECTOR, "input.visualizelink").click() | ||
| # changing the status of a link will change it in the browser graph too | ||
| self.link.status = "up" | ||
| await database_sync_to_async(self.link.save)() | ||
| message = await communicator.receive_json_from() | ||
| assert ( | ||
| json.loads(message["topology"])["links"][0]["properties"]["status"] == "up" | ||
| ) | ||
| self._snooze() | ||
| self.assertEqual( | ||
| self.web_driver.execute_script("return graph.data;")["links"][0][ | ||
| "properties" | ||
| ]["status"], | ||
| "up", | ||
| ) | ||
| # test status changing to down | ||
| self.link.status = "down" | ||
| await sync_to_async(self.link.save)() | ||
| message = await communicator.receive_json_from() | ||
| assert ( | ||
| json.loads(message["topology"])["links"][0]["properties"]["status"] | ||
| == "down" | ||
| ) | ||
| self._snooze() | ||
| self.assertEqual( | ||
| self.web_driver.execute_script("return graph.data;")["links"][0][ | ||
| "properties" | ||
| ]["status"], | ||
| "down", | ||
| ) | ||
| await communicator.disconnect() | ||
|
|
||
| async def test_node_status_update(self): | ||
| # preparation | ||
| communicator = await self._get_communicator(self.admin_client, self.topology.pk) | ||
| connected, _ = await communicator.connect() | ||
| assert connected is True | ||
| path = reverse(f"{self.prefix}_topology_change", args=[self.topology.pk]) | ||
| self.login() | ||
| self.open(path) | ||
| self.find_element(By.CSS_SELECTOR, "input.visualizelink").click() | ||
| # saving a new node will add it to the UI | ||
| new_node = copy(self.node1) | ||
| new_node.pk = None | ||
| await database_sync_to_async(new_node.save)() | ||
| message = await communicator.receive_json_from() | ||
| self.assertEqual(len(json.loads(message["topology"])["nodes"]), 3) | ||
| self._snooze() | ||
| self.assertEqual( | ||
| len(self.web_driver.execute_script("return graph.data;")["nodes"]), | ||
| 3, | ||
| ) | ||
| # deleting the node from the DB will remove it from the UI | ||
| await database_sync_to_async(new_node.delete)() | ||
| message = await communicator.receive_json_from() | ||
| self.assertEqual(len(json.loads(message["topology"])["nodes"]), 2) | ||
| self._snooze() | ||
| self.assertEqual( | ||
| len(self.web_driver.execute_script("return graph.data;")["nodes"]), | ||
| 2, | ||
| ) | ||
| await communicator.disconnect() | ||
|
|
||
| async def test_node_link_update(self): | ||
| # preparation | ||
| communicator = await self._get_communicator(self.admin_client, self.topology.pk) | ||
| connected, _ = await communicator.connect() | ||
| assert connected is True | ||
| path = reverse(f"{self.prefix}_topology_change", args=[self.topology.pk]) | ||
| self.login() | ||
| self.open(path) | ||
| self.find_element(By.CSS_SELECTOR, "input.visualizelink").click() | ||
| # deleting the link from the DB will remove it from the UI | ||
| await database_sync_to_async(self.link.delete)() | ||
| message = await communicator.receive_json_from() | ||
| self.assertEqual(len(json.loads(message["topology"])["links"]), 0) | ||
| self._snooze() | ||
| self.assertEqual( | ||
| len(self.web_driver.execute_script("return graph.data;")["links"]), | ||
| 0, | ||
| ) | ||
| # adding a link will add it to the UI | ||
| new_link = copy(self.link) | ||
| new_link.pk = None | ||
| await database_sync_to_async(new_link.save)() | ||
| message = await communicator.receive_json_from() | ||
| self.assertEqual(len(json.loads(message["topology"])["links"]), 1) | ||
| self._snooze() | ||
| self.assertEqual( | ||
| len(self.web_driver.execute_script("return graph.data;")["links"]), | ||
| 1, | ||
| ) | ||
| await communicator.disconnect() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this? This is imported when the class is loaded. This may happen before ready for all app config is executed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It wasn't imported. I tried it.
openwisp-network-topology/openwisp_network_topology/tests/utils.py
Lines 20 to 37 in 20f8800
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What did you try?
My question was regarding
application = import_string(getattr(settings, "ASGI_APPLICATION"))In channels, I see the following code:
https://github.com/django/channels/blob/a1352286ab4aa54ba5de7acdfdce44dcc73fecf5/channels/testing/live.py#L67-L70
https://github.com/django/channels/blob/a1352286ab4aa54ba5de7acdfdce44dcc73fecf5/channels/testing/live.py#L14-L19
https://github.com/django/channels/blob/a1352286ab4aa54ba5de7acdfdce44dcc73fecf5/channels/routing.py#L15-L33
So, the test class will automatically get the application from the Django settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I thought you were talking about models since GitHub highlighted those too. Yeah, it seems like we could have used it as well. I used this as a reference after seeing it in another test:
openwisp-network-topology/openwisp_network_topology/tests/test_websockets.py
Line 28 in 0685cae