1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
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";
11 describe("LogService", () => {
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) => {}
30 it("lists log files using propfind on live logs api endpoint", async () => {
31 const logService = new LogService(axiosInstance, apiWebdavClient, actions);
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:">
38 <D:href>/arvados/v1/container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/</D:href>
42 <D:collection xmlns:D="DAV:" />
44 <D:getlastmodified>Tue, 15 Aug 2023 12:54:37 GMT</D:getlastmodified>
45 <D:displayname></D:displayname>
47 <D:lockentry xmlns:D="DAV:">
57 <D:status>HTTP/1.1 200 OK</D:status>
61 <D:href>/arvados/v1/container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/stdout.txt</D:href>
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>
69 <D:lockentry xmlns:D="DAV:">
78 <D:resourcetype></D:resourcetype>
79 <D:getlastmodified>Tue, 15 Aug 2023 12:54:37 GMT</D:getlastmodified>
81 <D:status>HTTP/1.1 200 OK</D:status>
85 <D:href>/arvados/v1/container_requests/${containerRequest.uuid}/wrongpath.txt</D:href>
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>
93 <D:lockentry xmlns:D="DAV:">
102 <D:resourcetype></D:resourcetype>
103 <D:getlastmodified>Tue, 15 Aug 2023 12:54:37 GMT</D:getlastmodified>
105 <D:status>HTTP/1.1 200 OK</D:status>
109 const xmlDoc = (new DOMParser()).parseFromString(xmlData, "text/xml");
110 apiWebdavClient.propfind = jest.fn().mockReturnValue(Promise.resolve({responseXML: xmlDoc}));
113 const logs = await logService.listLogFiles(containerRequest);
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');
122 it("requests log file contents with correct range request", async () => {
123 const logService = new LogService(axiosInstance, apiWebdavClient, actions);
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)})
134 return Promise.reject();
138 let result = await logService.getLogFileContents(containerRequest, fileRecord, 0, 3);
140 expect(apiWebdavClient.get).toHaveBeenCalledWith(
141 `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
142 {headers: {Range: `bytes=0-3`}}
144 expect(result.logType).toEqual(LogEventType.STDOUT);
145 expect(result.contents).toEqual(['Line']);
148 result = await logService.getLogFileContents(containerRequest, fileRecord, 0, 10);
150 expect(apiWebdavClient.get).toHaveBeenCalledWith(
151 `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
152 {headers: {Range: `bytes=0-10`}}
154 expect(result.logType).toEqual(LogEventType.STDOUT);
155 expect(result.contents).toEqual(['Line 1', 'Line']);
158 result = await logService.getLogFileContents(containerRequest, fileRecord, 6, 14);
160 expect(apiWebdavClient.get).toHaveBeenCalledWith(
161 `container_requests/${containerRequest.uuid}/log/${containerRequest.containerUuid}/${fileRecord.name}`,
162 {headers: {Range: `bytes=6-14`}}
164 expect(result.logType).toEqual(LogEventType.STDOUT);
165 expect(result.contents).toEqual(['', 'Line 2', 'L']);