"use strict"; const test = require("node:test"); const assert = require("node:assert/strict"); const fs = require("fs"); const os = require("os"); const path = require("path"); const { uploadLogFile, isUnauthorizedError } = require("../lib/logUpload"); function fakeLogger() { const infos = []; const warnings = []; const errors = []; return { infos, warnings, errors, info: (msg) => infos.push(msg), warn: (msg) => warnings.push(msg), error: (msg) => errors.push(msg), currentLogFilePath: () => fakeLogger.logFilePath, }; } function withTempLogFile(fn) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "log-upload-test-")); const logFilePath = path.join(dir, "backup-202607.log"); fs.writeFileSync(logFilePath, "dummy log"); return fn(logFilePath); } test("401なしで成功した場合はそのままアップロード成功ログを出す", async () => { await withTempLogFile(async (logFilePath) => { const logger = fakeLogger(); logger.currentLogFilePath = () => logFilePath; const uploadCalls = []; const drive = { resolveSharedDriveId: async () => { throw new Error("呼ばれないはず(sharedriveId指定済み)"); }, ensureFolder: async () => ({ fileId: "logs-folder" }), uploadFile: async (token) => { uploadCalls.push(token); return { fileId: "f1" }; }, }; const getUserAccessToken = async () => { throw new Error("呼ばれないはず(accessToken指定済み)"); }; await uploadLogFile( { accessToken: "token-1", sharedriveId: "drive-1", backupFolderId: "folder-1" }, { logger, drive, getUserAccessToken } ); assert.deepEqual(uploadCalls, ["token-1"]); assert.ok(logger.infos.some((m) => m.includes("ログファイルアップロード成功"))); assert.equal(logger.errors.length, 0); }); }); test("401エラー発生時はアクセストークンを再取得して1回だけリトライし成功する", async () => { await withTempLogFile(async (logFilePath) => { const logger = fakeLogger(); logger.currentLogFilePath = () => logFilePath; const uploadCalls = []; let refreshCalls = 0; const drive = { ensureFolder: async () => ({ fileId: "logs-folder" }), uploadFile: async (token) => { uploadCalls.push(token); if (uploadCalls.length === 1) { throw new Error("アップロード失敗: 401 UNAUTHORIZED"); } return { fileId: "f1" }; }, }; const getUserAccessToken = async () => { refreshCalls += 1; return `fresh-token-${refreshCalls}`; }; await uploadLogFile( { accessToken: "expired-token", sharedriveId: "drive-1", backupFolderId: "folder-1" }, { logger, drive, getUserAccessToken } ); assert.deepEqual(uploadCalls, ["expired-token", "fresh-token-1"]); assert.equal(refreshCalls, 1); assert.ok(logger.warnings.some((m) => m.includes("401"))); assert.ok(logger.infos.some((m) => m.includes("ログファイルアップロード成功"))); assert.equal(logger.errors.length, 0); }); }); test("401エラーでリトライも失敗した場合は失敗ログを出す", async () => { await withTempLogFile(async (logFilePath) => { const logger = fakeLogger(); logger.currentLogFilePath = () => logFilePath; const drive = { ensureFolder: async () => ({ fileId: "logs-folder" }), uploadFile: async () => { throw new Error("アップロード失敗: 401 UNAUTHORIZED"); }, }; const getUserAccessToken = async () => "fresh-token"; await uploadLogFile( { accessToken: "expired-token", sharedriveId: "drive-1", backupFolderId: "folder-1" }, { logger, drive, getUserAccessToken } ); assert.ok(logger.errors.some((m) => m.includes("リトライ後"))); }); }); test("401以外のエラーはリトライせず失敗ログを出す", async () => { await withTempLogFile(async (logFilePath) => { const logger = fakeLogger(); logger.currentLogFilePath = () => logFilePath; let refreshCalls = 0; const drive = { ensureFolder: async () => ({ fileId: "logs-folder" }), uploadFile: async () => { throw new Error("アップロード失敗: 500 Internal Server Error"); }, }; const getUserAccessToken = async () => { refreshCalls += 1; return "fresh-token"; }; await uploadLogFile( { accessToken: "token-1", sharedriveId: "drive-1", backupFolderId: "folder-1" }, { logger, drive, getUserAccessToken } ); assert.equal(refreshCalls, 0); assert.ok(logger.errors.some((m) => m.includes("ログファイルアップロード失敗"))); }); }); test("isUnauthorizedError: 401を含むメッセージを検知する", () => { assert.equal(isUnauthorizedError(new Error("失敗: 401 UNAUTHORIZED")), true); assert.equal(isUnauthorizedError(new Error("失敗: 500 Internal Server Error")), false); });