Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / services / log-service / log-service.test.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { LogService } from "./log-service";
6 import { ApiActions } from "services/api/api-actions";
7 import axios from "axios";
8 import { WebDAVRequestConfig } from "common/webdav";
9 import { LogEventType } from "models/log";
10
11 describe("LogService", () => {
12
13     let apiWebdavClient: any;
14     const axiosInstance = axios.create();
15     const actions: ApiActions = {
16         progressFn: (id: string, working: boolean) => {},
17         errorFn: (id: string, message: string) => {}
18     };
19
20     beforeEach(() => {
21         apiWebdavClient = {
22             delete: jest.fn(),
23             upload: jest.fn(),
24             mkdir: jest.fn(),
25             get: jest.fn(),
26             propfind: jest.fn(),
27         } as any;
28     });
29
30     it("lists log files using propfind on live logs api endpoint", async () => {
31         const logService = new LogService(axiosInstance, apiWebdavClient, actions);
32
33         // given
34         const containerRequest = {uuid: 'zzzzz-xvhdp-000000000000000', containerUuid: 'zzzzz-dz642-000000000000000'};
35         const xmlData = `<?xml version="1.0" encoding="UTF-8"?>
36             <D:multistatus xmlns:D="DAV:">
37                     <D:response>
38                             <D:href>/arvados/v1/container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/</D:href>
39                             <D:propstat>
40                                     <D:prop>
41                                             <D:resourcetype>
42                                                     <D:collection xmlns:D="DAV:" />
43                                             </D:resourcetype>
44                                             <D:getlastmodified>Tue, 15 Aug 2023 12:54:37 GMT</D:getlastmodified>
45                                             <D:displayname></D:displayname>
46                                             <D:supportedlock>
47                                                     <D:lockentry xmlns:D="DAV:">
48                                                             <D:lockscope>
49                                                                     <D:exclusive />
50                                                             </D:lockscope>
51                                                             <D:locktype>
52                                                                     <D:write />
53                                                             </D:locktype>
54                                                     </D:lockentry>
55                                             </D:supportedlock>
56                                     </D:prop>
57                                     <D:status>HTTP/1.1 200 OK</D:status>
58                             </D:propstat>
59                     </D:response>
60                     <D:response>
61                             <D:href>/arvados/v1/container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/stdout.txt</D:href>
62                             <D:propstat>
63                                     <D:prop>
64                                             <D:displayname>stdout.txt</D:displayname>
65                                             <D:getcontentlength>15</D:getcontentlength>
66                                             <D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
67                                             <D:getetag>"177b8fb161ff9f58f"</D:getetag>
68                                             <D:supportedlock>
69                                                     <D:lockentry xmlns:D="DAV:">
70                                                             <D:lockscope>
71                                                                     <D:exclusive />
72                                                             </D:lockscope>
73                                                             <D:locktype>
74                                                                     <D:write />
75                                                             </D:locktype>
76                                                     </D:lockentry>
77                                             </D:supportedlock>
78                                             <D:resourcetype></D:resourcetype>
79                                             <D:getlastmodified>Tue, 15 Aug 2023 12:54:37 GMT</D:getlastmodified>
80                                     </D:prop>
81                                     <D:status>HTTP/1.1 200 OK</D:status>
82                             </D:propstat>
83                     </D:response>
84                     <D:response>
85                             <D:href>/arvados/v1/container_requests/${containerRequest.uuid}/wrongpath.txt</D:href>
86                             <D:propstat>
87                                     <D:prop>
88                                             <D:displayname>wrongpath.txt</D:displayname>
89                                             <D:getcontentlength>15</D:getcontentlength>
90                                             <D:getcontenttype>text/plain; charset=utf-8</D:getcontenttype>
91                                             <D:getetag>"177b8fb161ff9f58f"</D:getetag>
92                                             <D:supportedlock>
93                                                     <D:lockentry xmlns:D="DAV:">
94                                                             <D:lockscope>
95                                                                     <D:exclusive />
96                                                             </D:lockscope>
97                                                             <D:locktype>
98                                                                     <D:write />
99                                                             </D:locktype>
100                                                     </D:lockentry>
101                                             </D:supportedlock>
102                                             <D:resourcetype></D:resourcetype>
103                                             <D:getlastmodified>Tue, 15 Aug 2023 12:54:37 GMT</D:getlastmodified>
104                                     </D:prop>
105                                     <D:status>HTTP/1.1 200 OK</D:status>
106                             </D:propstat>
107                     </D:response>
108             </D:multistatus>`;
109         const xmlDoc = (new DOMParser()).parseFromString(xmlData, "text/xml");
110         apiWebdavClient.propfind = jest.fn().mockReturnValue(Promise.resolve({responseXML: xmlDoc}));
111
112         // when
113         const logs = await logService.listLogFiles(containerRequest);
114
115         // then
116         expect(apiWebdavClient.propfind).toHaveBeenCalledWith(`container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}`);
117         expect(logs.length).toEqual(1);
118         expect(logs[0]).toHaveProperty('name', 'stdout.txt');
119         expect(logs[0]).toHaveProperty('type', 'file');
120     });
121
122     it("requests log file contents with correct range request", async () => {
123         const logService = new LogService(axiosInstance, apiWebdavClient, actions);
124
125         // given
126         const containerRequest = {uuid: 'zzzzz-xvhdp-000000000000000', containerUuid: 'zzzzz-dz642-000000000000000'};
127         const fileRecord = {name: `stdout.txt`};
128         const fileContents = `Line 1\nLine 2\nLine 3`;
129         apiWebdavClient.get = jest.fn().mockImplementation((path: string, options: WebDAVRequestConfig) => {
130             const matches = /bytes=([0-9]+)-([0-9]+)/.exec(options.headers?.Range || '');
131             if (matches?.length === 3) {
132                 return Promise.resolve({responseText: fileContents.substring(Number(matches[1]), Number(matches[2]) + 1)})
133             }
134             return Promise.reject();
135         });
136
137         // when
138         let result = await logService.getLogFileContents(containerRequest, fileRecord, 0, 3);
139         // then
140         expect(apiWebdavClient.get).toHaveBeenCalledWith(
141             `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
142             {headers: {Range: `bytes=0-3`}}
143         );
144         expect(result.logType).toEqual(LogEventType.STDOUT);
145         expect(result.contents).toEqual(['Line']);
146
147         // when
148         result = await logService.getLogFileContents(containerRequest, fileRecord, 0, 10);
149         // then
150         expect(apiWebdavClient.get).toHaveBeenCalledWith(
151             `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
152             {headers: {Range: `bytes=0-10`}}
153         );
154         expect(result.logType).toEqual(LogEventType.STDOUT);
155         expect(result.contents).toEqual(['Line 1', 'Line']);
156
157         // when
158         result = await logService.getLogFileContents(containerRequest, fileRecord, 6, 14);
159         // then
160         expect(apiWebdavClient.get).toHaveBeenCalledWith(
161             `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
162             {headers: {Range: `bytes=6-14`}}
163         );
164         expect(result.logType).toEqual(LogEventType.STDOUT);
165         expect(result.contents).toEqual(['', 'Line 2', 'L']);
166     });
167
168 });