19231: Add smaller page sizes (10 and 20 items) to load faster
[arvados-workbench2.git] / src / services / common-service / common-resource-service.test.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { CommonResourceService } from "./common-resource-service";
6 import axios, { AxiosInstance } from "axios";
7 import MockAdapter from "axios-mock-adapter";
8 import { Resource } from "models/resource";
9 import { ApiActions } from "services/api/api-actions";
10
11 const actions: ApiActions = {
12     progressFn: (id: string, working: boolean) => {},
13     errorFn: (id: string, message: string) => {}
14 };
15
16 export const mockResourceService = <R extends Resource, C extends CommonResourceService<R>>(
17     Service: new (client: AxiosInstance, actions: ApiActions) => C) => {
18         const axiosInstance = axios.create();
19         const service = new Service(axiosInstance, actions);
20         Object.keys(service).map(key => service[key] = jest.fn());
21         return service;
22     };
23
24 describe("CommonResourceService", () => {
25     let axiosInstance: AxiosInstance;
26     let axiosMock: MockAdapter;
27
28     beforeEach(() => {
29         axiosInstance = axios.create();
30         axiosMock = new MockAdapter(axiosInstance);
31     });
32
33     it("#create", async () => {
34         axiosMock
35             .onPost("/resources")
36             .reply(200, { owner_uuid: "ownerUuidValue" });
37
38         const commonResourceService = new CommonResourceService(axiosInstance, "resources", actions);
39         const resource = await commonResourceService.create({ ownerUuid: "ownerUuidValue" });
40         expect(resource).toEqual({ ownerUuid: "ownerUuidValue" });
41     });
42
43     it("#create maps request params to snake case", async () => {
44         axiosInstance.post = jest.fn(() => Promise.resolve({data: {}}));
45         const commonResourceService = new CommonResourceService(axiosInstance, "resources", actions);
46         await commonResourceService.create({ ownerUuid: "ownerUuidValue" });
47         expect(axiosInstance.post).toHaveBeenCalledWith("/resources", {resource: {owner_uuid: "ownerUuidValue"}});
48     });
49
50     it("#create ignores fields listed as readonly", async () => {
51         axiosInstance.post = jest.fn(() => Promise.resolve({data: {}}));
52         const commonResourceService = new CommonResourceService(axiosInstance, "resources", actions);
53         // UUID fields are read-only on all resources.
54         await commonResourceService.create({ uuid: "this should be ignored", ownerUuid: "ownerUuidValue" });
55         expect(axiosInstance.post).toHaveBeenCalledWith("/resources", {resource: {owner_uuid: "ownerUuidValue"}});
56     });
57
58     it("#update ignores fields listed as readonly", async () => {
59         axiosInstance.put = jest.fn(() => Promise.resolve({data: {}}));
60         const commonResourceService = new CommonResourceService(axiosInstance, "resources", actions);
61         // UUID fields are read-only on all resources.
62         await commonResourceService.update('resource-uuid', { uuid: "this should be ignored", ownerUuid: "ownerUuidValue" });
63         expect(axiosInstance.put).toHaveBeenCalledWith("/resources/resource-uuid", {resource:  {owner_uuid: "ownerUuidValue"}});
64     });
65
66     it("#delete", async () => {
67         axiosMock
68             .onDelete("/resources/uuid")
69             .reply(200, { deleted_at: "now" });
70
71         const commonResourceService = new CommonResourceService(axiosInstance, "resources", actions);
72         const resource = await commonResourceService.delete("uuid");
73         expect(resource).toEqual({ deletedAt: "now" });
74     });
75
76     it("#get", async () => {
77         axiosMock
78             .onGet("/resources/uuid")
79             .reply(200, {
80                 modified_at: "now",
81                 properties: {
82                     responsible_owner_uuid: "another_owner"
83                 }
84             });
85
86         const commonResourceService = new CommonResourceService(axiosInstance, "resources", actions);
87         const resource = await commonResourceService.get("uuid");
88         // Only first level keys are mapped to camel case
89         expect(resource).toEqual({
90             modifiedAt: "now",
91             properties: {
92                 responsible_owner_uuid: "another_owner"
93             }
94         });
95     });
96
97     it("#list", async () => {
98         axiosMock
99             .onGet("/resources")
100             .reply(200, {
101                 kind: "kind",
102                 offset: 2,
103                 limit: 10,
104                 items: [{
105                     modified_at: "now",
106                     properties: {
107                         is_active: true
108                     }
109                 }],
110                 items_available: 20
111             });
112
113         const commonResourceService = new CommonResourceService(axiosInstance, "resources", actions);
114         const resource = await commonResourceService.list({ limit: 10, offset: 1 });
115         // First level keys are mapped to camel case inside "items" arrays
116         expect(resource).toEqual({
117             kind: "kind",
118             offset: 2,
119             limit: 10,
120             items: [{
121                 modifiedAt: "now",
122                 properties: {
123                     is_active: true
124                 }
125             }],
126             itemsAvailable: 20
127         });
128     });
129
130     it("#list using POST when query string is too big", async () => {
131         axiosMock
132             .onAny("/resources")
133             .reply(200);
134         const tooBig = 'x'.repeat(1500);
135         const commonResourceService = new CommonResourceService(axiosInstance, "resources", actions);
136         await commonResourceService.list({ filters: tooBig });
137         expect(axiosMock.history.get.length).toBe(0);
138         expect(axiosMock.history.post.length).toBe(1);
139         expect(axiosMock.history.post[0].data.get('filters')).toBe(`[${tooBig}]`);
140         expect(axiosMock.history.post[0].params._method).toBe('GET');
141     });
142
143     it("#list using GET when query string is not too big", async () => {
144         axiosMock
145             .onAny("/resources")
146             .reply(200);
147         const notTooBig = 'x'.repeat(1480);
148         const commonResourceService = new CommonResourceService(axiosInstance, "resources", actions);
149         await commonResourceService.list({ filters: notTooBig });
150         expect(axiosMock.history.post.length).toBe(0);
151         expect(axiosMock.history.get.length).toBe(1);
152         expect(axiosMock.history.get[0].params.filters).toBe(`[${notTooBig}]`);
153     });
154 });