"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 { buildMimeMessage, sendBackupReportMail, sendTestMail } = require("../lib/mailNotify"); 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), }; } test("buildMimeMessage: 添付ファイル無しでも本文がbase64で埋め込まれる", () => { const raw = buildMimeMessage({ from: "a@example.com", to: "b@example.com", subject: "件名", bodyText: "本文テキスト", }); assert.match(raw, /^From: a@example\.com/); assert.match(raw, /Subject: =\?UTF-8\?B\?/); assert.match(raw, /Content-Type: multipart\/mixed; boundary="/); assert.doesNotMatch(raw, /Content-Disposition: attachment/); }); test("buildMimeMessage: 添付ファイルがあればbase64化して埋め込む", () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "mail-notify-test-")); const attachmentPath = path.join(dir, "backup-202607.log"); fs.writeFileSync(attachmentPath, "ログ本文"); const raw = buildMimeMessage({ from: "a@example.com", to: "b@example.com", subject: "件名", bodyText: "本文", attachmentPath, }); assert.match(raw, /Content-Disposition: attachment; filename="backup-202607\.log"/); assert.match(raw, new RegExp(Buffer.from("ログ本文", "utf8").toString("base64"))); }); test("sendBackupReportMail: MAIL_FROM/MAIL_TO未設定ならスキップする", async () => { delete process.env.MAIL_FROM; delete process.env.MAIL_TO; const logger = fakeLogger(); let sendMailCalls = 0; await sendBackupReportMail( { success: true, hostname: "host", database: "db", elapsedSec: "1.0", logFilePath: "/dev/null" }, { logger, sendMail: async () => (sendMailCalls += 1) } ); assert.equal(sendMailCalls, 0); assert.ok(logger.warnings.some((m) => m.includes("スキップ"))); }); test("sendBackupReportMail: 成功時は成功ラベル入りの件名で送信する", async () => { process.env.MAIL_FROM = "from@example.com"; process.env.MAIL_TO = "to@example.com"; const logger = fakeLogger(); const calls = []; await sendBackupReportMail( { success: true, hostname: "nextoffice2", database: "Implem.Pleasanter", elapsedSec: "12.3", logFilePath: "/dev/null" }, { logger, sendMail: async (opts) => calls.push(opts) } ); assert.equal(calls.length, 1); assert.match(calls[0].subject, /バックアップ成功/); assert.match(calls[0].bodyText, /結果: 成功/); assert.ok(logger.infos.some((m) => m.includes("メール通知送信成功"))); delete process.env.MAIL_FROM; delete process.env.MAIL_TO; }); test("sendBackupReportMail: 失敗時は失敗ラベルとエラー内容を含める", async () => { process.env.MAIL_FROM = "from@example.com"; process.env.MAIL_TO = "to@example.com"; const logger = fakeLogger(); const calls = []; await sendBackupReportMail( { success: false, hostname: "nextoffice2", database: "Implem.Pleasanter", elapsedSec: "5.0", errorMessage: "401 UNAUTHORIZED", logFilePath: "/dev/null", }, { logger, sendMail: async (opts) => calls.push(opts) } ); assert.match(calls[0].subject, /バックアップ失敗/); assert.match(calls[0].bodyText, /エラー内容: 401 UNAUTHORIZED/); delete process.env.MAIL_FROM; delete process.env.MAIL_TO; }); test("sendBackupReportMail: 送信自体が失敗してもERRORログに記録し例外は投げない", async () => { process.env.MAIL_FROM = "from@example.com"; process.env.MAIL_TO = "to@example.com"; const logger = fakeLogger(); await sendBackupReportMail( { success: true, hostname: "h", database: "d", elapsedSec: "1.0", logFilePath: "/dev/null" }, { logger, sendMail: async () => { throw new Error("sendmail異常終了 (code=1)"); }, } ); assert.ok(logger.errors.some((m) => m.includes("メール通知送信失敗"))); delete process.env.MAIL_FROM; delete process.env.MAIL_TO; }); test("sendTestMail: MAIL_FROM/MAIL_TO未設定なら例外を投げる", async () => { delete process.env.MAIL_FROM; delete process.env.MAIL_TO; const logger = fakeLogger(); await assert.rejects(() => sendTestMail({ logger, sendMail: async () => {} }), /MAIL_FROM\/MAIL_TO/); }); test("sendTestMail: 設定済みならsendMailを1回呼ぶ", async () => { process.env.MAIL_FROM = "from@example.com"; process.env.MAIL_TO = "to@example.com"; const logger = fakeLogger(); const calls = []; await sendTestMail({ logger, sendMail: async (opts) => calls.push(opts) }); assert.equal(calls.length, 1); assert.equal(calls[0].from, "from@example.com"); assert.equal(calls[0].to, "to@example.com"); assert.match(calls[0].subject, /テスト送信/); delete process.env.MAIL_FROM; delete process.env.MAIL_TO; });