@spinnaker/azure 2026.2.2 → 2026.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/dist/azure.module.d.ts +3 -1
  2. package/dist/image/image.reader.d.ts +4 -2
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/instance/azureInstanceType.service.d.ts +6 -2
  6. package/dist/instance/details/AzureInstanceDetails.d.ts +67 -0
  7. package/dist/loadBalancer/configure/AzureLoadBalancerChoiceModal.d.ts +10 -5
  8. package/dist/loadBalancer/configure/AzureLoadBalancerModal.d.ts +102 -0
  9. package/dist/loadBalancer/details/azureLoadBalancerDetails.d.ts +26 -0
  10. package/dist/loadBalancer/loadBalancer.transformer.d.ts +45 -2
  11. package/dist/pipeline/stages/AzureAccountRegionClusterSelector.d.ts +32 -0
  12. package/dist/pipeline/stages/bake/azureBakeStage.d.ts +46 -2
  13. package/dist/pipeline/stages/destroyAsg/azureDestroyAsgStage.d.ts +14 -2
  14. package/dist/pipeline/stages/disableAsg/azureDisableAsgStage.d.ts +4 -2
  15. package/dist/pipeline/stages/enableAsg/azureEnableAsgStage.d.ts +4 -2
  16. package/dist/securityGroup/configure/AzureSecurityGroupModal.d.ts +70 -0
  17. package/dist/securityGroup/details/AzureSecurityGroupDetails.d.ts +51 -0
  18. package/dist/securityGroup/securityGroup.reader.d.ts +3 -2
  19. package/dist/securityGroup/securityGroup.transformer.d.ts +3 -2
  20. package/dist/securityGroup/securityGroup.write.service.d.ts +9 -2
  21. package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +88 -2
  22. package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +30 -2
  23. package/dist/serverGroup/configure/wizard/AzureCloneServerGroupModal.d.ts +44 -0
  24. package/dist/serverGroup/configure/wizard/pages/ServerGroupAdvancedSettings.d.ts +8 -0
  25. package/dist/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.d.ts +12 -0
  26. package/dist/serverGroup/configure/wizard/pages/ServerGroupCapacityAndZones.d.ts +11 -0
  27. package/dist/serverGroup/configure/wizard/pages/ServerGroupHealthSettings.d.ts +10 -0
  28. package/dist/serverGroup/configure/wizard/pages/ServerGroupImageSettings.d.ts +12 -0
  29. package/dist/serverGroup/configure/wizard/pages/ServerGroupInstanceType.d.ts +9 -0
  30. package/dist/serverGroup/configure/wizard/pages/ServerGroupLoadBalancers.d.ts +14 -0
  31. package/dist/serverGroup/configure/wizard/pages/ServerGroupNetworkSettings.d.ts +10 -0
  32. package/dist/serverGroup/configure/wizard/pages/ServerGroupSecurityGroups.d.ts +6 -0
  33. package/dist/serverGroup/configure/wizard/pages/ServerGroupTags.d.ts +9 -0
  34. package/dist/serverGroup/configure/wizard/pages/common.d.ts +14 -0
  35. package/dist/serverGroup/configure/wizard/pages/index.d.ts +10 -0
  36. package/dist/serverGroup/details/azureServerGroupDetails.d.ts +13 -0
  37. package/dist/serverGroup/details/rollback/RollbackServerGroupModal.d.ts +6 -4
  38. package/dist/serverGroup/serverGroup.transformer.d.ts +61 -2
  39. package/dist/validation/applicationName.validator.d.ts +6 -2
  40. package/package.json +5 -8
  41. package/src/azure.module.spec.ts +921 -0
  42. package/src/azure.module.ts +56 -68
  43. package/src/image/image.reader.js +4 -15
  44. package/src/image/image.reader.spec.js +4 -7
  45. package/src/instance/azureInstanceType.service.js +8 -16
  46. package/src/instance/azureInstanceType.service.spec.js +5 -13
  47. package/src/instance/details/AzureInstanceDetails.spec.tsx +364 -0
  48. package/src/instance/details/AzureInstanceDetails.tsx +533 -0
  49. package/src/loadBalancer/configure/AzureLoadBalancerChoiceModal.spec.tsx +125 -0
  50. package/src/loadBalancer/configure/AzureLoadBalancerChoiceModal.tsx +46 -26
  51. package/src/loadBalancer/configure/AzureLoadBalancerModal.spec.tsx +658 -0
  52. package/src/loadBalancer/configure/AzureLoadBalancerModal.tsx +960 -0
  53. package/src/loadBalancer/details/azureLoadBalancerDetails.spec.tsx +470 -0
  54. package/src/loadBalancer/details/azureLoadBalancerDetails.tsx +412 -0
  55. package/src/loadBalancer/loadBalancer.transformer.js +83 -93
  56. package/src/pipeline/stages/AzureAccountRegionClusterSelector.tsx +238 -0
  57. package/src/pipeline/stages/bake/azureBakeStage.tsx +723 -0
  58. package/src/pipeline/stages/destroyAsg/azureDestroyAsgStage.tsx +128 -0
  59. package/src/pipeline/stages/disableAsg/azureDisableAsgStage.tsx +92 -0
  60. package/src/pipeline/stages/enableAsg/azureEnableAsgStage.tsx +87 -0
  61. package/src/routerConsumers.spec.tsx +69 -0
  62. package/src/securityGroup/configure/AzureSecurityGroupModal.spec.tsx +558 -0
  63. package/src/securityGroup/configure/AzureSecurityGroupModal.tsx +782 -0
  64. package/src/securityGroup/details/AzureSecurityGroupDetails.spec.tsx +291 -0
  65. package/src/securityGroup/details/AzureSecurityGroupDetails.tsx +354 -0
  66. package/src/securityGroup/securityGroup.reader.js +3 -13
  67. package/src/securityGroup/securityGroup.transformer.js +3 -13
  68. package/src/securityGroup/securityGroup.write.service.spec.ts +105 -0
  69. package/src/securityGroup/securityGroup.write.service.ts +73 -0
  70. package/src/serverGroup/configure/serverGroupCommandBuilder.service.js +167 -166
  71. package/src/serverGroup/configure/serverGroupCommandBuilder.service.spec.js +63 -0
  72. package/src/serverGroup/configure/serverGroupConfiguration.service.js +325 -309
  73. package/src/serverGroup/configure/serverGroupConfiguration.service.spec.js +57 -53
  74. package/src/serverGroup/configure/wizard/AzureCloneServerGroupModal.spec.tsx +758 -0
  75. package/src/serverGroup/configure/wizard/AzureCloneServerGroupModal.tsx +349 -0
  76. package/src/serverGroup/configure/wizard/pages/ServerGroupAdvancedSettings.tsx +208 -0
  77. package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.tsx +122 -0
  78. package/src/serverGroup/configure/wizard/pages/ServerGroupCapacityAndZones.tsx +80 -0
  79. package/src/serverGroup/configure/wizard/pages/ServerGroupHealthSettings.tsx +97 -0
  80. package/src/serverGroup/configure/wizard/pages/ServerGroupImageSettings.tsx +151 -0
  81. package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +41 -0
  82. package/src/serverGroup/configure/wizard/pages/ServerGroupLoadBalancers.tsx +214 -0
  83. package/src/serverGroup/configure/wizard/pages/ServerGroupNetworkSettings.tsx +119 -0
  84. package/src/serverGroup/configure/wizard/pages/ServerGroupSecurityGroups.tsx +54 -0
  85. package/src/serverGroup/configure/wizard/pages/ServerGroupTags.tsx +40 -0
  86. package/src/serverGroup/configure/wizard/pages/common.tsx +72 -0
  87. package/src/serverGroup/configure/wizard/pages/index.ts +10 -0
  88. package/src/serverGroup/details/azureServerGroupDetails.spec.tsx +263 -0
  89. package/src/serverGroup/details/azureServerGroupDetails.tsx +318 -0
  90. package/src/serverGroup/details/rollback/RollbackServerGroupModal.tsx +24 -30
  91. package/src/serverGroup/serverGroup.transformer.js +11 -19
  92. package/src/serverGroup/serverGroup.transformer.spec.js +2 -6
  93. package/src/validation/applicationName.validator.js +14 -29
  94. package/dist/instance/details/instance.details.controller.d.ts +0 -2
  95. package/dist/loadBalancer/configure/createLoadBalancer.controller.d.ts +0 -2
  96. package/dist/loadBalancer/details/loadBalancerDetail.controller.d.ts +0 -2
  97. package/dist/pipeline/stages/bake/bakeExecutionDetails.controller.d.ts +0 -2
  98. package/dist/securityGroup/clone/cloneSecurityGroup.controller.d.ts +0 -2
  99. package/dist/securityGroup/configure/CreateSecurityGroupCtrl.d.ts +0 -2
  100. package/dist/securityGroup/configure/EditSecurityGroupCtrl.d.ts +0 -2
  101. package/dist/securityGroup/details/securityGroupDetail.controller.d.ts +0 -2
  102. package/dist/serverGroup/configure/serverGroup.configure.azure.module.d.ts +0 -2
  103. package/dist/serverGroup/configure/wizard/CloneServerGroup.azure.controller.d.ts +0 -2
  104. package/dist/serverGroup/configure/wizard/ServerGroupInstanceArchetype.controller.d.ts +0 -2
  105. package/dist/serverGroup/configure/wizard/ServerGroupInstanceType.controller.d.ts +0 -2
  106. package/dist/serverGroup/configure/wizard/advancedSettings/ServerGroupAdvancedSettings.controller.d.ts +0 -2
  107. package/dist/serverGroup/configure/wizard/advancedSettings/advancedSettingsSelector.directive.d.ts +0 -2
  108. package/dist/serverGroup/configure/wizard/basicSettings/ServerGroupBasicSettings.controller.d.ts +0 -2
  109. package/dist/serverGroup/configure/wizard/basicSettings/image.regional.filter.d.ts +0 -2
  110. package/dist/serverGroup/configure/wizard/capacity/capacitySelector.directive.d.ts +0 -2
  111. package/dist/serverGroup/configure/wizard/healthSettings/ServerGroupHealthSettings.controller.d.ts +0 -2
  112. package/dist/serverGroup/configure/wizard/healthSettings/healthSettingsSelector.directive.d.ts +0 -2
  113. package/dist/serverGroup/configure/wizard/image/ServerGroupImageSettings.controller.d.ts +0 -2
  114. package/dist/serverGroup/configure/wizard/image/imageSettingsSelector.directive.d.ts +0 -2
  115. package/dist/serverGroup/configure/wizard/loadBalancers/ServerGroupLoadBalancers.controller.d.ts +0 -2
  116. package/dist/serverGroup/configure/wizard/loadBalancers/serverGroupLoadBalancersSelector.directive.d.ts +0 -2
  117. package/dist/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettings.controller.d.ts +0 -2
  118. package/dist/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettingsSelector.directive.d.ts +0 -2
  119. package/dist/serverGroup/configure/wizard/securityGroup/ServerGroupSecurityGroups.controller.d.ts +0 -2
  120. package/dist/serverGroup/configure/wizard/securityGroup/serverGroupSecurityGroupsSelector.directive.d.ts +0 -2
  121. package/dist/serverGroup/configure/wizard/tags/tagsSelector.directive.d.ts +0 -2
  122. package/dist/serverGroup/configure/wizard/zones/zoneSelector.directive.d.ts +0 -2
  123. package/dist/serverGroup/details/serverGroup.details.module.d.ts +0 -2
  124. package/dist/serverGroup/details/serverGroupDetails.azure.controller.d.ts +0 -2
  125. package/src/instance/details/instance.details.controller.js +0 -371
  126. package/src/instance/details/instance.details.controller.spec.js +0 -129
  127. package/src/instance/details/instanceDetails.html +0 -43
  128. package/src/loadBalancer/configure/advancedSettings.html +0 -95
  129. package/src/loadBalancer/configure/createLoadBalancer.controller.js +0 -275
  130. package/src/loadBalancer/configure/createLoadBalancer.controller.spec.js +0 -39
  131. package/src/loadBalancer/configure/createLoadBalancer.html +0 -25
  132. package/src/loadBalancer/configure/createLoadBalancerProperties.html +0 -128
  133. package/src/loadBalancer/configure/editLoadBalancer.html +0 -26
  134. package/src/loadBalancer/configure/healthCheck.html +0 -74
  135. package/src/loadBalancer/configure/listeners.html +0 -50
  136. package/src/loadBalancer/details/loadBalancerDetail.controller.js +0 -181
  137. package/src/loadBalancer/details/loadBalancerDetail.controller.spec.js +0 -39
  138. package/src/loadBalancer/details/loadBalancerDetail.html +0 -128
  139. package/src/pipeline/stages/bake/azureBakeStage.js +0 -276
  140. package/src/pipeline/stages/bake/bakeExecutionDetails.controller.js +0 -40
  141. package/src/pipeline/stages/bake/bakeExecutionDetails.html +0 -50
  142. package/src/pipeline/stages/bake/bakeStage.html +0 -187
  143. package/src/pipeline/stages/destroyAsg/azureDestroyAsgStage.js +0 -73
  144. package/src/pipeline/stages/destroyAsg/destroyAsgStage.html +0 -9
  145. package/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html +0 -1
  146. package/src/pipeline/stages/disableAsg/azureDisableAsgStage.js +0 -65
  147. package/src/pipeline/stages/disableAsg/disableAsgStage.html +0 -11
  148. package/src/pipeline/stages/disableAsg/disableAsgStepLabel.html +0 -1
  149. package/src/pipeline/stages/enableAsg/azureEnableAsgStage.js +0 -70
  150. package/src/pipeline/stages/enableAsg/enableAsgStage.html +0 -11
  151. package/src/pipeline/stages/enableAsg/enableAsgStepLabel.html +0 -1
  152. package/src/securityGroup/clone/cloneSecurityGroup.controller.js +0 -162
  153. package/src/securityGroup/clone/cloneSecurityGroup.html +0 -8
  154. package/src/securityGroup/configure/CreateSecurityGroup.controller.spec.js +0 -78
  155. package/src/securityGroup/configure/CreateSecurityGroupCtrl.js +0 -265
  156. package/src/securityGroup/configure/EditSecurityGroupCtrl.js +0 -222
  157. package/src/securityGroup/configure/configSecurityGroup.mixin.controller.js +0 -262
  158. package/src/securityGroup/configure/createSecurityGroup.html +0 -20
  159. package/src/securityGroup/configure/createSecurityGroupIngress.html +0 -75
  160. package/src/securityGroup/configure/createSecurityGroupProperties.html +0 -88
  161. package/src/securityGroup/configure/editSecurityGroup.html +0 -19
  162. package/src/securityGroup/details/securityGroupDetail.controller.js +0 -142
  163. package/src/securityGroup/details/securityGroupDetail.html +0 -78
  164. package/src/securityGroup/securityGroup.write.service.js +0 -57
  165. package/src/serverGroup/configure/serverGroup.configure.azure.module.js +0 -47
  166. package/src/serverGroup/configure/wizard/CloneServerGroup.azure.controller.js +0 -247
  167. package/src/serverGroup/configure/wizard/ServerGroupInstanceArchetype.controller.js +0 -35
  168. package/src/serverGroup/configure/wizard/ServerGroupInstanceType.controller.js +0 -15
  169. package/src/serverGroup/configure/wizard/advancedSettings/ServerGroupAdvancedSettings.controller.js +0 -25
  170. package/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.html +0 -5
  171. package/src/serverGroup/configure/wizard/advancedSettings/advancedSettingsSelector.directive.html +0 -138
  172. package/src/serverGroup/configure/wizard/advancedSettings/advancedSettingsSelector.directive.js +0 -43
  173. package/src/serverGroup/configure/wizard/basicSettings/ServerGroupBasicSettings.controller.js +0 -71
  174. package/src/serverGroup/configure/wizard/basicSettings/basicSettings.html +0 -131
  175. package/src/serverGroup/configure/wizard/basicSettings/image.regional.filter.js +0 -15
  176. package/src/serverGroup/configure/wizard/basicSettings/image.regional.filter.spec.js +0 -42
  177. package/src/serverGroup/configure/wizard/capacity/capacity.html +0 -5
  178. package/src/serverGroup/configure/wizard/capacity/capacitySelector.directive.html +0 -20
  179. package/src/serverGroup/configure/wizard/capacity/capacitySelector.directive.js +0 -21
  180. package/src/serverGroup/configure/wizard/capacity/zones.html +0 -5
  181. package/src/serverGroup/configure/wizard/healthSettings/ServerGroupHealthSettings.controller.js +0 -38
  182. package/src/serverGroup/configure/wizard/healthSettings/healthSettings.html +0 -5
  183. package/src/serverGroup/configure/wizard/healthSettings/healthSettingsSelector.directive.html +0 -32
  184. package/src/serverGroup/configure/wizard/healthSettings/healthSettingsSelector.directive.js +0 -22
  185. package/src/serverGroup/configure/wizard/image/ServerGroupImageSettings.controller.js +0 -34
  186. package/src/serverGroup/configure/wizard/image/imageSettings.html +0 -5
  187. package/src/serverGroup/configure/wizard/image/imageSettingsSelector.directive.html +0 -33
  188. package/src/serverGroup/configure/wizard/image/imageSettingsSelector.directive.js +0 -22
  189. package/src/serverGroup/configure/wizard/instanceArchetype.html +0 -33
  190. package/src/serverGroup/configure/wizard/instanceType/instanceType.html +0 -10
  191. package/src/serverGroup/configure/wizard/instanceType.html +0 -33
  192. package/src/serverGroup/configure/wizard/loadBalancers/ServerGroupLoadBalancers.controller.js +0 -137
  193. package/src/serverGroup/configure/wizard/loadBalancers/loadBalancers.html +0 -3
  194. package/src/serverGroup/configure/wizard/loadBalancers/serverGroupLoadBalancersSelector.directive.html +0 -41
  195. package/src/serverGroup/configure/wizard/loadBalancers/serverGroupLoadBalancersSelector.directive.js +0 -22
  196. package/src/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettings.controller.js +0 -44
  197. package/src/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettingsSelector.directive.html +0 -54
  198. package/src/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettingsSelector.directive.js +0 -19
  199. package/src/serverGroup/configure/wizard/networkSettings/networkSettings.html +0 -3
  200. package/src/serverGroup/configure/wizard/securityGroup/ServerGroupSecurityGroups.controller.js +0 -28
  201. package/src/serverGroup/configure/wizard/securityGroup/securityGroups.html +0 -3
  202. package/src/serverGroup/configure/wizard/securityGroup/serverGroupSecurityGroupsSelector.directive.html +0 -40
  203. package/src/serverGroup/configure/wizard/securityGroup/serverGroupSecurityGroupsSelector.directive.js +0 -38
  204. package/src/serverGroup/configure/wizard/serverGroupWizard.html +0 -61
  205. package/src/serverGroup/configure/wizard/tags/tags.html +0 -5
  206. package/src/serverGroup/configure/wizard/tags/tagsSelector.directive.html +0 -16
  207. package/src/serverGroup/configure/wizard/tags/tagsSelector.directive.js +0 -30
  208. package/src/serverGroup/configure/wizard/templateSelection.html +0 -9
  209. package/src/serverGroup/configure/wizard/zones/zoneSelector.directive.html +0 -21
  210. package/src/serverGroup/configure/wizard/zones/zoneSelector.directive.js +0 -28
  211. package/src/serverGroup/details/serverGroup.details.module.js +0 -11
  212. package/src/serverGroup/details/serverGroupDetails.azure.controller.js +0 -260
  213. package/src/serverGroup/details/serverGroupDetails.html +0 -98
  214. package/src/subnet/subnetSelectField.directive.html +0 -28
  215. package/src/subnet/subnetSelectField.directive.js +0 -35
@@ -0,0 +1,470 @@
1
+ import { UISref } from '@uirouter/react';
2
+ import { UIRouterReact } from '@uirouter/react';
3
+ import { mount, shallow } from 'enzyme';
4
+ import React from 'react';
5
+ import { act } from 'react-dom/test-utils';
6
+ import { BehaviorSubject } from 'rxjs';
7
+
8
+ import { CollapsibleSection, createDeckRuntime, DeckRuntimeContext, LoadBalancerReader } from '@spinnaker/core';
9
+
10
+ import {
11
+ AzureLoadBalancerDetailsSection,
12
+ AzureLoadBalancerFirewallsSection,
13
+ azureLoadBalancerDetailsSections,
14
+ loadAzureLoadBalancerDetails,
15
+ useAzureLoadBalancerDetails,
16
+ } from './azureLoadBalancerDetails';
17
+
18
+ describe('AzureLoadBalancerDetails', () => {
19
+ let runtime: ReturnType<typeof createDeckRuntime>;
20
+ const RuntimeWrapper = ({ children }: React.PropsWithChildren<{}>) => (
21
+ <DeckRuntimeContext.Provider value={runtime}>{children}</DeckRuntimeContext.Provider>
22
+ );
23
+ const mountWithRuntime = (component: React.ReactElement) => mount(component, { wrappingComponent: RuntimeWrapper });
24
+
25
+ beforeEach(() => {
26
+ runtime = createDeckRuntime(new UIRouterReact());
27
+ });
28
+
29
+ afterEach(() => {
30
+ runtime.dispose();
31
+ });
32
+
33
+ function buildApp(loadBalancers: any[]) {
34
+ return {
35
+ loadBalancers: {
36
+ data: loadBalancers,
37
+ },
38
+ } as any;
39
+ }
40
+
41
+ const params = {
42
+ name: 'fnord-frontend',
43
+ accountId: 'test-account',
44
+ region: 'westus',
45
+ provider: 'azure',
46
+ } as any;
47
+
48
+ function deferred<T>() {
49
+ let resolve: (value: T) => void;
50
+ const promise = new Promise<T>((promiseResolve) => {
51
+ resolve = promiseResolve;
52
+ });
53
+ return { promise, resolve: resolve! };
54
+ }
55
+
56
+ it('does not refetch details when a rerender recreates callbacks and route params', async () => {
57
+ const summary = {
58
+ account: 'test-account',
59
+ name: 'fnord-frontend',
60
+ provider: 'azure',
61
+ region: 'westus',
62
+ } as any;
63
+ const status$ = new BehaviorSubject({
64
+ status: 'FETCHED',
65
+ loaded: true,
66
+ lastRefresh: 1,
67
+ data: [summary],
68
+ });
69
+ const app = {
70
+ getDataSource: jasmine.createSpy('getDataSource').and.returnValue({
71
+ status$,
72
+ refresh: jasmine.createSpy('refresh'),
73
+ }),
74
+ } as any;
75
+ const getLoadBalancerDetails = spyOn(LoadBalancerReader.prototype, 'getLoadBalancerDetails').and.returnValue(
76
+ new Promise(() => undefined),
77
+ );
78
+
79
+ function TestComponent({ renderCount }: { renderCount: number }) {
80
+ useAzureLoadBalancerDetails({
81
+ app,
82
+ loadBalancerParams: { ...params },
83
+ autoClose: () => undefined,
84
+ } as any);
85
+ return <span>{renderCount}</span>;
86
+ }
87
+
88
+ let wrapper: any;
89
+ await act(async () => {
90
+ wrapper = mountWithRuntime(<TestComponent renderCount={0} />);
91
+ await new Promise((resolve) => setTimeout(resolve, 0));
92
+ });
93
+
94
+ await act(async () => {
95
+ wrapper.setProps({ renderCount: 1 });
96
+ await new Promise((resolve) => setTimeout(resolve, 0));
97
+ });
98
+
99
+ expect(getLoadBalancerDetails).toHaveBeenCalledTimes(1);
100
+ wrapper.unmount();
101
+ });
102
+
103
+ it('does not let an older route request overwrite newer load balancer details', async () => {
104
+ const oldSummary = { account: 'test-account', name: 'old-lb', provider: 'azure', region: 'westus' } as any;
105
+ const newSummary = { account: 'test-account', name: 'new-lb', provider: 'azure', region: 'westus' } as any;
106
+ const status$ = new BehaviorSubject({
107
+ status: 'FETCHED',
108
+ loaded: true,
109
+ lastRefresh: 1,
110
+ data: [oldSummary, newSummary],
111
+ });
112
+ const app = {
113
+ getDataSource: jasmine.createSpy('getDataSource').and.returnValue({
114
+ status$,
115
+ refresh: jasmine.createSpy('refresh'),
116
+ }),
117
+ } as any;
118
+ const oldRequest = deferred<any[]>();
119
+ const newRequest = deferred<any[]>();
120
+ const getLoadBalancerDetails = spyOn(LoadBalancerReader.prototype, 'getLoadBalancerDetails').and.returnValues(
121
+ oldRequest.promise,
122
+ newRequest.promise,
123
+ );
124
+
125
+ function TestComponent({ name }: { name: string }) {
126
+ const result = useAzureLoadBalancerDetails({
127
+ app,
128
+ loadBalancerParams: { ...params, name },
129
+ autoClose: () => undefined,
130
+ } as any);
131
+ return <span>{result.data?.name || ''}</span>;
132
+ }
133
+
134
+ let wrapper: any;
135
+ await act(async () => {
136
+ wrapper = mountWithRuntime(<TestComponent name="old-lb" />);
137
+ await Promise.resolve();
138
+ });
139
+ await act(async () => {
140
+ wrapper.setProps({ name: 'new-lb' });
141
+ await Promise.resolve();
142
+ });
143
+
144
+ expect(getLoadBalancerDetails).toHaveBeenCalledTimes(2);
145
+
146
+ await act(async () => {
147
+ newRequest.resolve([{ name: 'new-lb' }]);
148
+ await newRequest.promise;
149
+ });
150
+ wrapper.update();
151
+ expect(wrapper.text()).toBe('new-lb');
152
+
153
+ await act(async () => {
154
+ oldRequest.resolve([{ name: 'old-lb' }]);
155
+ await oldRequest.promise;
156
+ });
157
+ wrapper.update();
158
+ expect(wrapper.text()).toBe('new-lb');
159
+ wrapper.unmount();
160
+ });
161
+
162
+ it('stops loading when a data source error invalidates an active details request', async () => {
163
+ const summary = { account: 'test-account', name: 'fnord-frontend', provider: 'azure', region: 'westus' } as any;
164
+ const status$ = new BehaviorSubject({
165
+ status: 'FETCHED',
166
+ loaded: true,
167
+ lastRefresh: 1,
168
+ data: [summary],
169
+ error: null,
170
+ });
171
+ const app = {
172
+ getDataSource: jasmine.createSpy('getDataSource').and.returnValue({
173
+ status$,
174
+ refresh: jasmine.createSpy('refresh'),
175
+ }),
176
+ } as any;
177
+ const request = deferred<any[]>();
178
+ const getLoadBalancerDetails = spyOn(LoadBalancerReader.prototype, 'getLoadBalancerDetails').and.returnValue(
179
+ request.promise,
180
+ );
181
+
182
+ function TestComponent() {
183
+ const result = useAzureLoadBalancerDetails({
184
+ app,
185
+ loadBalancerParams: params,
186
+ autoClose: () => undefined,
187
+ } as any);
188
+ return <span data-loading={result.loading}>{result.data?.name || ''}</span>;
189
+ }
190
+
191
+ let wrapper: any;
192
+ await act(async () => {
193
+ wrapper = mountWithRuntime(<TestComponent />);
194
+ await new Promise((resolve) => setTimeout(resolve, 0));
195
+ });
196
+ wrapper.update();
197
+ expect(getLoadBalancerDetails).toHaveBeenCalledTimes(1);
198
+ expect(wrapper.find('span').prop('data-loading')).toBe(true);
199
+
200
+ await act(async () => {
201
+ status$.next({
202
+ status: 'ERROR',
203
+ loaded: true,
204
+ lastRefresh: 2,
205
+ data: [summary],
206
+ error: new Error('load balancer data source failed'),
207
+ });
208
+ await Promise.resolve();
209
+ });
210
+ wrapper.update();
211
+ expect(wrapper.find('span').prop('data-loading')).toBe(false);
212
+
213
+ await act(async () => {
214
+ request.resolve([{ name: 'fnord-frontend' }]);
215
+ await request.promise;
216
+ });
217
+ wrapper.update();
218
+ expect(wrapper.text()).toBe('');
219
+ wrapper.unmount();
220
+ });
221
+
222
+ it('does not update state when a details request resolves after unmount', async () => {
223
+ const summary = { account: 'test-account', name: 'fnord-frontend', provider: 'azure', region: 'westus' } as any;
224
+ const status$ = new BehaviorSubject({
225
+ status: 'FETCHED',
226
+ loaded: true,
227
+ lastRefresh: 1,
228
+ data: [summary],
229
+ });
230
+ const app = {
231
+ getDataSource: jasmine.createSpy('getDataSource').and.returnValue({
232
+ status$,
233
+ refresh: jasmine.createSpy('refresh'),
234
+ }),
235
+ } as any;
236
+ const request = deferred<any[]>();
237
+ spyOn(LoadBalancerReader.prototype, 'getLoadBalancerDetails').and.returnValue(request.promise);
238
+
239
+ function TestComponent() {
240
+ useAzureLoadBalancerDetails({ app, loadBalancerParams: params, autoClose: () => undefined } as any);
241
+ return null;
242
+ }
243
+
244
+ let wrapper: any;
245
+ await act(async () => {
246
+ wrapper = mountWithRuntime(<TestComponent />);
247
+ await Promise.resolve();
248
+ });
249
+ const consoleError = spyOn(console, 'error');
250
+ wrapper.unmount();
251
+
252
+ await act(async () => {
253
+ request.resolve([{ name: 'fnord-frontend' }]);
254
+ await request.promise;
255
+ });
256
+
257
+ const errors = consoleError.calls.allArgs().flat().map(String).join(' ');
258
+ expect(errors).not.toContain('state update on an unmounted component');
259
+ });
260
+
261
+ it('loads matching summary details and preserves legacy Azure detail fields', async () => {
262
+ const summary = {
263
+ account: 'test-account',
264
+ name: 'fnord-frontend',
265
+ provider: 'azure',
266
+ region: 'westus',
267
+ loadBalancerType: 'APPLICATION_GATEWAY',
268
+ serverGroups: [],
269
+ };
270
+ const otherSummary = { ...summary, account: 'other-account' };
271
+ const details = [
272
+ { name: 'other-lb', securityGroups: ['sg-other'] },
273
+ {
274
+ name: 'fnord-frontend',
275
+ createdTime: 1710000000000,
276
+ dnsName: 'fnord.example.com',
277
+ securityGroups: ['sg-2', 'sg-1'],
278
+ },
279
+ ];
280
+ const loadBalancerReader = {
281
+ getLoadBalancerDetails: jasmine.createSpy('getLoadBalancerDetails').and.returnValue(Promise.resolve(details)),
282
+ };
283
+ const securityGroupReader = {
284
+ getApplicationSecurityGroup: jasmine.createSpy('getApplicationSecurityGroup').and.callFake(
285
+ (_app: any, account: string, region: string, id: string) =>
286
+ ({
287
+ 'sg-1': { id: 'sg-1', name: 'z-firewall', account, region },
288
+ 'sg-2': { id: 'sg-2', name: 'a-firewall', account, region },
289
+ }[id]),
290
+ ),
291
+ };
292
+ const autoClose = jasmine.createSpy('autoClose');
293
+
294
+ const loadBalancer = await loadAzureLoadBalancerDetails({
295
+ app: buildApp([otherSummary, summary]),
296
+ loadBalancerParams: params,
297
+ loadBalancerReader: loadBalancerReader as any,
298
+ securityGroupReader: securityGroupReader as any,
299
+ autoClose,
300
+ });
301
+
302
+ expect(loadBalancerReader.getLoadBalancerDetails).toHaveBeenCalledWith(
303
+ 'azure',
304
+ 'test-account',
305
+ 'westus',
306
+ 'fnord-frontend',
307
+ );
308
+ expect(securityGroupReader.getApplicationSecurityGroup).toHaveBeenCalledWith(
309
+ jasmine.anything(),
310
+ 'test-account',
311
+ 'westus',
312
+ 'sg-2',
313
+ );
314
+ expect(autoClose).not.toHaveBeenCalled();
315
+ expect(loadBalancer).toBe(summary as any);
316
+ expect(loadBalancer.elb).toBe(details[1] as any);
317
+ expect(loadBalancer.account).toBe('test-account');
318
+ expect(loadBalancer.loadBalancerType).toBe('Application Gateway');
319
+ expect(loadBalancer.securityGroups).toEqual([
320
+ { id: 'sg-2', name: 'a-firewall', account: 'test-account', region: 'westus' },
321
+ { id: 'sg-1', name: 'z-firewall', account: 'test-account', region: 'westus' },
322
+ ] as any);
323
+ });
324
+
325
+ it('uses the load balancer and security group readers from the runtime provider', async () => {
326
+ const summary = {
327
+ account: 'test-account',
328
+ name: 'fnord-frontend',
329
+ provider: 'azure',
330
+ region: 'westus',
331
+ } as any;
332
+ const status$ = new BehaviorSubject({
333
+ status: 'FETCHED',
334
+ loaded: true,
335
+ lastRefresh: 1,
336
+ data: [summary],
337
+ });
338
+ const app = {
339
+ getDataSource: jasmine.createSpy('getDataSource').and.returnValue({
340
+ status$,
341
+ refresh: jasmine.createSpy('refresh'),
342
+ }),
343
+ loadBalancers: { data: [summary] },
344
+ } as any;
345
+ const getLoadBalancerDetails = spyOn(LoadBalancerReader.prototype, 'getLoadBalancerDetails').and.resolveTo([
346
+ { name: 'fnord-frontend', securityGroups: ['firewall-id'] },
347
+ ] as any);
348
+ const getApplicationSecurityGroup = spyOn(
349
+ runtime.services.securityGroupReader,
350
+ 'getApplicationSecurityGroup',
351
+ ).and.returnValue({ id: 'firewall-id', name: 'firewall' } as any);
352
+
353
+ function TestComponent() {
354
+ useAzureLoadBalancerDetails({ app, loadBalancerParams: params, autoClose: () => undefined } as any);
355
+ return null;
356
+ }
357
+
358
+ let wrapper: any;
359
+ await act(async () => {
360
+ wrapper = mountWithRuntime(<TestComponent />);
361
+ await Promise.resolve();
362
+ await Promise.resolve();
363
+ });
364
+
365
+ expect(getLoadBalancerDetails.calls.mostRecent().object).toBe(runtime.services.loadBalancerReader);
366
+ expect(getApplicationSecurityGroup).toHaveBeenCalledWith(app, 'test-account', 'westus', 'firewall-id');
367
+ wrapper.unmount();
368
+ });
369
+
370
+ it('closes the details panel when no matching summary exists', async () => {
371
+ const loadBalancerReader = {
372
+ getLoadBalancerDetails: jasmine.createSpy('getLoadBalancerDetails'),
373
+ };
374
+ const autoClose = jasmine.createSpy('autoClose');
375
+
376
+ const loadBalancer = await loadAzureLoadBalancerDetails({
377
+ app: buildApp([{ name: 'fnord-frontend', account: 'test-account', region: 'eastus', provider: 'azure' }]),
378
+ loadBalancerParams: params,
379
+ loadBalancerReader: loadBalancerReader as any,
380
+ securityGroupReader: {} as any,
381
+ autoClose,
382
+ });
383
+
384
+ expect(loadBalancer).toBeUndefined();
385
+ expect(autoClose).toHaveBeenCalled();
386
+ expect(loadBalancerReader.getLoadBalancerDetails).not.toHaveBeenCalled();
387
+ });
388
+
389
+ it('loads matching details from fresh load balancer data instead of stale app data', async () => {
390
+ const staleSummary = { name: 'fnord-frontend', account: 'test-account', region: 'eastus', provider: 'azure' };
391
+ const freshSummary = { name: 'fnord-frontend', account: 'test-account', region: 'westus', provider: 'azure' };
392
+ const details = [{ name: 'fnord-frontend' }];
393
+ const loadBalancerReader = {
394
+ getLoadBalancerDetails: jasmine.createSpy('getLoadBalancerDetails').and.returnValue(Promise.resolve(details)),
395
+ };
396
+ const autoClose = jasmine.createSpy('autoClose');
397
+
398
+ const loadBalancer = await loadAzureLoadBalancerDetails({
399
+ app: buildApp([staleSummary]),
400
+ loadBalancers: [freshSummary],
401
+ loadBalancerParams: params,
402
+ loadBalancerReader: loadBalancerReader as any,
403
+ securityGroupReader: {} as any,
404
+ autoClose,
405
+ } as any);
406
+
407
+ expect(loadBalancer).toBe(freshSummary as any);
408
+ expect(autoClose).not.toHaveBeenCalled();
409
+ expect(loadBalancerReader.getLoadBalancerDetails).toHaveBeenCalledWith(
410
+ 'azure',
411
+ 'test-account',
412
+ 'westus',
413
+ 'fnord-frontend',
414
+ );
415
+ });
416
+
417
+ it('registers health checks separately from listeners', () => {
418
+ expect(azureLoadBalancerDetailsSections.map((Section) => Section.name)).toEqual([
419
+ 'AzureLoadBalancerDetailsSection',
420
+ 'AzureLoadBalancerStatusSection',
421
+ 'AzureLoadBalancerListenersSection',
422
+ 'AzureLoadBalancerFirewallsSection',
423
+ 'AzureLoadBalancerHealthChecksSection',
424
+ ]);
425
+ });
426
+
427
+ it('renders legacy server group navigation links', () => {
428
+ const loadBalancer = {
429
+ account: 'test-account',
430
+ loadBalancerType: 'Azure Load Balancer',
431
+ region: 'westus',
432
+ serverGroups: [{ name: 'fnord-v001', account: 'test-account', region: 'westus', isDisabled: false }],
433
+ };
434
+
435
+ const wrapper = shallow(<AzureLoadBalancerDetailsSection loadBalancer={loadBalancer as any} />);
436
+ const sectionContent = shallow(<div>{wrapper.find(CollapsibleSection).prop('children')}</div>);
437
+ const link = sectionContent.find(UISref);
438
+
439
+ expect(link.prop('to')).toBe('^.serverGroup');
440
+ expect(link.prop('params')).toEqual({
441
+ region: 'westus',
442
+ accountId: 'test-account',
443
+ serverGroup: 'fnord-v001',
444
+ provider: 'azure',
445
+ });
446
+ });
447
+
448
+ it('renders legacy firewall navigation links', () => {
449
+ const loadBalancer = {
450
+ account: 'test-account',
451
+ provider: 'azure',
452
+ region: 'westus',
453
+ vpcId: 'vnet-1',
454
+ securityGroups: [{ id: 'sg-1', name: 'frontend-firewall' }],
455
+ };
456
+
457
+ const wrapper = shallow(<AzureLoadBalancerFirewallsSection loadBalancer={loadBalancer as any} />);
458
+ const sectionContent = shallow(<div>{wrapper.find(CollapsibleSection).prop('children')}</div>);
459
+ const link = sectionContent.find(UISref);
460
+
461
+ expect(link.prop('to')).toBe('^.firewallDetails');
462
+ expect(link.prop('params')).toEqual({
463
+ name: 'frontend-firewall',
464
+ accountId: 'test-account',
465
+ region: 'westus',
466
+ vpcId: 'vnet-1',
467
+ provider: 'azure',
468
+ });
469
+ });
470
+ });