16848: Adds unit test.
[arvados-workbench2.git] / src / store / auth / auth-action.test.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { getNewExtraToken, initAuth } from "./auth-action";
6 import { API_TOKEN_KEY } from "~/services/auth-service/auth-service";
7
8 import 'jest-localstorage-mock';
9 import { ServiceRepository, createServices } from "~/services/services";
10 import { configureStore, RootStore } from "../store";
11 import { createBrowserHistory } from "history";
12 import { Config, mockConfig } from '~/common/config';
13 import { ApiActions } from "~/services/api/api-actions";
14 import { ACCOUNT_LINK_STATUS_KEY } from '~/services/link-account-service/link-account-service';
15 import Axios from "axios";
16 import MockAdapter from "axios-mock-adapter";
17 import { ImportMock } from 'ts-mock-imports';
18 import * as servicesModule from "~/services/services";
19 import { SessionStatus } from "~/models/session";
20
21 describe('auth-actions', () => {
22     const axiosInst = Axios.create({ headers: {} });
23     const axiosMock = new MockAdapter(axiosInst);
24
25     let store: RootStore;
26     let services: ServiceRepository;
27     const config: any = {};
28     const actions: ApiActions = {
29         progressFn: (id: string, working: boolean) => { },
30         errorFn: (id: string, message: string) => { }
31     };
32     let importMocks: any[];
33
34     beforeEach(() => {
35         axiosMock.reset();
36         services = createServices(mockConfig({}), actions, axiosInst);
37         store = configureStore(createBrowserHistory(), services, config);
38         localStorage.clear();
39         importMocks = [];
40     });
41
42     afterEach(() => {
43         importMocks.map(m => m.restore());
44     });
45
46     it('creates an extra token', async () => {
47         axiosMock
48             .onGet("/users/current")
49             .reply(200, {
50                 email: "test@test.com",
51                 first_name: "John",
52                 last_name: "Doe",
53                 uuid: "zzzzz-tpzed-abcefg",
54                 owner_uuid: "ownerUuid",
55                 is_admin: false,
56                 is_active: true,
57                 username: "jdoe",
58                 prefs: {}
59             })
60             .onGet("/api_client_authorizations/current")
61             .reply(200, {
62                 expires_at: "2140-01-01T00:00:00.000Z",
63                 api_token: 'extra token',
64             })
65             .onPost("/api_client_authorizations")
66             .replyOnce(200, {
67                 uuid: 'zzzzz-gj3su-xxxxxxxxxx',
68                 apiToken: 'extra token',
69             })
70             .onPost("/api_client_authorizations")
71             .reply(200, {
72                 uuid: 'zzzzz-gj3su-xxxxxxxxxx',
73                 apiToken: 'extra additional token',
74             });
75
76         importMocks.push(ImportMock.mockFunction(servicesModule, 'createServices', services));
77         sessionStorage.setItem(ACCOUNT_LINK_STATUS_KEY, "0");
78         localStorage.setItem(API_TOKEN_KEY, "token");
79
80         const config: any = {
81             rootUrl: "https://zzzzz.arvadosapi.com",
82             uuidPrefix: "zzzzz",
83             remoteHosts: { },
84             apiRevision: 12345678,
85             clusterConfig: {
86                 Login: { LoginCluster: "" },
87             },
88         };
89
90         // Set up auth, confirm that no extra token was requested
91         await store.dispatch(initAuth(config))
92         expect(store.getState().auth.apiToken).toBeDefined();
93         expect(store.getState().auth.extraApiToken).toBeUndefined();
94
95         // Ask for an extra token
96         await store.dispatch(getNewExtraToken());
97         expect(store.getState().auth.apiToken).toBeDefined();
98         expect(store.getState().auth.extraApiToken).toBeDefined();
99         const extraToken = store.getState().auth.extraApiToken;
100
101         // Ask for a cached extra token
102         await store.dispatch(getNewExtraToken(true));
103         expect(store.getState().auth.extraApiToken).toBe(extraToken);
104
105         // Ask for a new extra token, should make a second api request
106         await store.dispatch(getNewExtraToken(false));
107         expect(store.getState().auth.extraApiToken).toBeDefined();
108         expect(store.getState().auth.extraApiToken).not.toBe(extraToken);
109     });
110
111     it('should initialise state with user and api token from local storage', (done) => {
112         axiosMock
113             .onGet("/users/current")
114             .reply(200, {
115                 email: "test@test.com",
116                 first_name: "John",
117                 last_name: "Doe",
118                 uuid: "zzzzz-tpzed-abcefg",
119                 owner_uuid: "ownerUuid",
120                 is_admin: false,
121                 is_active: true,
122                 username: "jdoe",
123                 prefs: {}
124             })
125             .onGet("/api_client_authorizations/current")
126             .reply(200, {
127                 expires_at: "2140-01-01T00:00:00.000Z"
128             })
129             .onGet("https://xc59z.arvadosapi.com/discovery/v1/apis/arvados/v1/rest")
130             .reply(200, {
131                 baseUrl: "https://xc59z.arvadosapi.com/arvados/v1",
132                 keepWebServiceUrl: "",
133                 keepWebInlineServiceUrl: "",
134                 remoteHosts: {},
135                 rootUrl: "https://xc59z.arvadosapi.com",
136                 uuidPrefix: "xc59z",
137                 websocketUrl: "",
138                 workbenchUrl: "",
139                 workbench2Url: "",
140                 revision: 12345678
141             });
142
143         importMocks.push(ImportMock.mockFunction(servicesModule, 'createServices', services));
144
145         // Only test the case when a link account operation is not being cancelled
146         sessionStorage.setItem(ACCOUNT_LINK_STATUS_KEY, "0");
147         localStorage.setItem(API_TOKEN_KEY, "token");
148
149         const config: any = {
150             rootUrl: "https://zzzzz.arvadosapi.com",
151             uuidPrefix: "zzzzz",
152             remoteHosts: { xc59z: "xc59z.arvadosapi.com" },
153             apiRevision: 12345678,
154             clusterConfig: {
155                 Login: { LoginCluster: "" },
156             },
157         };
158
159         store.dispatch(initAuth(config));
160
161         store.subscribe(() => {
162             const auth = store.getState().auth;
163             if (auth.apiToken === "token" &&
164                 auth.sessions.length === 2 &&
165                 auth.sessions[0].status === SessionStatus.VALIDATED &&
166                 auth.sessions[1].status === SessionStatus.VALIDATED
167             ) {
168                 try {
169                     expect(auth).toEqual({
170                         apiToken: "token",
171                         apiTokenExpiration: new Date("2140-01-01T00:00:00.000Z"),
172                         config: {
173                             apiRevision: 12345678,
174                             clusterConfig: {
175                                 Login: {
176                                     LoginCluster: "",
177                                 },
178                             },
179                             remoteHosts: {
180                                 "xc59z": "xc59z.arvadosapi.com",
181                             },
182                             rootUrl: "https://zzzzz.arvadosapi.com",
183                             uuidPrefix: "zzzzz",
184                         },
185                         sshKeys: [],
186                         extraApiToken: undefined,
187                         extraApiTokenExpiration: undefined,
188                         homeCluster: "zzzzz",
189                         localCluster: "zzzzz",
190                         loginCluster: undefined,
191                         remoteHostsConfig: {
192                             "zzzzz": {
193                                 "apiRevision": 12345678,
194                                 "clusterConfig": {
195                                     "Login": {
196                                         "LoginCluster": "",
197                                     },
198                                 },
199                                 "remoteHosts": {
200                                     "xc59z": "xc59z.arvadosapi.com",
201                                 },
202                                 "rootUrl": "https://zzzzz.arvadosapi.com",
203                                 "uuidPrefix": "zzzzz",
204                             },
205                             "xc59z": mockConfig({
206                                 apiRevision: 12345678,
207                                 baseUrl: "https://xc59z.arvadosapi.com/arvados/v1",
208                                 rootUrl: "https://xc59z.arvadosapi.com",
209                                 uuidPrefix: "xc59z"
210                             })
211                         },
212                         remoteHosts: {
213                             zzzzz: "zzzzz.arvadosapi.com",
214                             xc59z: "xc59z.arvadosapi.com"
215                         },
216                         sessions: [{
217                             "active": true,
218                             "baseUrl": undefined,
219                             "clusterId": "zzzzz",
220                             "email": "test@test.com",
221                             "loggedIn": true,
222                             "remoteHost": "https://zzzzz.arvadosapi.com",
223                             "status": 2,
224                             "token": "token",
225                             "name": "John Doe",
226                             "apiRevision": 12345678,
227                             "uuid": "zzzzz-tpzed-abcefg",
228                             "userIsActive": true
229                         }, {
230                             "active": false,
231                             "baseUrl": "",
232                             "clusterId": "xc59z",
233                             "email": "",
234                             "loggedIn": false,
235                             "remoteHost": "xc59z.arvadosapi.com",
236                             "status": 2,
237                             "token": "",
238                             "name": "",
239                             "uuid": "",
240                             "apiRevision": 0,
241                         }],
242                         user: {
243                             email: "test@test.com",
244                             firstName: "John",
245                             lastName: "Doe",
246                             uuid: "zzzzz-tpzed-abcefg",
247                             ownerUuid: "ownerUuid",
248                             username: "jdoe",
249                             prefs: { profile: {} },
250                             isAdmin: false,
251                             isActive: true
252                         }
253                     });
254                     done();
255                 } catch (e) {
256                     fail(e);
257                 }
258             }
259         });
260     });
261
262
263     // TODO: Add remaining action tests
264     /*
265        it('should fire external url to login', () => {
266        const initialState = undefined;
267        window.location.assign = jest.fn();
268        reducer(initialState, authActions.LOGIN());
269        expect(window.location.assign).toBeCalledWith(
270        `/login?return_to=${window.location.protocol}//${window.location.host}/token`
271        );
272        });
273
274        it('should fire external url to logout', () => {
275        const initialState = undefined;
276        window.location.assign = jest.fn();
277        reducer(initialState, authActions.LOGOUT());
278        expect(window.location.assign).toBeCalledWith(
279        `/logout?return_to=${location.protocol}//${location.host}`
280        );
281        });
282      */
283 });