mirror of
https://github.com/louislam/uptime-kuma.git
synced 2026-01-31 11:03:11 +08:00
chore: made code more robust to undefined expiry (#6625)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -222,6 +222,11 @@ class DomainExpiry extends BeanModel {
|
||||
log.debug("domain_expiry", "No notification, no need to send domain notification");
|
||||
return;
|
||||
}
|
||||
// sanity check if expiry date is valid before calculating days remaining. Should not happen and likely indicates a bug in the code.
|
||||
if (!domain.expiry || isNaN(new Date(domain.expiry).getTime())) {
|
||||
log.warn("domain_expiry", `No valid expiry date passed to sendNotifications for ${name} (expiry: ${domain.expiry}), skipping notification`);
|
||||
return;
|
||||
}
|
||||
|
||||
const daysRemaining = getDaysRemaining(new Date(), domain.expiry);
|
||||
const lastSent = domain.lastExpiryNotificationSent;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
process.env.UPTIME_KUMA_HIDE_LOG = [ "info_db", "info_server" ].join(",");
|
||||
|
||||
const { describe, test } = require("node:test");
|
||||
const { describe, test, mock } = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const DomainExpiry = require("../../server/model/domain_expiry");
|
||||
const mockWebhook = require("./notification-providers/mock-webhook");
|
||||
@@ -70,4 +70,109 @@ describe("Domain Expiry", () => {
|
||||
await testDb.destroy();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
test("sendNotifications() handles domain with null expiry without sending NaN", async () => {
|
||||
// Regression test for bug: "Domain name will expire in NaN days"
|
||||
// Mock forMonitor to return a bean with null expiry
|
||||
const mockDomain = {
|
||||
domain: "test-null.com",
|
||||
expiry: null,
|
||||
lastExpiryNotificationSent: null
|
||||
};
|
||||
|
||||
mock.method(DomainExpiry, "forMonitor", async () => mockDomain);
|
||||
|
||||
try {
|
||||
const hook = {
|
||||
"port": 3012,
|
||||
"url": "should-not-be-called-null"
|
||||
};
|
||||
|
||||
const monTest = {
|
||||
type: "http",
|
||||
url: "https://test-null.com",
|
||||
domainExpiryNotification: true
|
||||
};
|
||||
|
||||
const notif = {
|
||||
name: "TestNullExpiry",
|
||||
config: JSON.stringify({
|
||||
type: "webhook",
|
||||
httpMethod: "post",
|
||||
webhookContentType: "json",
|
||||
webhookURL: `http://127.0.0.1:${hook.port}/${hook.url}`
|
||||
})
|
||||
};
|
||||
|
||||
// Race between sendNotifications and mockWebhook timeout
|
||||
// If webhook is called, we fail. If it times out, we pass.
|
||||
const result = await Promise.race([
|
||||
DomainExpiry.sendNotifications(monTest, [ notif ]),
|
||||
mockWebhook(hook.port, hook.url, 500).then(() => {
|
||||
throw new Error("Webhook was called but should not have been for null expiry");
|
||||
}).catch((e) => {
|
||||
if (e.reason === "Timeout") {
|
||||
return "timeout"; // Expected - webhook was not called
|
||||
}
|
||||
throw e;
|
||||
})
|
||||
]);
|
||||
|
||||
assert.ok(result === undefined || result === "timeout", "Should not send notification for null expiry");
|
||||
} finally {
|
||||
mock.restoreAll();
|
||||
}
|
||||
});
|
||||
|
||||
test("sendNotifications() handles domain with undefined expiry without sending NaN", async () => {
|
||||
try {
|
||||
// Mock forMonitor to return a bean with undefined expiry (newly created bean scenario)
|
||||
const mockDomain = {
|
||||
domain: "test-undefined.com",
|
||||
expiry: undefined,
|
||||
lastExpiryNotificationSent: null
|
||||
};
|
||||
|
||||
mock.method(DomainExpiry, "forMonitor", async () => mockDomain);
|
||||
|
||||
const hook = {
|
||||
"port": 3013,
|
||||
"url": "should-not-be-called-undefined"
|
||||
};
|
||||
|
||||
const monTest = {
|
||||
type: "http",
|
||||
url: "https://test-undefined.com",
|
||||
domainExpiryNotification: true
|
||||
};
|
||||
|
||||
const notif = {
|
||||
name: "TestUndefinedExpiry",
|
||||
config: JSON.stringify({
|
||||
type: "webhook",
|
||||
httpMethod: "post",
|
||||
webhookContentType: "json",
|
||||
webhookURL: `http://127.0.0.1:${hook.port}/${hook.url}`
|
||||
})
|
||||
};
|
||||
|
||||
// Race between sendNotifications and mockWebhook timeout
|
||||
// If webhook is called, we fail. If it times out, we pass.
|
||||
const result = await Promise.race([
|
||||
DomainExpiry.sendNotifications(monTest, [ notif ]),
|
||||
mockWebhook(hook.port, hook.url, 500).then(() => {
|
||||
throw new Error("Webhook was called but should not have been for undefined expiry");
|
||||
}).catch((e) => {
|
||||
if (e.reason === "Timeout") {
|
||||
return "timeout"; // Expected - webhook was not called
|
||||
}
|
||||
throw e;
|
||||
})
|
||||
]);
|
||||
|
||||
assert.ok(result === undefined || result === "timeout", "Should not send notification for undefined expiry");
|
||||
} finally {
|
||||
mock.restoreAll();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user