@@ -342,14 +338,20 @@
- {{ $t("Date Created") }}: {{ $root.datetime(incident.createdDate) }} ({{
- dateFromNow(incident.createdDate)
- }})
+ {{
+ $t("dateCreatedAtFromNow", {
+ date: $root.datetime(incident.createdDate),
+ fromNow: dateFromNow(incident.createdDate),
+ })
+ }}
- {{ $t("Last Updated") }}: {{ $root.datetime(incident.lastUpdatedDate) }} ({{
- dateFromNow(incident.lastUpdatedDate)
- }})
+ {{
+ $t("lastUpdatedAtFromNow", {
+ date: $root.datetime(incident.lastUpdatedDate),
+ fromNow: dateFromNow(incident.lastUpdatedDate),
+ })
+ }}
@@ -572,9 +574,9 @@
-
{{ $t("Last Updated") }}: {{ lastUpdateTimeDisplay }}
+
{{ $t("lastUpdatedAt", { date: lastUpdateTimeDisplay }) }}
- {{ $tc("statusPageRefreshIn", [updateCountdownText]) }}
+ {{ $t("statusPageRefreshIn", [updateCountdownText]) }}
diff --git a/test/backend-test/monitors/test-postgres.js b/test/backend-test/monitors/test-postgres.js
index a633d9806..3a408b5a3 100644
--- a/test/backend-test/monitors/test-postgres.js
+++ b/test/backend-test/monitors/test-postgres.js
@@ -49,5 +49,203 @@ describe(
await assert.rejects(postgresMonitor.check(monitor, heartbeat, {}), regex);
});
+
+ test("check() sets status to UP when custom query returns single value", async () => {
+ // The default timeout of 30 seconds might not be enough for the container to start
+ const postgresContainer = await new PostgreSqlContainer("postgres:latest")
+ .withStartupTimeout(60000)
+ .start();
+
+ const postgresMonitor = new PostgresMonitorType();
+ const monitor = {
+ databaseConnectionString: postgresContainer.getConnectionUri(),
+ databaseQuery: "SELECT 42",
+ conditions: "[]",
+ };
+
+ const heartbeat = {
+ msg: "",
+ status: PENDING,
+ };
+
+ try {
+ await postgresMonitor.check(monitor, heartbeat, {});
+ assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`);
+ } finally {
+ await postgresContainer.stop();
+ }
+ });
+ test("check() sets status to UP when custom query result meets condition", async () => {
+ const postgresContainer = await new PostgreSqlContainer("postgres:latest")
+ .withStartupTimeout(60000)
+ .start();
+
+ const postgresMonitor = new PostgresMonitorType();
+ const monitor = {
+ databaseConnectionString: postgresContainer.getConnectionUri(),
+ databaseQuery: "SELECT 42 AS value",
+ conditions: JSON.stringify([
+ {
+ type: "expression",
+ andOr: "and",
+ variable: "result",
+ operator: "equals",
+ value: "42",
+ },
+ ]),
+ };
+
+ const heartbeat = {
+ msg: "",
+ status: PENDING,
+ };
+
+ try {
+ await postgresMonitor.check(monitor, heartbeat, {});
+ assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`);
+ } finally {
+ await postgresContainer.stop();
+ }
+ });
+ test("check() rejects when custom query result does not meet condition", async () => {
+ const postgresContainer = await new PostgreSqlContainer("postgres:latest")
+ .withStartupTimeout(60000)
+ .start();
+
+ const postgresMonitor = new PostgresMonitorType();
+ const monitor = {
+ databaseConnectionString: postgresContainer.getConnectionUri(),
+ databaseQuery: "SELECT 99 AS value",
+ conditions: JSON.stringify([
+ {
+ type: "expression",
+ andOr: "and",
+ variable: "result",
+ operator: "equals",
+ value: "42",
+ },
+ ]),
+ };
+
+ const heartbeat = {
+ msg: "",
+ status: PENDING,
+ };
+
+ try {
+ await assert.rejects(
+ postgresMonitor.check(monitor, heartbeat, {}),
+ new Error("Query result did not meet the specified conditions (99)")
+ );
+ assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`);
+ } finally {
+ await postgresContainer.stop();
+ }
+ });
+ test("check() rejects when query returns no results with conditions", async () => {
+ const postgresContainer = await new PostgreSqlContainer("postgres:latest")
+ .withStartupTimeout(60000)
+ .start();
+
+ const postgresMonitor = new PostgresMonitorType();
+ const monitor = {
+ databaseConnectionString: postgresContainer.getConnectionUri(),
+ databaseQuery: "SELECT 1 WHERE 1 = 0",
+ conditions: JSON.stringify([
+ {
+ type: "expression",
+ andOr: "and",
+ variable: "result",
+ operator: "equals",
+ value: "1",
+ },
+ ]),
+ };
+
+ const heartbeat = {
+ msg: "",
+ status: PENDING,
+ };
+
+ try {
+ await assert.rejects(
+ postgresMonitor.check(monitor, heartbeat, {}),
+ new Error("Database connection/query failed: Query returned no results")
+ );
+ assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`);
+ } finally {
+ await postgresContainer.stop();
+ }
+ });
+ test("check() rejects when query returns multiple rows with conditions", async () => {
+ const postgresContainer = await new PostgreSqlContainer("postgres:latest")
+ .withStartupTimeout(60000)
+ .start();
+
+ const postgresMonitor = new PostgresMonitorType();
+ const monitor = {
+ databaseConnectionString: postgresContainer.getConnectionUri(),
+ databaseQuery: "SELECT 1 UNION ALL SELECT 2",
+ conditions: JSON.stringify([
+ {
+ type: "expression",
+ andOr: "and",
+ variable: "result",
+ operator: "equals",
+ value: "1",
+ },
+ ]),
+ };
+
+ const heartbeat = {
+ msg: "",
+ status: PENDING,
+ };
+
+ try {
+ await assert.rejects(
+ postgresMonitor.check(monitor, heartbeat, {}),
+ new Error("Database connection/query failed: Multiple values were found, expected only one value")
+ );
+ assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`);
+ } finally {
+ await postgresContainer.stop();
+ }
+ });
+ test("check() rejects when query returns multiple columns with conditions", async () => {
+ const postgresContainer = await new PostgreSqlContainer("postgres:latest")
+ .withStartupTimeout(60000)
+ .start();
+
+ const postgresMonitor = new PostgresMonitorType();
+ const monitor = {
+ databaseConnectionString: postgresContainer.getConnectionUri(),
+ databaseQuery: "SELECT 1 AS col1, 2 AS col2",
+ conditions: JSON.stringify([
+ {
+ type: "expression",
+ andOr: "and",
+ variable: "result",
+ operator: "equals",
+ value: "1",
+ },
+ ]),
+ };
+
+ const heartbeat = {
+ msg: "",
+ status: PENDING,
+ };
+
+ try {
+ await assert.rejects(
+ postgresMonitor.check(monitor, heartbeat, {}),
+ new Error("Database connection/query failed: Multiple columns were found, expected only one value")
+ );
+ assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`);
+ } finally {
+ await postgresContainer.stop();
+ }
+ });
}
);
diff --git a/test/backend-test/test-domain.js b/test/backend-test/test-domain.js
index e1c95cd5f..c00d94e24 100644
--- a/test/backend-test/test-domain.js
+++ b/test/backend-test/test-domain.js
@@ -96,58 +96,26 @@ describe("Domain Expiry", () => {
});
describe("Domain Parsing", () => {
- test("throws error for invalid domain (no domain part)", async () => {
+ test("throws error for IP address (isIp check)", async () => {
const monitor = {
type: "http",
- url: "https://",
+ url: "https://127.0.0.1",
domainExpiryNotification: true,
};
await assert.rejects(
async () => await DomainExpiry.checkSupport(monitor),
(error) => {
assert.strictEqual(error.constructor.name, "TranslatableError");
- assert.strictEqual(error.message, "domain_expiry_unsupported_invalid_domain");
+ assert.strictEqual(error.message, "domain_expiry_unsupported_is_ip");
return true;
}
);
});
- test("throws error for IPv4 address instead of domain", async () => {
+ test("throws error for too short suffix(example.a)", async () => {
const monitor = {
type: "http",
- url: "https://192.168.1.1",
- domainExpiryNotification: true,
- };
- await assert.rejects(
- async () => await DomainExpiry.checkSupport(monitor),
- (error) => {
- assert.strictEqual(error.constructor.name, "TranslatableError");
- assert.strictEqual(error.message, "domain_expiry_unsupported_invalid_domain");
- return true;
- }
- );
- });
-
- test("throws error for IPv6 address", async () => {
- const monitor = {
- type: "http",
- url: "https://[2001:db8::1]",
- domainExpiryNotification: true,
- };
- await assert.rejects(
- async () => await DomainExpiry.checkSupport(monitor),
- (error) => {
- assert.strictEqual(error.constructor.name, "TranslatableError");
- assert.strictEqual(error.message, "domain_expiry_unsupported_invalid_domain");
- return true;
- }
- );
- });
-
- test("throws error for single-letter TLD", async () => {
- const monitor = {
- type: "http",
- url: "https://example.x",
+ url: "https://example.a",
domainExpiryNotification: true,
};
await assert.rejects(
@@ -159,6 +127,22 @@ describe("Domain Expiry", () => {
}
);
});
+
+ test("throws error for non-ICANN TLD (e.g. .local)", async () => {
+ const monitor = {
+ type: "http",
+ url: "https://example.local",
+ domainExpiryNotification: true,
+ };
+ await assert.rejects(
+ async () => await DomainExpiry.checkSupport(monitor),
+ (error) => {
+ assert.strictEqual(error.constructor.name, "TranslatableError");
+ assert.strictEqual(error.message, "domain_expiry_unsupported_is_icann");
+ return true;
+ }
+ );
+ });
});
describe("Edge Cases & RDAP Support", () => {
@@ -205,22 +189,6 @@ describe("Domain Expiry", () => {
assert.strictEqual(supportInfo.domain, "example.com");
assert.strictEqual(supportInfo.tld, "com");
});
-
- test("throws error for unsupported TLD without RDAP endpoint", async () => {
- const monitor = {
- type: "http",
- url: "https://example.localhost",
- domainExpiryNotification: true,
- };
- await assert.rejects(
- async () => await DomainExpiry.checkSupport(monitor),
- (error) => {
- assert.strictEqual(error.constructor.name, "TranslatableError");
- assert.strictEqual(error.message, "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint");
- return true;
- }
- );
- });
});
});