mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-07-25 21:24:50 +02:00
99 lines
2.8 KiB
TypeScript
99 lines
2.8 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
import { describe, expect, test, vi } from 'vitest';
|
|
import { ApiCallService } from '@/server/api/ApiCallService.js';
|
|
import Logger from '@/logger.js';
|
|
import { envOption } from '@/env.js';
|
|
import { logManager } from '@/logging/logging-runtime.js';
|
|
import { PrettyConsoleBackend } from '@/logging/PrettyConsoleBackend.js';
|
|
import type { LogBackend } from '@/logging/LogBackend.js';
|
|
import type { LogRecord } from '@/logging/types.js';
|
|
|
|
/** API失敗ログを確認するための最小Fastify応答を作成します。 */
|
|
function createReply() {
|
|
return {
|
|
code: vi.fn(),
|
|
header: vi.fn(),
|
|
send: vi.fn(),
|
|
};
|
|
}
|
|
|
|
/** APIサービスの依存関係を最小限の仮実装へ差し替えます。 */
|
|
function createService() {
|
|
const authenticateService = {
|
|
authenticate: vi.fn().mockResolvedValue([null, null]),
|
|
};
|
|
const telemetryService = {
|
|
startSpan: vi.fn((_name: string, callback: () => unknown) => callback()),
|
|
captureMessage: vi.fn(),
|
|
};
|
|
const apiLoggerService = { logger: new Logger('api') };
|
|
|
|
const service = new ApiCallService(
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
authenticateService as never,
|
|
{} as never,
|
|
{} as never,
|
|
apiLoggerService as never,
|
|
telemetryService as never,
|
|
);
|
|
return { service, telemetryService };
|
|
}
|
|
|
|
describe('ApiCallService structured error logging', () => {
|
|
test('redacts API credentials and serializes the endpoint error', async () => {
|
|
const write = vi.fn<LogBackend['write']>();
|
|
logManager.setBackend({ write });
|
|
const previousQuiet = envOption.quiet;
|
|
envOption.quiet = false;
|
|
const { service, telemetryService } = createService();
|
|
try {
|
|
const reply = createReply();
|
|
const endpoint = {
|
|
name: 'notes/show',
|
|
meta: {},
|
|
params: {},
|
|
exec: vi.fn().mockRejectedValue(new TypeError('broken endpoint')),
|
|
};
|
|
const request = {
|
|
method: 'POST',
|
|
body: {
|
|
i: 'native-token',
|
|
password: 'password',
|
|
options: { visible: true },
|
|
},
|
|
query: {},
|
|
headers: {},
|
|
ip: '127.0.0.1',
|
|
};
|
|
|
|
await service.handleRequest(endpoint as never, request as never, reply as never);
|
|
|
|
const record = write.mock.calls[0][0] as LogRecord;
|
|
expect(record).toMatchObject({
|
|
eventName: 'api.endpoint.failed',
|
|
attributes: {
|
|
'api.endpoint': 'notes/show',
|
|
'api.params': {
|
|
i: '[REDACTED]',
|
|
password: '[REDACTED]',
|
|
options: { visible: true },
|
|
},
|
|
},
|
|
error: { type: 'TypeError', message: 'broken endpoint' },
|
|
});
|
|
expect(record.attributes?.['error.id']).toEqual(expect.any(String));
|
|
expect(telemetryService.captureMessage.mock.calls[0][1].extra).not.toHaveProperty('ps');
|
|
} finally {
|
|
service.dispose();
|
|
envOption.quiet = previousQuiet;
|
|
logManager.setBackend(new PrettyConsoleBackend({ output: () => undefined }));
|
|
}
|
|
});
|
|
});
|