resourcePickerData.test.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import {
  2. createARGResourcesResponse,
  3. createMockARGResourceGroupsResponse,
  4. createMockARGSubscriptionResponse,
  5. } from '../__mocks__/argResourcePickerResponse';
  6. import { createMockInstanceSetttings } from '../__mocks__/instanceSettings';
  7. import { AzureGraphResponse } from '../types';
  8. import ResourcePickerData from './resourcePickerData';
  9. const createResourcePickerData = (responses: AzureGraphResponse[]) => {
  10. const instanceSettings = createMockInstanceSetttings();
  11. const resourcePickerData = new ResourcePickerData(instanceSettings);
  12. const postResource = jest.fn();
  13. responses.forEach((res) => {
  14. postResource.mockResolvedValueOnce(res);
  15. });
  16. resourcePickerData.postResource = postResource;
  17. return { resourcePickerData, postResource };
  18. };
  19. describe('AzureMonitor resourcePickerData', () => {
  20. describe('getSubscriptions', () => {
  21. it('makes 1 call to ARG with the correct path and query arguments', async () => {
  22. const mockResponse = createMockARGSubscriptionResponse();
  23. const { resourcePickerData, postResource } = createResourcePickerData([mockResponse]);
  24. await resourcePickerData.getSubscriptions();
  25. expect(postResource).toBeCalledTimes(1);
  26. const firstCall = postResource.mock.calls[0];
  27. const [path, postBody] = firstCall;
  28. expect(path).toEqual('resourcegraph/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01');
  29. expect(postBody.query).toContain("where type == 'microsoft.resources/subscriptions'");
  30. });
  31. it('returns formatted subscriptions', async () => {
  32. const mockResponse = createMockARGSubscriptionResponse();
  33. const { resourcePickerData } = createResourcePickerData([mockResponse]);
  34. const subscriptions = await resourcePickerData.getSubscriptions();
  35. expect(subscriptions.length).toEqual(6);
  36. expect(subscriptions[0]).toEqual({
  37. id: '1',
  38. name: 'Primary Subscription',
  39. type: 'Subscription',
  40. typeLabel: 'Subscription',
  41. uri: '/subscriptions/1',
  42. children: [],
  43. });
  44. });
  45. it('makes multiple requests when arg returns a skipToken and passes the right skipToken to each subsequent call', async () => {
  46. const response1 = {
  47. ...createMockARGSubscriptionResponse(),
  48. $skipToken: 'skipfirst100',
  49. };
  50. const response2 = createMockARGSubscriptionResponse();
  51. const { resourcePickerData, postResource } = createResourcePickerData([response1, response2]);
  52. await resourcePickerData.getSubscriptions();
  53. expect(postResource).toHaveBeenCalledTimes(2);
  54. const secondCall = postResource.mock.calls[1];
  55. const [_, postBody] = secondCall;
  56. expect(postBody.options.$skipToken).toEqual('skipfirst100');
  57. });
  58. it('returns a concatenates a formatted array of subscriptions when there are multiple pages from arg', async () => {
  59. const response1 = {
  60. ...createMockARGSubscriptionResponse(),
  61. $skipToken: 'skipfirst100',
  62. };
  63. const response2 = createMockARGSubscriptionResponse();
  64. const { resourcePickerData } = createResourcePickerData([response1, response2]);
  65. const subscriptions = await resourcePickerData.getSubscriptions();
  66. expect(subscriptions.length).toEqual(12);
  67. expect(subscriptions[0]).toEqual({
  68. id: '1',
  69. name: 'Primary Subscription',
  70. type: 'Subscription',
  71. typeLabel: 'Subscription',
  72. uri: '/subscriptions/1',
  73. children: [],
  74. });
  75. });
  76. it('throws an error if it does not recieve data from arg', async () => {
  77. const mockResponse = { data: [] };
  78. const { resourcePickerData } = createResourcePickerData([mockResponse]);
  79. try {
  80. await resourcePickerData.getSubscriptions();
  81. throw Error('expected getSubscriptions to fail but it succeeded');
  82. } catch (err) {
  83. expect(err.message).toEqual('No subscriptions were found');
  84. }
  85. });
  86. });
  87. describe('getResourceGroupsBySubscriptionId', () => {
  88. it('makes 1 call to ARG with the correct path and query arguments', async () => {
  89. const mockResponse = createMockARGResourceGroupsResponse();
  90. const { resourcePickerData, postResource } = createResourcePickerData([mockResponse]);
  91. await resourcePickerData.getResourceGroupsBySubscriptionId('123', 'logs');
  92. expect(postResource).toBeCalledTimes(1);
  93. const firstCall = postResource.mock.calls[0];
  94. const [path, postBody] = firstCall;
  95. expect(path).toEqual('resourcegraph/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01');
  96. expect(postBody.query).toContain("type == 'microsoft.resources/subscriptions/resourcegroups'");
  97. expect(postBody.query).toContain("where subscriptionId == '123'");
  98. });
  99. it('returns formatted resourceGroups', async () => {
  100. const mockResponse = createMockARGResourceGroupsResponse();
  101. const { resourcePickerData } = createResourcePickerData([mockResponse]);
  102. const resourceGroups = await resourcePickerData.getResourceGroupsBySubscriptionId('123', 'logs');
  103. expect(resourceGroups.length).toEqual(6);
  104. expect(resourceGroups[0]).toEqual({
  105. id: 'prod',
  106. name: 'Production',
  107. type: 'ResourceGroup',
  108. typeLabel: 'Resource Group',
  109. uri: '/subscriptions/abc-123/resourceGroups/prod',
  110. children: [],
  111. });
  112. });
  113. it('makes multiple requests when it is returned a skip token', async () => {
  114. const response1 = {
  115. ...createMockARGResourceGroupsResponse(),
  116. $skipToken: 'skipfirst100',
  117. };
  118. const response2 = createMockARGResourceGroupsResponse();
  119. const { resourcePickerData, postResource } = createResourcePickerData([response1, response2]);
  120. await resourcePickerData.getResourceGroupsBySubscriptionId('123', 'logs');
  121. expect(postResource).toHaveBeenCalledTimes(2);
  122. const secondCall = postResource.mock.calls[1];
  123. const [_, postBody] = secondCall;
  124. expect(postBody.options.$skipToken).toEqual('skipfirst100');
  125. });
  126. it('returns a concatonized and formatted array of resourceGroups when there are multiple pages', async () => {
  127. const response1 = {
  128. ...createMockARGResourceGroupsResponse(),
  129. $skipToken: 'skipfirst100',
  130. };
  131. const response2 = createMockARGResourceGroupsResponse();
  132. const { resourcePickerData } = createResourcePickerData([response1, response2]);
  133. const resourceGroups = await resourcePickerData.getResourceGroupsBySubscriptionId('123', 'logs');
  134. expect(resourceGroups.length).toEqual(12);
  135. expect(resourceGroups[0]).toEqual({
  136. id: 'prod',
  137. name: 'Production',
  138. type: 'ResourceGroup',
  139. typeLabel: 'Resource Group',
  140. uri: '/subscriptions/abc-123/resourceGroups/prod',
  141. children: [],
  142. });
  143. });
  144. it('throws an error if it recieves data with a malformed uri', async () => {
  145. const mockResponse = {
  146. data: [
  147. {
  148. resourceGroupURI: '/a-differently-formatted/uri/than/the/type/we/planned/to/parse',
  149. resourceGroupName: 'Production',
  150. },
  151. ],
  152. };
  153. const { resourcePickerData } = createResourcePickerData([mockResponse]);
  154. try {
  155. await resourcePickerData.getResourceGroupsBySubscriptionId('123', 'logs');
  156. throw Error('expected getResourceGroupsBySubscriptionId to fail but it succeeded');
  157. } catch (err) {
  158. expect(err.message).toEqual('unable to fetch resource groups');
  159. }
  160. });
  161. it('filters by metric specific resources', async () => {
  162. const mockResponse = createMockARGResourceGroupsResponse();
  163. const { resourcePickerData, postResource } = createResourcePickerData([mockResponse]);
  164. await resourcePickerData.getResourceGroupsBySubscriptionId('123', 'metrics');
  165. expect(postResource).toBeCalledTimes(1);
  166. const firstCall = postResource.mock.calls[0];
  167. const [_, postBody] = firstCall;
  168. expect(postBody.query).toContain('wandisco.fusion/migrators');
  169. });
  170. });
  171. describe('getResourcesForResourceGroup', () => {
  172. it('makes 1 call to ARG with the correct path and query arguments', async () => {
  173. const mockResponse = createARGResourcesResponse();
  174. const { resourcePickerData, postResource } = createResourcePickerData([mockResponse]);
  175. await resourcePickerData.getResourcesForResourceGroup('dev', 'logs');
  176. expect(postResource).toBeCalledTimes(1);
  177. const firstCall = postResource.mock.calls[0];
  178. const [path, postBody] = firstCall;
  179. expect(path).toEqual('resourcegraph/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01');
  180. expect(postBody.query).toContain('resources');
  181. expect(postBody.query).toContain('where id hasprefix "dev"');
  182. });
  183. it('returns formatted resources', async () => {
  184. const mockResponse = createARGResourcesResponse();
  185. const { resourcePickerData } = createResourcePickerData([mockResponse]);
  186. const resources = await resourcePickerData.getResourcesForResourceGroup('dev', 'logs');
  187. expect(resources.length).toEqual(4);
  188. expect(resources[0]).toEqual({
  189. id: 'web-server',
  190. name: 'web-server',
  191. type: 'Resource',
  192. location: 'North Europe',
  193. resourceGroupName: 'dev',
  194. typeLabel: 'Microsoft.Compute/virtualMachines',
  195. uri: '/subscriptions/def-456/resourceGroups/dev/providers/Microsoft.Compute/virtualMachines/web-server',
  196. });
  197. });
  198. it('throws an error if it recieves data with a malformed uri', async () => {
  199. const mockResponse = {
  200. data: [
  201. {
  202. id: '/a-differently-formatted/uri/than/the/type/we/planned/to/parse',
  203. name: 'web-server',
  204. type: 'Microsoft.Compute/virtualMachines',
  205. resourceGroup: 'dev',
  206. subscriptionId: 'def-456',
  207. location: 'northeurope',
  208. },
  209. ],
  210. };
  211. const { resourcePickerData } = createResourcePickerData([mockResponse]);
  212. try {
  213. await resourcePickerData.getResourcesForResourceGroup('dev', 'logs');
  214. throw Error('expected getResourcesForResourceGroup to fail but it succeeded');
  215. } catch (err) {
  216. expect(err.message).toEqual('unable to fetch resource details');
  217. }
  218. });
  219. it('should filter metrics resources', async () => {
  220. const mockResponse = createARGResourcesResponse();
  221. const { resourcePickerData, postResource } = createResourcePickerData([mockResponse]);
  222. await resourcePickerData.getResourcesForResourceGroup('dev', 'metrics');
  223. expect(postResource).toBeCalledTimes(1);
  224. const firstCall = postResource.mock.calls[0];
  225. const [_, postBody] = firstCall;
  226. expect(postBody.query).toContain('wandisco.fusion/migrators');
  227. });
  228. });
  229. describe('search', () => {
  230. it('makes requests for metrics searches', async () => {
  231. const mockResponse = {
  232. data: [
  233. {
  234. id: '/subscriptions/subId/resourceGroups/rgName/providers/Microsoft.Compute/virtualMachines/vmname',
  235. name: 'vmName',
  236. type: 'microsoft.compute/virtualmachines',
  237. resourceGroup: 'rgName',
  238. subscriptionId: 'subId',
  239. location: 'northeurope',
  240. },
  241. ],
  242. };
  243. const { resourcePickerData, postResource } = createResourcePickerData([mockResponse]);
  244. const formattedResults = await resourcePickerData.search('vmname', 'metrics');
  245. expect(postResource).toBeCalledTimes(1);
  246. const firstCall = postResource.mock.calls[0];
  247. const [_, postBody] = firstCall;
  248. expect(postBody.query).not.toContain('union resourcecontainers');
  249. expect(postBody.query).toContain('where id contains "vmname"');
  250. expect(formattedResults[0]).toEqual({
  251. id: 'vmname',
  252. name: 'vmName',
  253. type: 'Resource',
  254. location: 'North Europe',
  255. resourceGroupName: 'rgName',
  256. typeLabel: 'Virtual machines',
  257. uri: '/subscriptions/subId/resourceGroups/rgName/providers/Microsoft.Compute/virtualMachines/vmname',
  258. });
  259. });
  260. it('makes requests for logs searches', async () => {
  261. const mockResponse = {
  262. data: [
  263. {
  264. id: '/subscriptions/subId/resourceGroups/rgName',
  265. name: 'rgName',
  266. type: 'microsoft.resources/subscriptions/resourcegroups',
  267. resourceGroup: 'rgName',
  268. subscriptionId: 'subId',
  269. location: 'northeurope',
  270. },
  271. ],
  272. };
  273. const { resourcePickerData, postResource } = createResourcePickerData([mockResponse]);
  274. const formattedResults = await resourcePickerData.search('rgName', 'logs');
  275. expect(postResource).toBeCalledTimes(1);
  276. const firstCall = postResource.mock.calls[0];
  277. const [_, postBody] = firstCall;
  278. expect(postBody.query).toContain('union resourcecontainers');
  279. expect(formattedResults[0]).toEqual({
  280. id: 'rgName',
  281. name: 'rgName',
  282. type: 'ResourceGroup',
  283. location: 'North Europe',
  284. resourceGroupName: 'rgName',
  285. typeLabel: 'Resource groups',
  286. uri: '/subscriptions/subId/resourceGroups/rgName',
  287. });
  288. });
  289. it('throws an error if it receives data it can not parse', async () => {
  290. const mockResponse = {
  291. data: [
  292. {
  293. id: '/a-differently-formatted/uri/than/the/type/we/planned/to/parse',
  294. name: 'web-server',
  295. type: 'Microsoft.Compute/virtualMachines',
  296. resourceGroup: 'dev',
  297. subscriptionId: 'def-456',
  298. location: 'northeurope',
  299. },
  300. ],
  301. };
  302. const { resourcePickerData } = createResourcePickerData([mockResponse]);
  303. try {
  304. await resourcePickerData.search('dev', 'logs');
  305. throw Error('expected search test to fail but it succeeded');
  306. } catch (err) {
  307. expect(err.message).toEqual('unable to fetch resource details');
  308. }
  309. });
  310. });
  311. });