Coverage for src/meshadmin/server/networks/tests/test_views.py: 100%
368 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-10 16:08 +0200
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-10 16:08 +0200
1from datetime import timedelta
3import pytest
4from django.urls import reverse
5from django.utils import timezone
7from meshadmin.common.utils import create_keys
8from meshadmin.server.networks.models import (
9 CA,
10 ConfigRollout,
11 Group,
12 Host,
13 HostConfig,
14 Network,
15 Rule,
16 Template,
17)
18from meshadmin.server.networks.services import create_group, create_template
21class TestRolloutViews:
22 def test_rollout_creation_with_hosts(self, auth_client, test_network):
23 client, user = auth_client()
24 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
25 hosts = []
26 for i in range(3):
27 host = Host.objects.create(
28 network=test_net,
29 name=f"test-host-{i}",
30 assigned_ip=f"100.100.64.{i + 1}",
31 )
32 hosts.append(host)
34 data = {
35 "name": "new-rollout",
36 "notes": "Test notes",
37 "hosts": [host.id for host in hosts[:2]],
38 }
40 response = client.post(
41 reverse(
42 "networks:network-rollout-create", kwargs={"network_id": test_net.id}
43 ),
44 data,
45 )
47 assert response.status_code == 302
48 rollout = ConfigRollout.objects.get(name="new-rollout")
49 assert rollout.network == test_net
50 assert rollout.notes == "Test notes"
51 assert rollout.status == "PENDING"
52 assert list(rollout.target_hosts.all()) == hosts[:2]
53 for host in hosts[:2]:
54 host.refresh_from_db()
55 assert host.config_freeze is True
57 def test_rollout_unfreeze(self, auth_client, test_network):
58 client, user = auth_client()
59 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
60 hosts = []
61 for i in range(2):
62 host = Host.objects.create(
63 network=test_net,
64 name=f"test-host-{i}",
65 assigned_ip=f"100.100.64.{i + 1}",
66 config_freeze=True,
67 )
68 hosts.append(host)
70 rollout = ConfigRollout.objects.create(
71 name="test-rollout",
72 network=test_net,
73 status="PENDING",
74 )
75 rollout.target_hosts.add(*hosts)
77 # Test single host unfreeze
78 response = client.post(
79 reverse("networks:rollout-unfreeze", kwargs={"pk": rollout.pk}),
80 {"host_id": hosts[0].id},
81 )
83 assert response.status_code == 302
84 rollout.refresh_from_db()
85 assert rollout.status == "PENDING"
86 assert list(rollout.completed_hosts.all()) == [hosts[0]]
87 hosts[0].refresh_from_db()
88 assert hosts[0].config_freeze is False
89 hosts[1].refresh_from_db()
90 assert hosts[1].config_freeze is True
92 # Test complete rollout unfreeze
93 response = client.post(
94 reverse("networks:rollout-unfreeze", kwargs={"pk": rollout.pk}),
95 )
96 assert response.status_code == 302
97 rollout.refresh_from_db()
98 assert rollout.status == "COMPLETED"
99 assert set(rollout.completed_hosts.all()) == set(hosts)
100 for host in hosts:
101 host.refresh_from_db()
102 assert host.config_freeze is False
104 def test_rollout_update(self, auth_client, test_network):
105 client, user = auth_client()
106 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
107 hosts = []
108 for i in range(3):
109 host = Host.objects.create(
110 network=test_net,
111 name=f"test-host-{i}",
112 assigned_ip=f"100.100.64.{i + 1}",
113 )
114 hosts.append(host)
116 rollout = ConfigRollout.objects.create(
117 name="test-rollout",
118 network=test_net,
119 status="PENDING",
120 )
121 rollout.target_hosts.add(*hosts[:2])
123 data = {
124 "name": "updated-rollout",
125 "notes": "Updated notes",
126 "hosts": [hosts[0].id, hosts[2].id],
127 }
129 response = client.post(
130 reverse("networks:rollout-edit", kwargs={"pk": rollout.pk}),
131 data,
132 )
134 assert response.status_code == 302
135 rollout.refresh_from_db()
136 assert rollout.name == "updated-rollout"
137 assert rollout.notes == "Updated notes"
138 assert list(rollout.target_hosts.all()) == [hosts[0], hosts[2]]
139 hosts[0].refresh_from_db()
140 hosts[1].refresh_from_db()
141 hosts[2].refresh_from_db()
142 assert hosts[0].config_freeze is True
143 assert hosts[1].config_freeze is False
144 assert hosts[2].config_freeze is True
146 def test_rollout_delete(self, auth_client, test_network):
147 client, user = auth_client()
148 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
149 hosts = []
150 for i in range(2):
151 host = Host.objects.create(
152 network=test_net,
153 name=f"test-host-{i}",
154 assigned_ip=f"100.100.64.{i + 1}",
155 config_freeze=True,
156 )
157 hosts.append(host)
159 rollout = ConfigRollout.objects.create(
160 name="test-rollout",
161 network=test_net,
162 status="PENDING",
163 )
164 rollout.target_hosts.add(*hosts)
166 response = client.post(
167 reverse("networks:rollout-delete", kwargs={"pk": rollout.pk}),
168 )
170 assert response.status_code == 302
171 assert not ConfigRollout.objects.filter(pk=rollout.pk).exists()
172 for host in hosts:
173 host.refresh_from_db()
174 assert host.config_freeze is False
177class TestHostViews:
178 def test_host_refresh_config_with_rollout(self, auth_client, test_network):
179 client, user = auth_client()
180 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
181 _, public_key = create_keys()
182 host = Host.objects.create(
183 network=test_net,
184 name="test-host",
185 assigned_ip="100.100.64.1",
186 config_freeze=True,
187 public_key=public_key,
188 interface="nebula1",
189 )
190 rollout = ConfigRollout.objects.create(
191 name="test-rollout",
192 network=test_net,
193 status="PENDING",
194 )
195 rollout.target_hosts.add(host)
196 response = client.post(
197 reverse(
198 "networks:host-refresh-config",
199 kwargs={"pk": host.pk, "rollout_id": rollout.pk},
200 ),
201 )
202 assert response.status_code == 302
203 assert response.url == reverse(
204 "networks:rollout-detail", kwargs={"pk": rollout.pk}
205 )
206 host.refresh_from_db()
207 assert host.hostconfig_set.count() > 0
208 latest_config = host.hostconfig_set.latest("created_at")
209 assert "pki" in latest_config.config
210 assert "lighthouse" in latest_config.config
212 def test_config_diff_view(self, auth_client, test_network):
213 client, user = auth_client()
214 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
215 host = Host.objects.create(name="test_host", network=test_net)
217 # Create two configs with different content
218 config1 = HostConfig.objects.create(
219 host=host, config="test: config1\nline: 1", sha256="abc123"
220 )
221 config2 = HostConfig.objects.create(
222 host=host, config="test: config2\nline: 2", sha256="def456"
223 )
225 # Test successful diff
226 response = client.get(
227 reverse(
228 "networks:config-diff",
229 kwargs={"base_id": config1.id, "compare_id": config2.id},
230 )
231 )
232 assert response.status_code == 200
233 assert b"-test: config1" in response.content
234 assert b"+test: config2" in response.content
235 assert b"-line: 1" in response.content
236 assert b"+line: 2" in response.content
238 # Test no changes between identical configs
239 config3 = HostConfig.objects.create(
240 host=host, config="test: config1\nline: 1", sha256="abc123"
241 )
242 response = client.get(
243 reverse(
244 "networks:config-diff",
245 kwargs={"base_id": config1.id, "compare_id": config3.id},
246 )
247 )
248 assert response.status_code == 200
249 assert b"No differences found" in response.content
251 # Test non-existent config
252 response = client.get(
253 reverse(
254 "networks:config-diff",
255 kwargs={"base_id": 99999, "compare_id": config2.id},
256 )
257 )
258 assert response.status_code == 200
259 assert b"Error" in response.content
261 def test_make_signing_ca(self, auth_client, test_network):
262 client, user = auth_client()
263 network = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
264 new_ca = CA.objects.create(
265 network=network,
266 name="new_signing_ca",
267 key="test_key",
268 cert="test_cert",
269 )
270 original_signing_ca = network.signingca.ca
271 response = client.post(
272 reverse("networks:ca-make-signing", kwargs={"pk": new_ca.pk}),
273 )
274 assert response.status_code == 200
275 network.refresh_from_db()
276 assert network.signingca.ca == new_ca
277 assert network.signingca.ca != original_signing_ca
280class TestCRUDWithParentNetwork:
281 @pytest.mark.parametrize(
282 "url_name,data,expected_model,expected_fields",
283 [
284 (
285 "networks:network-ca-create",
286 {"name": "test_ca"},
287 CA,
288 {"name": "test_ca", "cert__isnull": False, "key__isnull": False},
289 ),
290 (
291 "networks:network-group-create",
292 {"name": "test_group"},
293 Group,
294 {"name": "test_group"},
295 ),
296 ],
297 )
298 def test_entity_creation_with_parent_network(
299 self,
300 auth_client,
301 test_network,
302 url_name,
303 data,
304 expected_model,
305 expected_fields,
306 ):
307 client, user = auth_client()
308 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
309 data["network"] = test_net.id
311 response = client.post(
312 reverse(url_name, kwargs={"network_id": test_net.id}),
313 data,
314 )
315 assert response.status_code == 302
316 filters = {**expected_fields, "network": test_net}
317 assert expected_model.objects.filter(**filters).exists()
319 @pytest.mark.parametrize(
320 "url_name,model,update_data",
321 [
322 ("networks:ca-edit", CA, {"name": "updated_name"}),
323 ("networks:group-edit", Group, {"name": "updated_name"}),
324 ],
325 )
326 def test_entity_update_with_parent_network(
327 self, auth_client, test_network, url_name, model, update_data
328 ):
329 client, user = auth_client()
330 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
332 obj = model.objects.create(
333 network=test_net, name=f"test_{model._meta.model_name}"
334 )
335 update_data["network"] = test_net.id
337 response = client.post(
338 reverse(url_name, kwargs={"pk": obj.id}),
339 update_data,
340 )
341 assert response.status_code == 302
342 obj.refresh_from_db()
343 assert obj.name == update_data["name"]
345 @pytest.mark.parametrize(
346 "url_name,model",
347 [
348 ("networks:ca-delete", CA),
349 ("networks:group-delete", Group),
350 ],
351 )
352 def test_entity_deletion_with_parent_network(
353 self, auth_client, test_network, url_name, model
354 ):
355 client, user = auth_client()
356 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
357 obj = model.objects.create(
358 network=test_net,
359 name=f"test_{model._meta.model_name}",
360 )
361 response = client.post(
362 reverse(url_name, kwargs={"pk": obj.id}),
363 )
364 assert response.status_code == 302
365 assert not model.objects.filter(id=obj.id).exists()
368class TestRuleViews:
369 def test_add_rule_to_group_success(self, auth_client, test_network):
370 client, user = auth_client()
371 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
373 # First create a security group
374 group_data = {"name": "test_group", "description": "Test security group"}
375 response = client.post(
376 reverse(
377 "networks:network-group-create", kwargs={"network_id": test_net.id}
378 ),
379 group_data,
380 )
381 assert response.status_code == 302
382 security_group = Group.objects.get(name="test_group")
384 # create target groups
385 target_group1 = create_group(test_net.pk, "target_group1")
386 target_group2 = create_group(test_net.pk, "target_group2")
387 target_group3 = create_group(test_net.pk, "target_group3")
389 # add a rule to the group
390 rule_data = {
391 "security_group": security_group.id,
392 "direction": "I",
393 "proto": "tcp",
394 "port": "80",
395 "cidr": "0.0.0.0/0",
396 "group": target_group1.id,
397 "groups": [target_group2.id, target_group3.id],
398 "local_cidr": "192.168.1.0/24",
399 }
401 response = client.post(
402 reverse("networks:group-add-rule"),
403 rule_data,
404 )
406 assert response.status_code == 200
407 assert Rule.objects.filter(security_group=security_group).exists()
408 rule = Rule.objects.get(security_group=security_group)
409 assert rule.direction == "I"
410 assert rule.proto == "tcp"
411 assert rule.port == "80"
412 assert rule.cidr == "0.0.0.0/0"
413 assert rule.group == target_group1
414 assert set(rule.groups.all()) == {target_group2, target_group3}
415 assert rule.local_cidr == "192.168.1.0/24"
417 def test_add_rule_validation_no_target(self, auth_client, test_network):
418 client, user = auth_client()
419 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
420 group = create_group(test_net.pk, "test_group")
421 rule_data = {
422 "security_group": group.id,
423 "direction": "I",
424 "proto": "tcp",
425 "port": "80",
426 }
427 response = client.post(
428 reverse("networks:group-add-rule"),
429 rule_data,
430 )
431 assert response.status_code == 200
432 assert "At least one of group, groups, or CIDR" in response.content.decode()
433 assert not Rule.objects.filter(security_group=group).exists()
435 def test_add_rule_validation_invalid_port(self, auth_client, test_network):
436 client, user = auth_client()
437 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
438 group = create_group(test_net.pk, "test_group")
439 rule_data = {
440 "security_group": group.id,
441 "direction": "I",
442 "proto": "tcp",
443 "port": "80-invalid",
444 "cidr": "0.0.0.0/0",
445 }
447 response = client.post(
448 reverse("networks:group-add-rule"),
449 rule_data,
450 )
452 assert response.status_code == 200
453 assert "Port range must be two" in response.content.decode()
454 assert not Rule.objects.filter(security_group=group).exists()
456 # Port out of range
457 rule_data["port"] = "70000"
458 response = client.post(
459 reverse("networks:group-add-rule"),
460 rule_data,
461 )
462 assert response.status_code == 200
463 assert "Port must be" in response.content.decode()
464 assert not Rule.objects.filter(security_group=group).exists()
466 def test_add_rule_validation_invalid_cidr(self, auth_client, test_network):
467 client, user = auth_client()
468 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
469 group = create_group(test_net.pk, "test_group")
470 rule_data = {
471 "security_group": group.id,
472 "direction": "I",
473 "proto": "tcp",
474 "port": "80",
475 "cidr": "invalid-cidr",
476 }
477 response = client.post(
478 reverse("networks:group-add-rule"),
479 rule_data,
480 )
481 assert response.status_code == 200
482 assert "Invalid CIDR format" in response.content.decode()
483 assert not Rule.objects.filter(security_group=group).exists()
485 # Invalid local CIDR format
486 rule_data = {
487 "security_group": group.id,
488 "direction": "I",
489 "proto": "tcp",
490 "port": "80",
491 "cidr": "0.0.0.0/0",
492 "local_cidr": "invalid-local-cidr",
493 }
494 response = client.post(
495 reverse("networks:group-add-rule"),
496 rule_data,
497 )
498 assert response.status_code == 200
499 assert "Invalid CIDR format" in response.content.decode()
500 assert not Rule.objects.filter(security_group=group).exists()
503class TestNetworkViews:
504 def test_network_list(self, auth_client, test_network):
505 client, user = auth_client()
506 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
507 response = client.get(reverse("networks:network-list"))
508 assert response.status_code == 200
509 assert test_net.name.encode() in response.content
510 assert test_net.cidr.encode() in response.content
512 def test_network_detail(self, auth_client, test_network):
513 client, user = auth_client()
514 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
515 ca = CA.objects.create(network=test_net, name="test_ca")
516 group = create_group(test_net.pk, "test_group")
518 response = client.get(
519 reverse("networks:network-detail", kwargs={"pk": test_net.id})
520 )
522 assert response.status_code == 200
523 assert test_net.name.encode() in response.content
524 assert test_net.cidr.encode() in response.content
525 assert ca.name.encode() in response.content
526 assert group.name.encode() in response.content
528 @pytest.mark.parametrize(
529 "test_data,expected_status",
530 [
531 ({"name": "test_net", "cidr": "192.168.1.0/24", "update_interval": 5}, 302),
532 ({"name": "test_net", "cidr": "10.0.0.0/16", "update_interval": 5}, 302),
533 ({"name": "test_net", "cidr": "100.64.0.0/24", "update_interval": 5}, 302),
534 (
535 {"name": "test_net", "cidr": "199.100.69.0/24", "update_interval": 5},
536 200,
537 ),
538 ],
539 )
540 def test_network_cidr_validation(self, auth_client, test_data, expected_status):
541 client, _ = auth_client()
542 response = client.post(reverse("networks:network-create"), test_data)
544 assert response.status_code == expected_status
546 if expected_status == 302:
547 assert Network.objects.filter(name=test_data["name"]).exists()
548 else:
549 assert not Network.objects.filter(name=test_data["name"]).exists()
552class TestTemplateViews:
553 def test_template_creation_with_security_group(self, auth_client, test_network):
554 client, user = auth_client()
555 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
556 group1 = create_group(test_net.pk, "group1")
557 group2 = create_group(test_net.pk, "group2")
558 security_group = create_group(
559 test_net.pk,
560 "security_group",
561 "Security group for testing",
562 )
563 Rule.objects.create(
564 security_group=security_group,
565 group=group1,
566 direction="I",
567 proto="tcp",
568 port="80",
569 )
570 data = {
571 "name": "test_template",
572 "network": test_net.id,
573 "is_lighthouse": False,
574 "is_relay": False,
575 "use_relay": True,
576 "groups": [group1.id, group2.id, security_group.id],
577 }
578 response = client.post(
579 reverse(
580 "networks:network-template-create", kwargs={"network_id": test_net.id}
581 ),
582 data,
583 )
584 assert response.status_code == 302
585 template = Template.objects.get(name="test_template")
586 assert list(template.groups.all()) == [group1, group2, security_group]
588 def test_template_deletion(self, auth_client, test_network):
589 client, user = auth_client()
590 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
591 security_group = create_group(
592 test_net.pk, "test_security_group", "Test security group"
593 )
594 template = create_template(
595 "test_template",
596 test_net.name,
597 groups=[security_group.name],
598 )
600 response = client.post(
601 reverse("networks:template-delete", kwargs={"pk": template.id}),
602 )
604 assert response.status_code == 302
605 assert not Template.objects.filter(id=template.id).exists()
606 assert Group.objects.filter(id=security_group.id).exists()
608 def test_template_creation_with_all_settings(self, auth_client, test_network):
609 client, user = auth_client()
610 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
611 data = {
612 "name": "test_template_with_settings",
613 "network": test_net.id,
614 "is_lighthouse": False,
615 "is_relay": False,
616 "use_relay": True,
617 "reusable": False,
618 "usage_limit": 5,
619 "expiry_days": 30,
620 "ephemeral_peers": True,
621 }
623 response = client.post(
624 reverse(
625 "networks:network-template-create", kwargs={"network_id": test_net.id}
626 ),
627 data,
628 )
630 assert response.status_code == 302
631 template = Template.objects.get(name="test_template_with_settings")
632 assert template.reusable is False
633 assert template.usage_limit == 5
634 assert template.ephemeral_peers is True
635 assert template.expires_at is not None
637 # The expiry_days field should be converted to an expires_at datetime
638 # Check that it's approximately 30 days in the future (within 1 day tolerance)
639 expected_expiry = timezone.now() + timedelta(days=30)
640 assert (
641 abs((template.expires_at - expected_expiry).total_seconds()) < 86400
642 ) # 1 day in seconds
644 def test_template_update_with_enrollment_settings(self, auth_client, test_network):
645 client, user = auth_client()
646 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
647 template = create_template(
648 "template_to_update",
649 test_net.name,
650 reusable=True,
651 usage_limit=None,
652 ephemeral_peers=False,
653 )
654 data = {
655 "name": "updated_template",
656 "network": test_net.id,
657 "is_lighthouse": False,
658 "is_relay": False,
659 "use_relay": True,
660 "reusable": False,
661 "usage_limit": 10,
662 "expiry_days": 15,
663 "ephemeral_peers": True,
664 }
665 response = client.post(
666 reverse("networks:template-edit", kwargs={"pk": template.id}),
667 data,
668 )
669 assert response.status_code == 302
670 template.refresh_from_db()
671 assert template.name == "updated_template"
672 assert template.reusable is False
673 assert template.usage_limit == 10
674 assert template.ephemeral_peers is True
675 assert template.expires_at is not None
677 # Check expiry date is approximately 15 days in the future
678 expected_expiry = timezone.now() + timedelta(days=15)
679 assert abs((template.expires_at - expected_expiry).total_seconds()) < 86400
681 def test_template_update_remove_expiry(self, auth_client, test_network):
682 client, user = auth_client()
683 test_net = test_network(name="testnet", cidr="100.100.64.0/24", user=user)
684 template = Template.objects.create(
685 name="template_with_expiry",
686 network=test_net,
687 expires_at=timezone.now() + timedelta(days=30),
688 )
689 data = {
690 "name": "template_without_expiry",
691 "network": test_net.id,
692 "is_lighthouse": False,
693 "is_relay": False,
694 "use_relay": True,
695 "reusable": True,
696 "usage_limit": "", # Empty string for no limit
697 "expiry_days": "", # Empty string for no expiration
698 "ephemeral_peers": False,
699 }
700 response = client.post(
701 reverse("networks:template-edit", kwargs={"pk": template.id}),
702 data,
703 )
704 assert response.status_code == 302
705 template.refresh_from_db()
706 assert template.name == "template_without_expiry"
707 assert template.expires_at is None
710class TestNetworkMembershipViews:
711 def test_add_member(self, auth_client, test_network, create_user):
712 client, admin_user = auth_client()
713 network = test_network(name="testnet", cidr="100.100.64.0/24", user=admin_user)
714 new_member = create_user(email="member@example.com", username="member")
715 url = reverse("networks:network-member-add", kwargs={"network_id": network.id})
716 data = {
717 "email": new_member.email,
718 "role": "MEMBER",
719 }
720 response = client.post(url, data)
721 assert response.status_code == 302
722 membership = network.memberships.get(user=new_member)
723 assert membership.role == "MEMBER"
725 def test_add_duplicate_member(self, auth_client, test_network, create_user):
726 client, admin_user = auth_client()
727 network = test_network(name="testnet", cidr="100.100.64.0/24", user=admin_user)
728 existing_member = create_user(email="member@example.com", username="member")
729 network.memberships.create(user=existing_member, role="MEMBER")
730 url = reverse("networks:network-member-add", kwargs={"network_id": network.id})
731 data = {
732 "email": existing_member.email,
733 "role": "MEMBER",
734 }
735 response = client.post(url, data)
736 assert response.status_code == 200
737 assert (
738 "This user is already a member of the network" in response.content.decode()
739 )
741 def test_edit_member_role(self, auth_client, test_network, create_user):
742 client, admin_user = auth_client()
743 network = test_network(name="testnet", cidr="100.100.64.0/24", user=admin_user)
744 member = create_user(email="member@example.com", username="member")
745 membership = network.memberships.create(user=member, role="MEMBER")
746 url = reverse(
747 "networks:network-member-edit",
748 kwargs={"network_id": network.id, "pk": membership.pk},
749 )
750 headers = {"HX-Request": "true"}
751 data = "role=ADMIN"
752 response = client.put(
753 url, data=data, content_type="application/x-www-form-urlencoded", **headers
754 )
755 assert response.status_code == 200
756 membership.refresh_from_db()
757 assert membership.role == "ADMIN"
758 assert "membership-row-" in response.content.decode()
760 def test_delete_member(self, auth_client, test_network, create_user):
761 client, admin_user = auth_client()
762 network = test_network(name="testnet", cidr="100.100.64.0/24", user=admin_user)
763 member = create_user(email="member@example.com", username="member")
764 membership = network.memberships.create(user=member, role="MEMBER")
765 url = reverse(
766 "networks:network-member-delete",
767 kwargs={"network_id": network.id, "pk": membership.pk},
768 )
769 headers = {"HX-Request": "true"}
770 response = client.delete(url, **headers)
771 assert response.status_code == 404
772 assert not network.memberships.filter(pk=membership.pk).exists()
774 def test_unauthorized_member_operations(
775 self, auth_client, test_network, create_user
776 ):
777 client, _ = auth_client()
778 admin_user = create_user(email="admin@example.com", username="admin")
779 network = test_network(name="testnet", cidr="100.100.64.0/24", user=admin_user)
780 member = create_user(email="member@example.com", username="member")
781 membership = network.memberships.create(user=member, role="MEMBER")
782 add_url = reverse(
783 "networks:network-member-add", kwargs={"network_id": network.id}
784 )
785 response = client.post(add_url, {"email": "new@example.com", "role": "MEMBER"})
786 assert response.status_code == 403
787 edit_url = reverse(
788 "networks:network-member-edit",
789 kwargs={"network_id": network.id, "pk": membership.pk},
790 )
791 response = client.put(
792 edit_url,
793 data="role=ADMIN",
794 content_type="application/x-www-form-urlencoded",
795 )
796 assert response.status_code == 403
797 delete_url = reverse(
798 "networks:network-member-delete",
799 kwargs={"network_id": network.id, "pk": membership.pk},
800 )
801 response = client.delete(delete_url)
802 assert response.status_code == 403