1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-07-25 21:24:50 +02:00
Files
misskey/packages/backend/test/unit/server/ApiCallService.ts
おさむのひと 0849dc7cdd enhance: 構造化ログAPIと正規化 (#17719)
* enhance: 構造化ログAPIと正規化

* fix changelog

* fixed copilot review

* fixed coderabbit review
2026-07-15 20:14:19 +09:00

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 }));
}
});
});