blob: 13aa18542befc6b119f2411cb58d9154bc21db97 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
import { addVerboseName, deleteVerboseName, fetchLog } from './log';
import { setCsrfToken } from './csrf';
const snapshot = {
level: 'INFO',
mode: 'ASYNC',
verboseNames: [],
auditNames: ['query'],
logPath: '/logs/fe.warn.log',
showingLastBytes: 12,
contents: 'warning\n',
contentError: null,
};
function legacySuccess() {
return new Response(JSON.stringify({ code: 0, data: {
LogConfiguration: { Level: 'INFO', Mode: 'ASYNC', VerboseNames: '', AuditNames: 'query' },
LogContents: { logPath: '/logs/fe.warn.log', showingLast: '12 bytes of log', log: '<pre>warning</br></pre>' },
} }), {
status: 200,
headers: { 'Content-Type': 'application/json', 'X-Request-Id': 'req-log' },
});
}
function mutationSuccess() {
return new Response(JSON.stringify({ code: 0, data: {} }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
describe('Log UI API', () => {
afterEach(() => {
setCsrfToken(null);
vi.restoreAllMocks();
});
it('reads the structured log snapshot', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(legacySuccess());
await expect(fetchLog()).resolves.toEqual(snapshot);
expect(fetchSpy).toHaveBeenCalledWith('/rest/v1/log', expect.objectContaining({ method: 'GET' }));
});
it.each([
['add', addVerboseName],
['delete', deleteVerboseName],
] as const)('%s reuses the legacy form endpoint', async (label, operation) => {
setCsrfToken('csrf-log');
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mutationSuccess());
await operation('org.apache.doris.M10Probe');
const init = fetchSpy.mock.calls[0]?.[1];
const headers = new Headers(init?.headers);
expect(fetchSpy.mock.calls[0]?.[0]).toBe('/rest/v1/log');
expect(init?.method).toBe('POST');
expect(headers.get('Content-Type')).toContain('application/x-www-form-urlencoded');
expect(headers.get('X-Doris-CSRF-Token')).toBe('csrf-log');
expect(init?.body).toBe(`${label === 'add' ? 'add_verbose' : 'del_verbose'}=org.apache.doris.M10Probe`);
});
it('rejects invalid names before calling the legacy endpoint', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch');
await expect(addVerboseName('not a logger name')).rejects.toMatchObject({ code: 'UI_LOG_VERBOSE_INVALID' });
expect(fetchSpy).not.toHaveBeenCalled();
});
});