1use crate::{
8 AzureHttpClient, Result,
9 ops::networking::NetworkingOps,
10 types::networking::{
11 LoadBalancer, LoadBalancerCreateRequest, LoadBalancerListResult, NetworkSecurityGroup,
12 NetworkSecurityGroupCreateRequest, NetworkSecurityGroupListResult, SecurityRule,
13 SecurityRuleListResult, Subnet, SubnetListResult, VirtualNetwork,
14 VirtualNetworkCreateRequest, VirtualNetworkListResult,
15 },
16};
17
18pub struct NetworkingClient<'a> {
23 ops: NetworkingOps<'a>,
24 client: &'a AzureHttpClient,
25}
26
27impl<'a> NetworkingClient<'a> {
28 pub(crate) fn new(client: &'a AzureHttpClient) -> Self {
30 Self {
31 ops: NetworkingOps::new(client),
32 client,
33 }
34 }
35
36 pub async fn list_vnets(&self, resource_group_name: &str) -> Result<VirtualNetworkListResult> {
40 self.ops
41 .list_vnets(self.client.subscription_id(), resource_group_name)
42 .await
43 }
44
45 pub async fn list_vnets_all(&self) -> Result<VirtualNetworkListResult> {
47 self.ops.list_vnets_all(self.client.subscription_id()).await
48 }
49
50 pub async fn get_vnet(
52 &self,
53 resource_group_name: &str,
54 vnet_name: &str,
55 ) -> Result<VirtualNetwork> {
56 self.ops
57 .get_vnet(
58 self.client.subscription_id(),
59 resource_group_name,
60 vnet_name,
61 "",
62 )
63 .await
64 }
65
66 pub async fn create_vnet(
68 &self,
69 resource_group_name: &str,
70 vnet_name: &str,
71 body: &VirtualNetworkCreateRequest,
72 ) -> Result<VirtualNetwork> {
73 self.ops
74 .create_vnet(
75 self.client.subscription_id(),
76 resource_group_name,
77 vnet_name,
78 body,
79 )
80 .await
81 }
82
83 pub async fn delete_vnet(&self, resource_group_name: &str, vnet_name: &str) -> Result<()> {
85 self.ops
86 .delete_vnet(
87 self.client.subscription_id(),
88 resource_group_name,
89 vnet_name,
90 )
91 .await
92 }
93
94 pub async fn list_subnets(
96 &self,
97 resource_group_name: &str,
98 vnet_name: &str,
99 ) -> Result<SubnetListResult> {
100 self.ops
101 .list_subnets(
102 self.client.subscription_id(),
103 resource_group_name,
104 vnet_name,
105 )
106 .await
107 }
108
109 pub async fn get_subnet(
111 &self,
112 resource_group_name: &str,
113 vnet_name: &str,
114 subnet_name: &str,
115 ) -> Result<Subnet> {
116 self.ops
117 .get_subnet(
118 self.client.subscription_id(),
119 resource_group_name,
120 vnet_name,
121 subnet_name,
122 "",
123 )
124 .await
125 }
126
127 pub async fn list_nsgs(
131 &self,
132 resource_group_name: &str,
133 ) -> Result<NetworkSecurityGroupListResult> {
134 self.ops
135 .list_nsgs(self.client.subscription_id(), resource_group_name)
136 .await
137 }
138
139 pub async fn list_nsgs_all(&self) -> Result<NetworkSecurityGroupListResult> {
141 self.ops.list_nsgs_all(self.client.subscription_id()).await
142 }
143
144 pub async fn get_nsg(
146 &self,
147 resource_group_name: &str,
148 nsg_name: &str,
149 ) -> Result<NetworkSecurityGroup> {
150 self.ops
151 .get_nsg(
152 self.client.subscription_id(),
153 resource_group_name,
154 nsg_name,
155 "",
156 )
157 .await
158 }
159
160 pub async fn create_nsg(
162 &self,
163 resource_group_name: &str,
164 nsg_name: &str,
165 body: &NetworkSecurityGroupCreateRequest,
166 ) -> Result<NetworkSecurityGroup> {
167 self.ops
168 .create_nsg(
169 self.client.subscription_id(),
170 resource_group_name,
171 nsg_name,
172 body,
173 )
174 .await
175 }
176
177 pub async fn delete_nsg(&self, resource_group_name: &str, nsg_name: &str) -> Result<()> {
179 self.ops
180 .delete_nsg(self.client.subscription_id(), resource_group_name, nsg_name)
181 .await
182 }
183
184 pub async fn list_security_rules(
186 &self,
187 resource_group_name: &str,
188 nsg_name: &str,
189 ) -> Result<SecurityRuleListResult> {
190 self.ops
191 .list_security_rules(self.client.subscription_id(), resource_group_name, nsg_name)
192 .await
193 }
194
195 pub async fn get_security_rule(
197 &self,
198 resource_group_name: &str,
199 nsg_name: &str,
200 rule_name: &str,
201 ) -> Result<SecurityRule> {
202 self.ops
203 .get_security_rule(
204 self.client.subscription_id(),
205 resource_group_name,
206 nsg_name,
207 rule_name,
208 )
209 .await
210 }
211
212 pub async fn create_security_rule(
214 &self,
215 resource_group_name: &str,
216 nsg_name: &str,
217 rule_name: &str,
218 body: &SecurityRule,
219 ) -> Result<SecurityRule> {
220 self.ops
221 .create_security_rule(
222 self.client.subscription_id(),
223 resource_group_name,
224 nsg_name,
225 rule_name,
226 body,
227 )
228 .await
229 }
230
231 pub async fn delete_security_rule(
233 &self,
234 resource_group_name: &str,
235 nsg_name: &str,
236 rule_name: &str,
237 ) -> Result<()> {
238 self.ops
239 .delete_security_rule(
240 self.client.subscription_id(),
241 resource_group_name,
242 nsg_name,
243 rule_name,
244 )
245 .await
246 }
247
248 pub async fn list_load_balancers(
252 &self,
253 resource_group_name: &str,
254 ) -> Result<LoadBalancerListResult> {
255 self.ops
256 .list_load_balancers(self.client.subscription_id(), resource_group_name)
257 .await
258 }
259
260 pub async fn list_load_balancers_all(&self) -> Result<LoadBalancerListResult> {
262 self.ops
263 .list_load_balancers_all(self.client.subscription_id())
264 .await
265 }
266
267 pub async fn get_load_balancer(
269 &self,
270 resource_group_name: &str,
271 lb_name: &str,
272 ) -> Result<LoadBalancer> {
273 self.ops
274 .get_load_balancer(
275 self.client.subscription_id(),
276 resource_group_name,
277 lb_name,
278 "",
279 )
280 .await
281 }
282
283 pub async fn create_load_balancer(
285 &self,
286 resource_group_name: &str,
287 lb_name: &str,
288 body: &LoadBalancerCreateRequest,
289 ) -> Result<LoadBalancer> {
290 self.ops
291 .create_load_balancer(
292 self.client.subscription_id(),
293 resource_group_name,
294 lb_name,
295 body,
296 )
297 .await
298 }
299
300 pub async fn delete_load_balancer(
302 &self,
303 resource_group_name: &str,
304 lb_name: &str,
305 ) -> Result<()> {
306 self.ops
307 .delete_load_balancer(self.client.subscription_id(), resource_group_name, lb_name)
308 .await
309 }
310
311 pub async fn delete_network_interface(
315 &self,
316 resource_group_name: &str,
317 nic_name: &str,
318 ) -> Result<()> {
319 self.ops
320 .delete_network_interface(self.client.subscription_id(), resource_group_name, nic_name)
321 .await
322 }
323
324 pub async fn delete_nat_gateway(
328 &self,
329 resource_group_name: &str,
330 nat_gateway_name: &str,
331 ) -> Result<()> {
332 self.ops
333 .delete_nat_gateway(
334 self.client.subscription_id(),
335 resource_group_name,
336 nat_gateway_name,
337 )
338 .await
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 #[tokio::test]
349 async fn list_vnets_returns_empty_list() {
350 let mut mock = crate::MockClient::new();
351 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks")
352 .returning_json(serde_json::json!({ "value": [] }));
353
354 let client = AzureHttpClient::from_mock(mock);
355 let result = client
356 .networking()
357 .list_vnets("my-rg")
358 .await
359 .expect("list_vnets failed");
360 assert!(result.value.is_empty());
361 assert!(result.next_link.is_none());
362 }
363
364 #[tokio::test]
365 async fn list_vnets_returns_vnet_with_fields() {
366 let mut mock = crate::MockClient::new();
367 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks")
368 .returning_json(serde_json::json!({
369 "value": [{
370 "id": "/subscriptions/sub/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/vnet1",
371 "name": "vnet1",
372 "type": "Microsoft.Network/virtualNetworks",
373 "location": "eastus",
374 "properties": {
375 "provisioningState": "Succeeded",
376 "addressSpace": { "addressPrefixes": ["10.0.0.0/16"] }
377 }
378 }]
379 }));
380
381 let client = AzureHttpClient::from_mock(mock);
382 let result = client
383 .networking()
384 .list_vnets("my-rg")
385 .await
386 .expect("list_vnets failed");
387 assert_eq!(result.value.len(), 1);
388 let vnet = &result.value[0];
389 assert_eq!(vnet.name.as_deref(), Some("vnet1"));
390 assert_eq!(vnet.location.as_deref(), Some("eastus"));
391 assert_eq!(
392 vnet.properties
393 .as_ref()
394 .and_then(|p| p.provisioning_state.as_deref()),
395 Some("Succeeded"),
396 );
397 assert_eq!(
398 vnet.properties
399 .as_ref()
400 .and_then(|p| p.address_space.as_ref())
401 .map(|a| a.address_prefixes.as_slice()),
402 Some(["10.0.0.0/16".to_string()].as_slice()),
403 );
404 }
405
406 #[tokio::test]
407 async fn list_vnets_all_uses_subscription_scope() {
408 let mut mock = crate::MockClient::new();
409 mock.expect_get(
410 "/subscriptions/test-subscription-id/providers/Microsoft.Network/virtualNetworks",
411 )
412 .returning_json(serde_json::json!({ "value": [] }));
413
414 let client = AzureHttpClient::from_mock(mock);
415 let result = client
416 .networking()
417 .list_vnets_all()
418 .await
419 .expect("list_vnets_all failed");
420 assert!(result.value.is_empty());
421 }
422
423 #[tokio::test]
424 async fn get_vnet_injects_subscription_id() {
425 let mut mock = crate::MockClient::new();
426 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/my-vnet")
427 .returning_json(serde_json::json!({
428 "id": "/subscriptions/sub/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/my-vnet",
429 "name": "my-vnet",
430 "type": "Microsoft.Network/virtualNetworks",
431 "location": "westus2",
432 "properties": {
433 "provisioningState": "Succeeded",
434 "addressSpace": { "addressPrefixes": ["10.1.0.0/16"] }
435 }
436 }));
437
438 let client = AzureHttpClient::from_mock(mock);
439 let vnet = client
440 .networking()
441 .get_vnet("rg1", "my-vnet")
442 .await
443 .expect("get_vnet failed");
444 assert_eq!(vnet.name.as_deref(), Some("my-vnet"));
445 assert_eq!(vnet.location.as_deref(), Some("westus2"));
446 assert!(vnet.id.is_some());
447 }
448
449 #[tokio::test]
450 async fn create_vnet_sends_put_and_returns_vnet() {
451 let mut mock = crate::MockClient::new();
452 mock.expect_put("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/new-vnet")
453 .returning_json(serde_json::json!({
454 "name": "new-vnet",
455 "location": "eastus",
456 "properties": {
457 "provisioningState": "Updating",
458 "addressSpace": { "addressPrefixes": ["10.2.0.0/16"] }
459 }
460 }));
461
462 let client = AzureHttpClient::from_mock(mock);
463 let request = VirtualNetworkCreateRequest {
464 location: "eastus".into(),
465 properties: Some(crate::types::networking::VirtualNetworkPropertiesFormat {
466 address_space: Some(crate::types::networking::AddressSpace {
467 address_prefixes: vec!["10.2.0.0/16".into()],
468 }),
469 ..Default::default()
470 }),
471 ..Default::default()
472 };
473 let vnet = client
474 .networking()
475 .create_vnet("rg1", "new-vnet", &request)
476 .await
477 .expect("create_vnet failed");
478 assert_eq!(vnet.name.as_deref(), Some("new-vnet"));
479 assert_eq!(
480 vnet.properties
481 .as_ref()
482 .and_then(|p| p.provisioning_state.as_deref()),
483 Some("Updating"),
484 );
485 }
486
487 #[tokio::test]
488 async fn delete_vnet_sends_delete() {
489 let mut mock = crate::MockClient::new();
490 mock.expect_delete("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/old-vnet")
491 .returning_json(serde_json::json!(null));
492
493 let client = AzureHttpClient::from_mock(mock);
494 client
495 .networking()
496 .delete_vnet("rg1", "old-vnet")
497 .await
498 .expect("delete_vnet failed");
499 }
500
501 #[tokio::test]
502 async fn list_subnets_returns_subnet_list() {
503 let mut mock = crate::MockClient::new();
504 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets")
505 .returning_json(serde_json::json!({
506 "value": [{
507 "id": "/subscriptions/sub/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/default",
508 "name": "default",
509 "properties": { "addressPrefix": "10.0.0.0/24", "provisioningState": "Succeeded" }
510 }]
511 }));
512
513 let client = AzureHttpClient::from_mock(mock);
514 let result = client
515 .networking()
516 .list_subnets("rg1", "my-vnet")
517 .await
518 .expect("list_subnets failed");
519 assert_eq!(result.value.len(), 1);
520 assert_eq!(result.value[0].name.as_deref(), Some("default"));
521 assert_eq!(
522 result.value[0]
523 .properties
524 .as_ref()
525 .and_then(|p| p.address_prefix.as_deref()),
526 Some("10.0.0.0/24"),
527 );
528 }
529
530 #[tokio::test]
531 async fn get_subnet_returns_address_prefix() {
532 let mut mock = crate::MockClient::new();
533 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/default")
534 .returning_json(serde_json::json!({
535 "id": "/subscriptions/sub/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/default",
536 "name": "default",
537 "properties": { "addressPrefix": "10.0.0.0/24", "provisioningState": "Succeeded" }
538 }));
539
540 let client = AzureHttpClient::from_mock(mock);
541 let subnet = client
542 .networking()
543 .get_subnet("rg1", "my-vnet", "default")
544 .await
545 .expect("get_subnet failed");
546 assert_eq!(subnet.name.as_deref(), Some("default"));
547 assert_eq!(
548 subnet
549 .properties
550 .as_ref()
551 .and_then(|p| p.address_prefix.as_deref()),
552 Some("10.0.0.0/24"),
553 );
554 }
555
556 #[tokio::test]
559 async fn list_nsgs_returns_nsg_list() {
560 let mut mock = crate::MockClient::new();
561 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups")
562 .returning_json(serde_json::json!({
563 "value": [{
564 "id": "/subscriptions/sub/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/my-nsg",
565 "name": "my-nsg",
566 "type": "Microsoft.Network/networkSecurityGroups",
567 "location": "eastus",
568 "properties": {
569 "provisioningState": "Succeeded",
570 "securityRules": [],
571 "defaultSecurityRules": []
572 }
573 }]
574 }));
575
576 let client = AzureHttpClient::from_mock(mock);
577 let result = client
578 .networking()
579 .list_nsgs("rg1")
580 .await
581 .expect("list_nsgs failed");
582 assert_eq!(result.value.len(), 1);
583 let nsg = &result.value[0];
584 assert_eq!(nsg.name.as_deref(), Some("my-nsg"));
585 assert_eq!(nsg.location.as_deref(), Some("eastus"));
586 assert_eq!(
587 nsg.properties
588 .as_ref()
589 .and_then(|p| p.provisioning_state.as_deref()),
590 Some("Succeeded"),
591 );
592 }
593
594 #[tokio::test]
595 async fn list_nsgs_all_uses_subscription_scope() {
596 let mut mock = crate::MockClient::new();
597 mock.expect_get(
598 "/subscriptions/test-subscription-id/providers/Microsoft.Network/networkSecurityGroups",
599 )
600 .returning_json(serde_json::json!({ "value": [] }));
601
602 let client = AzureHttpClient::from_mock(mock);
603 let result = client
604 .networking()
605 .list_nsgs_all()
606 .await
607 .expect("list_nsgs_all failed");
608 assert!(result.value.is_empty());
609 }
610
611 #[tokio::test]
612 async fn get_nsg_returns_nsg_with_default_rules() {
613 let mut mock = crate::MockClient::new();
614 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/my-nsg")
615 .returning_json(serde_json::json!({
616 "id": "/subscriptions/sub/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/my-nsg",
617 "name": "my-nsg",
618 "location": "eastus",
619 "properties": {
620 "provisioningState": "Succeeded",
621 "securityRules": [],
622 "defaultSecurityRules": [
623 {
624 "name": "AllowVnetInBound",
625 "properties": {
626 "priority": 65000,
627 "access": "Allow",
628 "direction": "Inbound",
629 "protocol": "*"
630 }
631 }
632 ]
633 }
634 }));
635
636 let client = AzureHttpClient::from_mock(mock);
637 let nsg = client
638 .networking()
639 .get_nsg("rg1", "my-nsg")
640 .await
641 .expect("get_nsg failed");
642 assert_eq!(nsg.name.as_deref(), Some("my-nsg"));
643 assert!(nsg.id.is_some());
644 let default_rules = &nsg.properties.as_ref().unwrap().default_security_rules;
645 assert_eq!(default_rules.len(), 1);
646 assert_eq!(default_rules[0].name.as_deref(), Some("AllowVnetInBound"));
647 }
648
649 #[tokio::test]
650 async fn create_nsg_sends_put_and_returns_nsg() {
651 let mut mock = crate::MockClient::new();
652 mock.expect_put("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/new-nsg")
653 .returning_json(serde_json::json!({
654 "name": "new-nsg",
655 "location": "eastus",
656 "properties": {
657 "provisioningState": "Updating",
658 "securityRules": [],
659 "defaultSecurityRules": []
660 }
661 }));
662
663 let client = AzureHttpClient::from_mock(mock);
664 let request = NetworkSecurityGroupCreateRequest {
665 location: "eastus".into(),
666 ..Default::default()
667 };
668 let nsg = client
669 .networking()
670 .create_nsg("rg1", "new-nsg", &request)
671 .await
672 .expect("create_nsg failed");
673 assert_eq!(nsg.name.as_deref(), Some("new-nsg"));
674 assert_eq!(
675 nsg.properties
676 .as_ref()
677 .and_then(|p| p.provisioning_state.as_deref()),
678 Some("Updating"),
679 );
680 }
681
682 #[tokio::test]
683 async fn delete_nsg_sends_delete() {
684 let mut mock = crate::MockClient::new();
685 mock.expect_delete("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/old-nsg")
686 .returning_json(serde_json::json!(null));
687
688 let client = AzureHttpClient::from_mock(mock);
689 client
690 .networking()
691 .delete_nsg("rg1", "old-nsg")
692 .await
693 .expect("delete_nsg failed");
694 }
695
696 #[tokio::test]
699 async fn list_security_rules_returns_empty_initially() {
700 let mut mock = crate::MockClient::new();
701 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/my-nsg/securityRules")
702 .returning_json(serde_json::json!({ "value": [] }));
703
704 let client = AzureHttpClient::from_mock(mock);
705 let result = client
706 .networking()
707 .list_security_rules("rg1", "my-nsg")
708 .await
709 .expect("list_security_rules failed");
710 assert!(result.value.is_empty());
711 }
712
713 #[tokio::test]
714 async fn list_security_rules_returns_rule_with_fields() {
715 let mut mock = crate::MockClient::new();
716 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/my-nsg/securityRules")
717 .returning_json(serde_json::json!({
718 "value": [{
719 "name": "allow-http",
720 "properties": {
721 "priority": 100,
722 "protocol": "Tcp",
723 "access": "Allow",
724 "direction": "Inbound",
725 "sourcePortRange": "*",
726 "destinationPortRange": "80",
727 "sourceAddressPrefix": "*",
728 "destinationAddressPrefix": "*",
729 "provisioningState": "Succeeded"
730 }
731 }]
732 }));
733
734 let client = AzureHttpClient::from_mock(mock);
735 let result = client
736 .networking()
737 .list_security_rules("rg1", "my-nsg")
738 .await
739 .expect("list_security_rules failed");
740 assert_eq!(result.value.len(), 1);
741 let rule = &result.value[0];
742 assert_eq!(rule.name.as_deref(), Some("allow-http"));
743 assert_eq!(rule.properties.as_ref().and_then(|p| p.priority), Some(100),);
744 assert_eq!(
745 rule.properties.as_ref().and_then(|p| p.access.as_deref()),
746 Some("Allow"),
747 );
748 assert_eq!(
749 rule.properties
750 .as_ref()
751 .and_then(|p| p.destination_port_range.as_deref()),
752 Some("80"),
753 );
754 }
755
756 #[tokio::test]
757 async fn get_security_rule_returns_rule() {
758 let mut mock = crate::MockClient::new();
759 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/my-nsg/securityRules/my-rule")
760 .returning_json(serde_json::json!({
761 "name": "my-rule",
762 "properties": {
763 "priority": 200,
764 "protocol": "Tcp",
765 "access": "Deny",
766 "direction": "Outbound",
767 "sourcePortRange": "*",
768 "destinationPortRange": "443",
769 "sourceAddressPrefix": "*",
770 "destinationAddressPrefix": "Internet"
771 }
772 }));
773
774 let client = AzureHttpClient::from_mock(mock);
775 let rule = client
776 .networking()
777 .get_security_rule("rg1", "my-nsg", "my-rule")
778 .await
779 .expect("get_security_rule failed");
780 assert_eq!(rule.name.as_deref(), Some("my-rule"));
781 assert_eq!(
782 rule.properties
783 .as_ref()
784 .and_then(|p| p.direction.as_deref()),
785 Some("Outbound"),
786 );
787 assert_eq!(
788 rule.properties.as_ref().and_then(|p| p.access.as_deref()),
789 Some("Deny"),
790 );
791 assert_eq!(
792 rule.properties
793 .as_ref()
794 .and_then(|p| p.destination_port_range.as_deref()),
795 Some("443"),
796 );
797 }
798
799 #[tokio::test]
800 async fn create_security_rule_sends_put_and_returns_rule() {
801 let mut mock = crate::MockClient::new();
802 mock.expect_put("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/my-nsg/securityRules/new-rule")
803 .returning_json(serde_json::json!({
804 "name": "new-rule",
805 "properties": {
806 "priority": 300,
807 "protocol": "Tcp",
808 "access": "Allow",
809 "direction": "Inbound",
810 "sourcePortRange": "*",
811 "destinationPortRange": "8080",
812 "sourceAddressPrefix": "*",
813 "destinationAddressPrefix": "*",
814 "provisioningState": "Succeeded"
815 }
816 }));
817
818 let client = AzureHttpClient::from_mock(mock);
819 let body = SecurityRule {
820 name: Some("new-rule".into()),
821 properties: Some(crate::types::networking::SecurityRulePropertiesFormat {
822 protocol: Some("Tcp".into()),
823 source_port_range: Some("*".into()),
824 destination_port_range: Some("8080".into()),
825 source_address_prefix: Some("*".into()),
826 destination_address_prefix: Some("*".into()),
827 access: Some("Allow".into()),
828 priority: Some(300),
829 direction: Some("Inbound".into()),
830 ..Default::default()
831 }),
832 ..Default::default()
833 };
834 let rule = client
835 .networking()
836 .create_security_rule("rg1", "my-nsg", "new-rule", &body)
837 .await
838 .expect("create_security_rule failed");
839 assert_eq!(rule.name.as_deref(), Some("new-rule"));
840 assert_eq!(rule.properties.as_ref().and_then(|p| p.priority), Some(300),);
841 assert_eq!(
842 rule.properties
843 .as_ref()
844 .and_then(|p| p.provisioning_state.as_deref()),
845 Some("Succeeded"),
846 );
847 }
848
849 #[tokio::test]
850 async fn delete_security_rule_sends_delete() {
851 let mut mock = crate::MockClient::new();
852 mock.expect_delete("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/my-nsg/securityRules/old-rule")
853 .returning_json(serde_json::json!(null));
854
855 let client = AzureHttpClient::from_mock(mock);
856 client
857 .networking()
858 .delete_security_rule("rg1", "my-nsg", "old-rule")
859 .await
860 .expect("delete_security_rule failed");
861 }
862
863 #[tokio::test]
866 async fn list_load_balancers_returns_empty_list() {
867 let mut mock = crate::MockClient::new();
868 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers")
869 .returning_json(serde_json::json!({ "value": [] }));
870
871 let client = AzureHttpClient::from_mock(mock);
872 let result = client
873 .networking()
874 .list_load_balancers("rg1")
875 .await
876 .expect("list_load_balancers failed");
877 assert!(result.value.is_empty());
878 }
879
880 #[tokio::test]
881 async fn list_load_balancers_returns_lb_with_standard_sku() {
882 let mut mock = crate::MockClient::new();
883 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers")
884 .returning_json(serde_json::json!({
885 "value": [{
886 "id": "/subscriptions/sub/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers/my-lb",
887 "name": "my-lb",
888 "type": "Microsoft.Network/loadBalancers",
889 "location": "eastus",
890 "sku": { "name": "Standard", "tier": "Regional" },
891 "properties": {
892 "provisioningState": "Succeeded",
893 "frontendIPConfigurations": [],
894 "backendAddressPools": []
895 }
896 }]
897 }));
898
899 let client = AzureHttpClient::from_mock(mock);
900 let result = client
901 .networking()
902 .list_load_balancers("rg1")
903 .await
904 .expect("list_load_balancers failed");
905 assert_eq!(result.value.len(), 1);
906 let lb = &result.value[0];
907 assert_eq!(lb.name.as_deref(), Some("my-lb"));
908 assert_eq!(lb.location.as_deref(), Some("eastus"));
909 assert_eq!(
910 lb.sku.as_ref().and_then(|s| s.name.as_deref()),
911 Some("Standard")
912 );
913 assert_eq!(
914 lb.properties
915 .as_ref()
916 .and_then(|p| p.provisioning_state.as_deref()),
917 Some("Succeeded"),
918 );
919 }
920
921 #[tokio::test]
922 async fn list_load_balancers_all_uses_subscription_scope() {
923 let mut mock = crate::MockClient::new();
924 mock.expect_get(
925 "/subscriptions/test-subscription-id/providers/Microsoft.Network/loadBalancers",
926 )
927 .returning_json(serde_json::json!({ "value": [] }));
928
929 let client = AzureHttpClient::from_mock(mock);
930 let result = client
931 .networking()
932 .list_load_balancers_all()
933 .await
934 .expect("list_load_balancers_all failed");
935 assert!(result.value.is_empty());
936 }
937
938 #[tokio::test]
939 async fn get_load_balancer_deserializes_frontend_ips() {
940 let mut mock = crate::MockClient::new();
941 mock.expect_get("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers/my-lb")
942 .returning_json(serde_json::json!({
943 "id": "/subscriptions/sub/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers/my-lb",
944 "name": "my-lb",
945 "location": "eastus",
946 "sku": { "name": "Standard" },
947 "properties": {
948 "provisioningState": "Succeeded",
949 "frontendIPConfigurations": [{
950 "name": "frontend",
951 "properties": {
952 "privateIPAllocationMethod": "Dynamic",
953 "provisioningState": "Succeeded"
954 }
955 }]
956 }
957 }));
958
959 let client = AzureHttpClient::from_mock(mock);
960 let lb = client
961 .networking()
962 .get_load_balancer("rg1", "my-lb")
963 .await
964 .expect("get_load_balancer failed");
965 assert_eq!(lb.name.as_deref(), Some("my-lb"));
966 assert!(lb.id.is_some());
967 let frontend_ips = &lb.properties.as_ref().unwrap().frontend_ip_configurations;
968 assert_eq!(frontend_ips.len(), 1);
969 assert_eq!(frontend_ips[0].name.as_deref(), Some("frontend"));
970 assert_eq!(
971 frontend_ips[0]
972 .properties
973 .as_ref()
974 .and_then(|p| p.private_ip_allocation_method.as_deref()),
975 Some("Dynamic"),
976 );
977 }
978
979 #[tokio::test]
980 async fn create_load_balancer_sends_put_and_returns_lb() {
981 let mut mock = crate::MockClient::new();
982 mock.expect_put("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers/new-lb")
983 .returning_json(serde_json::json!({
984 "name": "new-lb",
985 "location": "eastus",
986 "sku": { "name": "Standard" },
987 "properties": {
988 "provisioningState": "Succeeded",
989 "frontendIPConfigurations": []
990 }
991 }));
992
993 let client = AzureHttpClient::from_mock(mock);
994 let request = LoadBalancerCreateRequest {
995 location: "eastus".into(),
996 sku: Some(crate::types::networking::LoadBalancerSku {
997 name: Some("Standard".into()),
998 ..Default::default()
999 }),
1000 ..Default::default()
1001 };
1002 let lb = client
1003 .networking()
1004 .create_load_balancer("rg1", "new-lb", &request)
1005 .await
1006 .expect("create_load_balancer failed");
1007 assert_eq!(lb.name.as_deref(), Some("new-lb"));
1008 assert_eq!(
1009 lb.properties
1010 .as_ref()
1011 .and_then(|p| p.provisioning_state.as_deref()),
1012 Some("Succeeded"),
1013 );
1014 }
1015
1016 #[tokio::test]
1017 async fn delete_load_balancer_sends_delete() {
1018 let mut mock = crate::MockClient::new();
1019 mock.expect_delete("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers/old-lb")
1020 .returning_json(serde_json::json!(null));
1021
1022 let client = AzureHttpClient::from_mock(mock);
1023 client
1024 .networking()
1025 .delete_load_balancer("rg1", "old-lb")
1026 .await
1027 .expect("delete_load_balancer failed");
1028 }
1029
1030 #[tokio::test]
1033 async fn delete_network_interface_sends_delete() {
1034 let mut mock = crate::MockClient::new();
1035 mock.expect_delete("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/networkInterfaces/old-nic")
1036 .returning_json(serde_json::json!(null));
1037
1038 let client = AzureHttpClient::from_mock(mock);
1039 client
1040 .networking()
1041 .delete_network_interface("rg1", "old-nic")
1042 .await
1043 .expect("delete_network_interface failed");
1044 }
1045
1046 #[tokio::test]
1049 async fn delete_nat_gateway_sends_delete() {
1050 let mut mock = crate::MockClient::new();
1051 mock.expect_delete("/subscriptions/test-subscription-id/resourceGroups/rg1/providers/Microsoft.Network/natGateways/old-natgw")
1052 .returning_json(serde_json::json!(null));
1053
1054 let client = AzureHttpClient::from_mock(mock);
1055 client
1056 .networking()
1057 .delete_nat_gateway("rg1", "old-natgw")
1058 .await
1059 .expect("delete_nat_gateway failed");
1060 }
1061}