Fix tests for project notifications.

This commit is contained in:
grossmj 2019-02-23 23:20:11 +07:00
parent 7fe8f7e716
commit 135d56371d
6 changed files with 79 additions and 85 deletions

View File

@ -110,9 +110,9 @@ class Notification:
self.project_emit("node.updated", node.__json__())
except (aiohttp.web.HTTPNotFound, aiohttp.web.HTTPForbidden): # Project closing
return
# elif action == "ping":
# event["compute_id"] = compute_id
# self.project_emit(action, event)
elif action == "ping":
event["compute_id"] = compute_id
self.project_emit(action, event)
else:
self.project_emit(action, event, project_id)

View File

@ -220,22 +220,20 @@ class ProjectHandler:
async def notification(request, response):
controller = Controller.instance()
project_id = request.match_info["project_id"]
project = controller.get_project(request.match_info["project_id"])
response.content_type = "application/json"
response.set_status(200)
response.enable_chunked_encoding()
await response.prepare(request)
log.info("New client has connected to the notification stream for project ID '{}' (HTTP long-polling method)".format(project_id))
log.info("New client has connected to the notification stream for project ID '{}' (HTTP long-polling method)".format(project.id))
try:
with controller.notification.project_queue(project_id) as queue:
with controller.notification.project_queue(project.id) as queue:
while True:
msg = await queue.get_json(5)
await response.write(("{}\n".format(msg)).encode("utf-8"))
finally:
log.info("Client has disconnected from notification for project ID '{}' (HTTP long-polling method)".format(project_id))
try:
project = controller.get_project(project_id)
log.info("Client has disconnected from notification for project ID '{}' (HTTP long-polling method)".format(project.id))
if project.auto_close:
# To avoid trouble with client connecting disconnecting we sleep few seconds before checking
# if someone else is not connected
@ -243,8 +241,7 @@ class ProjectHandler:
if not controller.notification.project_has_listeners(project.id):
log.info("Project '{}' is automatically closing due to no client listening".format(project.id))
await project.close()
except aiohttp.web.HTTPNotFound:
pass
@Route.get(
r"/projects/{project_id}/notifications/ws",
@ -259,36 +256,32 @@ class ProjectHandler:
async def notification_ws(request, response):
controller = Controller.instance()
project_id = request.match_info["project_id"]
project = controller.get_project(request.match_info["project_id"])
ws = aiohttp.web.WebSocketResponse()
await ws.prepare(request)
request.app['websockets'].add(ws)
asyncio.ensure_future(process_websocket(ws))
log.info("New client has connected to the notification stream for project ID '{}' (WebSocket method)".format(project_id))
log.info("New client has connected to the notification stream for project ID '{}' (WebSocket method)".format(project.id))
try:
with controller.notification.project_queue(project_id) as queue:
with controller.notification.project_queue(project.id) as queue:
while True:
notification = await queue.get_json(5)
if ws.closed:
break
await ws.send_str(notification)
finally:
log.info("Client has disconnected from notification stream for project ID '{}' (WebSocket method)".format(project_id))
log.info("Client has disconnected from notification stream for project ID '{}' (WebSocket method)".format(project.id))
if not ws.closed:
await ws.close()
request.app['websockets'].discard(ws)
try:
project = controller.get_project(project_id)
if project.auto_close:
# To avoid trouble with client connecting disconnecting we sleep few seconds before checking
# if someone else is not connected
await asyncio.sleep(5)
if not controller.notification.project_has_listeners(project_id):
if not controller.notification.project_has_listeners(project.id):
log.info("Project '{}' is automatically closing due to no client listening".format(project.id))
await project.close()
except aiohttp.web.HTTPNotFound:
pass
return ws

View File

@ -68,7 +68,7 @@ def test_add_node(async_run, project, compute):
link = Link(project)
link.create = AsyncioMagicMock()
link._project.controller.notification.project_emit = MagicMock()
link._project.emit_notification = MagicMock()
project.dump = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4))
assert link._nodes == [
@ -87,7 +87,7 @@ def test_add_node(async_run, project, compute):
}
]
assert project.dump.called
assert not link._project.controller.notification.project_emit.called
assert not link._project.emit_notification.called
assert not link.create.called
@ -97,7 +97,7 @@ def test_add_node(async_run, project, compute):
async_run(link.add_node(node2, 0, 4))
assert link.create.called
link._project.controller.notification.project_emit.assert_called_with("link.created", link.__json__())
link._project.emit_notification.assert_called_with("link.created", link.__json__())
assert link in node2.links
@ -112,7 +112,7 @@ def test_add_node_already_connected(async_run, project, compute):
link = Link(project)
link.create = AsyncioMagicMock()
link._project.controller.notification.project_emit = MagicMock()
link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4))
node2 = Node(project, compute, "node2", node_type="qemu")
node2._ports = [EthernetPort("E0", 0, 0, 4)]
@ -133,7 +133,7 @@ def test_add_node_cloud(async_run, project, compute):
link = Link(project)
link.create = AsyncioMagicMock()
link._project.controller.notification.project_emit = MagicMock()
link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4))
async_run(link.add_node(node2, 0, 4))
@ -150,7 +150,7 @@ def test_add_node_cloud_to_cloud(async_run, project, compute):
link = Link(project)
link.create = AsyncioMagicMock()
link._project.controller.notification.project_emit = MagicMock()
link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4))
with pytest.raises(aiohttp.web.HTTPConflict):
@ -166,7 +166,7 @@ def test_add_node_same_node(async_run, project, compute):
link = Link(project)
link.create = AsyncioMagicMock()
link._project.controller.notification.project_emit = MagicMock()
link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4))
with pytest.raises(aiohttp.web.HTTPConflict):
@ -184,7 +184,7 @@ def test_add_node_serial_to_ethernet(async_run, project, compute):
link = Link(project)
link.create = AsyncioMagicMock()
link._project.controller.notification.project_emit = MagicMock()
link._project.emit_notification = MagicMock()
async_run(link.add_node(node1, 0, 4))
with pytest.raises(aiohttp.web.HTTPConflict):
@ -295,25 +295,25 @@ def test_default_capture_file_name(project, compute, async_run):
assert link.default_capture_file_name() == "Hello_0-4_to_w0rld_1-3.pcap"
def test_start_capture(link, async_run, tmpdir, project, controller):
def test_start_capture(link, async_run, tmpdir):
async def fake_reader():
return AsyncioBytesIO()
link.read_pcap_from_source = fake_reader
controller._notification = MagicMock()
link._project.emit_notification = MagicMock()
async_run(link.start_capture(capture_file_name="test.pcap"))
assert link._capturing
assert link._capture_file_name == "test.pcap"
controller._notification.project_emit.assert_called_with("link.updated", link.__json__())
link._project.emit_notification.assert_called_with("link.updated", link.__json__())
def test_stop_capture(link, async_run, tmpdir, project, controller):
def test_stop_capture(link, async_run, tmpdir):
link._capturing = True
controller._notification = MagicMock()
link._project.emit_notification = MagicMock()
async_run(link.stop_capture())
assert link._capturing is False
controller._notification.project_emit.assert_called_with("link.updated", link.__json__())
link._project.emit_notification.assert_called_with("link.updated", link.__json__())
def test_delete(async_run, project, compute):
@ -322,7 +322,7 @@ def test_delete(async_run, project, compute):
link = Link(project)
link.create = AsyncioMagicMock()
link._project.controller.notification.project_emit = MagicMock()
link._project.emit_notification = MagicMock()
project.dump = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4))
@ -342,7 +342,7 @@ def test_update_filters(async_run, project, compute):
link = Link(project)
link.create = AsyncioMagicMock()
link._project.controller.notification.project_emit = MagicMock()
link._project.emit_notification = MagicMock()
project.dump = AsyncioMagicMock()
async_run(link.add_node(node1, 0, 4))

View File

@ -353,23 +353,23 @@ def test_update_properties(node, compute, project, async_run, controller):
#controller._notification.emit.assert_called_with("node.updated", node_notif)
def test_update_only_controller(node, controller, compute, project, async_run):
def test_update_only_controller(node, controller, compute, async_run):
"""
When updating property used only on controller we don't need to
call the compute
"""
compute.put = AsyncioMagicMock()
controller._notification = AsyncioMagicMock()
node._project.emit_notification = AsyncioMagicMock()
async_run(node.update(x=42))
assert not compute.put.called
assert node.x == 42
controller._notification.project_emit.assert_called_with("node.updated", node.__json__())
node._project.emit_notification.assert_called_with("node.updated", node.__json__())
# If nothing change a second notif should not be send
controller._notification = AsyncioMagicMock()
node._project.emit_notification = AsyncioMagicMock()
async_run(node.update(x=42))
assert not controller._notification.project_emit.called
assert not node._project.emit_notification.called
def test_update_no_changes(node, compute, project, async_run):

View File

@ -45,7 +45,7 @@ def test_emit_to_all(async_run, controller, project):
Send an event to all if we don't have a project id in the event
"""
notif = controller.notification
with notif.project_queue(project) as queue:
with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping
notif.project_emit('test', {})
@ -60,7 +60,7 @@ def test_emit_to_project(async_run, controller, project):
Send an event to a project listeners
"""
notif = controller.notification
with notif.project_queue(project) as queue:
with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping
# This event has not listener
@ -74,20 +74,20 @@ def test_emit_to_project(async_run, controller, project):
def test_dispatch(async_run, controller, project):
notif = controller.notification
with notif.project_queue(project) as queue:
with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping
async_run(notif.dispatch("test", {}, compute_id=1))
async_run(notif.dispatch("test", {}, project_id=project.id, compute_id=1))
msg = async_run(queue.get(5))
assert msg == ('test', {}, {})
def test_dispatch_ping(async_run, controller, project):
notif = controller.notification
with notif.project_queue(project) as queue:
with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping
async_run(notif.dispatch("ping", {}, compute_id=12))
async_run(notif.dispatch("ping", {}, project_id=project.id, compute_id=12))
msg = async_run(queue.get(5))
assert msg == ('ping', {'compute_id': 12}, {})
@ -99,7 +99,7 @@ def test_dispatch_node_updated(async_run, controller, node, project):
"""
notif = controller.notification
with notif.project_queue(project) as queue:
with notif.project_queue(project.id) as queue:
assert len(notif._project_listeners[project.id]) == 1
async_run(queue.get(0.1)) # ping
async_run(notif.dispatch("node.updated", {
@ -108,6 +108,7 @@ def test_dispatch_node_updated(async_run, controller, node, project):
"name": "hello",
"startup_config": "ip 192"
},
project_id=project.id,
compute_id=1))
assert node.name == "hello"
action, event, _ = async_run(queue.get(5))

View File

@ -86,11 +86,11 @@ def test_json(tmpdir):
def test_update(controller, async_run):
project = Project(controller=controller, name="Hello")
controller._notification = MagicMock()
project.emit_notification = MagicMock()
assert project.name == "Hello"
async_run(project.update(name="World"))
assert project.name == "World"
controller.notification.project_emit.assert_any_call("project.updated", project.__json__())
project.emit_notification.assert_any_call("project.updated", project.__json__())
def test_update_on_compute(controller, async_run):
@ -99,7 +99,7 @@ def test_update_on_compute(controller, async_run):
compute.id = "local"
project = Project(controller=controller, name="Test")
project._project_created_on_compute = [compute]
controller._notification = MagicMock()
project.emit_notification = MagicMock()
async_run(project.update(variables=variables))
@ -154,7 +154,7 @@ def test_add_node_local(async_run, controller):
compute = MagicMock()
compute.id = "local"
project = Project(controller=controller, name="Test")
controller._notification = MagicMock()
project.emit_notification = MagicMock()
response = MagicMock()
response.json = {"console": 2048}
@ -174,7 +174,7 @@ def test_add_node_local(async_run, controller):
'name': 'test'},
timeout=1200)
assert compute in project._project_created_on_compute
controller.notification.project_emit.assert_any_call("node.created", node.__json__())
project.emit_notification.assert_any_call("node.created", node.__json__())
def test_add_node_non_local(async_run, controller):
@ -184,7 +184,7 @@ def test_add_node_non_local(async_run, controller):
compute = MagicMock()
compute.id = "remote"
project = Project(controller=controller, name="Test")
controller._notification = MagicMock()
project.emit_notification = MagicMock()
response = MagicMock()
response.json = {"console": 2048}
@ -202,7 +202,7 @@ def test_add_node_non_local(async_run, controller):
'name': 'test'},
timeout=1200)
assert compute in project._project_created_on_compute
controller.notification.project_emit.assert_any_call("node.created", node.__json__())
project.emit_notification.assert_any_call("node.created", node.__json__())
def test_add_node_from_template(async_run, controller):
@ -212,7 +212,7 @@ def test_add_node_from_template(async_run, controller):
compute = MagicMock()
compute.id = "local"
project = Project(controller=controller, name="Test")
controller._notification = MagicMock()
project.emit_notification = MagicMock()
template = Template(str(uuid.uuid4()), {
"compute_id": "local",
"name": "Test",
@ -234,7 +234,7 @@ def test_add_node_from_template(async_run, controller):
})
assert compute in project._project_created_on_compute
controller.notification.project_emit.assert_any_call("node.created", node.__json__())
project.emit_notification.assert_any_call("node.created", node.__json__())
def test_delete_node(async_run, controller):
@ -243,7 +243,7 @@ def test_delete_node(async_run, controller):
"""
compute = MagicMock()
project = Project(controller=controller, name="Test")
controller._notification = MagicMock()
project.emit_notification = MagicMock()
response = MagicMock()
response.json = {"console": 2048}
@ -255,7 +255,7 @@ def test_delete_node(async_run, controller):
assert node.id not in project._nodes
compute.delete.assert_any_call('/projects/{}/vpcs/nodes/{}'.format(project.id, node.id))
controller.notification.project_emit.assert_any_call("node.deleted", node.__json__())
project.emit_notification.assert_any_call("node.deleted", node.__json__())
def test_delete_node_delete_link(async_run, controller):
@ -264,7 +264,7 @@ def test_delete_node_delete_link(async_run, controller):
"""
compute = MagicMock()
project = Project(controller=controller, name="Test")
controller._notification = MagicMock()
project.emit_notification = MagicMock()
response = MagicMock()
response.json = {"console": 2048}
@ -280,8 +280,8 @@ def test_delete_node_delete_link(async_run, controller):
assert link.id not in project._links
compute.delete.assert_any_call('/projects/{}/vpcs/nodes/{}'.format(project.id, node.id))
controller.notification.project_emit.assert_any_call("node.deleted", node.__json__())
controller.notification.project_emit.assert_any_call("link.deleted", link.__json__())
project.emit_notification.assert_any_call("node.deleted", node.__json__())
project.emit_notification.assert_any_call("link.deleted", link.__json__())
def test_get_node(async_run, controller):
@ -331,14 +331,14 @@ def test_add_link(async_run, project, controller):
vm1._ports = [EthernetPort("E0", 0, 3, 1)]
vm2 = async_run(project.add_node(compute, "test2", None, node_type="vpcs", properties={"startup_config": "test.cfg"}))
vm2._ports = [EthernetPort("E0", 0, 4, 2)]
controller._notification = MagicMock()
project.emit_notification = MagicMock()
link = async_run(project.add_link())
async_run(link.add_node(vm1, 3, 1))
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock_udp_create:
async_run(link.add_node(vm2, 4, 2))
assert mock_udp_create.called
assert len(link._nodes) == 2
controller.notification.project_emit.assert_any_call("link.created", link.__json__())
project.emit_notification.assert_any_call("link.created", link.__json__())
def test_list_links(async_run, project):
@ -379,18 +379,18 @@ def test_delete_link(async_run, project, controller):
assert len(project._links) == 0
link = async_run(project.add_link())
assert len(project._links) == 1
controller._notification = MagicMock()
project.emit_notification = MagicMock()
async_run(project.delete_link(link.id))
controller.notification.project_emit.assert_any_call("link.deleted", link.__json__())
project.emit_notification.assert_any_call("link.deleted", link.__json__())
assert len(project._links) == 0
def test_add_drawing(async_run, project, controller):
controller.notification.project_emit = MagicMock()
project.emit_notification = MagicMock()
drawing = async_run(project.add_drawing(None, svg="<svg></svg>"))
assert len(project._drawings) == 1
controller.notification.project_emit.assert_any_call("drawing.created", drawing.__json__())
project.emit_notification.assert_any_call("drawing.created", drawing.__json__())
def test_get_drawing(async_run, project):
@ -413,9 +413,9 @@ def test_delete_drawing(async_run, project, controller):
assert len(project._drawings) == 0
drawing = async_run(project.add_drawing())
assert len(project._drawings) == 1
controller._notification = MagicMock()
project.emit_notification = MagicMock()
async_run(project.delete_drawing(drawing.id))
controller.notification.project_emit.assert_any_call("drawing.deleted", drawing.__json__())
project.emit_notification.assert_any_call("drawing.deleted", drawing.__json__())
assert len(project._drawings) == 0
@ -478,10 +478,10 @@ def test_open_close(async_run, controller):
async_run(project.open())
assert not project.start_all.called
assert project.status == "opened"
controller._notification = MagicMock()
project.emit_notification = MagicMock()
async_run(project.close())
assert project.status == "closed"
controller.notification.project_emit.assert_any_call("project.closed", project.__json__())
project.emit_notification.assert_any_call("project.closed", project.__json__())
def test_open_auto_start(async_run, controller):