From 2785ddf522c84ef78c55f6442132b4a0a07f582a Mon Sep 17 00:00:00 2001 From: cyril59310 Date: Sun, 28 Dec 2025 03:16:32 +0100 Subject: [PATCH 01/21] default icons for status page --- src/lang/en.json | 3 +- src/pages/StatusPage.vue | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/lang/en.json b/src/lang/en.json index 727358fdc..4b7511758 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -1246,5 +1246,6 @@ "labelDomainNameExpiryNotification": "Domain Name Expiry Notification", "domainExpiryDescription": "Trigger notification when domain names expires in:", "minimumIntervalWarning": "Intervals below 20 seconds may result in poor performance.", - "lowIntervalWarning": "Are you sure want to set the interval value below 20 seconds? Performance may be degraded, particularly if there are a large number of monitors." + "lowIntervalWarning": "Are you sure want to set the interval value below 20 seconds? Performance may be degraded, particularly if there are a large number of monitors.", + "Image reset to default": "Image reset to default" } diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 74a5c3f66..f2fcebf7c 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -138,6 +138,9 @@

+ @@ -962,6 +965,21 @@ export default { } }, + /** + * Reset logo image to default (public/icon.svg) + * @returns {void} + */ + resetToDefaultImage() { + if (! this.editMode) { + return; + } + + this.imgDataUrl = "/icon.svg"; + this.config.icon = this.imgDataUrl; + toast.success(this.$t("Image reset to default")); + }, + + /** * Create an incident for this status page * @returns {void} @@ -1181,6 +1199,58 @@ footer { cursor: pointer; box-shadow: 0 15px 70px rgba(0, 0, 0, 0.9); } + + /* Reset button placed at top-left of the logo */ + .reset-top-left { + position: absolute; + top: 0; + left: -15px; + z-index: 2; + width: 20px; + height: 20px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: white; + border: none; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); + cursor: pointer; + padding: 0; + transition: transform $easing-in 0.18s, box-shadow $easing-in 0.18s, background-color $easing-in 0.18s; + transform-origin: center; + + &:hover { + background-color: rgba(0, 0, 0, 0.06); + transform: scale(1.18); + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18); + } + + &:hover ~ .icon-upload { + transform: none !important; + } + } + + .small-reset-btn { + transition: transform $easing-in 0.18s, box-shadow $easing-in 0.18s, background-color $easing-in 0.18s; + font-size: 18px; + width: 18px; + height: 18px; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: transparent; + border: none; + cursor: pointer; + + &:hover { + background-color: rgba(0, 0, 0, 0.04); + transform: scale(1.18); + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12); + } + } } .logo { From ab61f8dc89dbc644627b0be85c7b91de852d82fe Mon Sep 17 00:00:00 2001 From: cyril59310 Date: Sun, 28 Dec 2025 03:46:40 +0100 Subject: [PATCH 02/21] fix --- src/pages/StatusPage.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index f2fcebf7c..b2bcb1c52 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -979,7 +979,6 @@ export default { toast.success(this.$t("Image reset to default")); }, - /** * Create an incident for this status page * @returns {void} From 875dd1288a6a21de4bc5dfc00e65594cde2f5139 Mon Sep 17 00:00:00 2001 From: cyril59310 Date: Sun, 28 Dec 2025 20:30:54 +0100 Subject: [PATCH 03/21] translation key update --- src/lang/en.json | 2 +- src/pages/StatusPage.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/en.json b/src/lang/en.json index 4b7511758..c6c410c23 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -1247,5 +1247,5 @@ "domainExpiryDescription": "Trigger notification when domain names expires in:", "minimumIntervalWarning": "Intervals below 20 seconds may result in poor performance.", "lowIntervalWarning": "Are you sure want to set the interval value below 20 seconds? Performance may be degraded, particularly if there are a large number of monitors.", - "Image reset to default": "Image reset to default" + "imageResetConfirmation": "Image reset to default" } diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index b2bcb1c52..7b2426a3c 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -976,7 +976,7 @@ export default { this.imgDataUrl = "/icon.svg"; this.config.icon = this.imgDataUrl; - toast.success(this.$t("Image reset to default")); + toast.success(this.$t("imageResetConfirmation")); }, /** From bcfd1e929558c58b926680f420a2ea8687387612 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 15:41:07 +0100 Subject: [PATCH 04/21] group related components --- .../monitor-conditions/test-evaluator.js | 75 ++++---- .../monitor-conditions/test-operators.js | 180 +++++++++--------- 2 files changed, 130 insertions(+), 125 deletions(-) diff --git a/test/backend-test/monitor-conditions/test-evaluator.js b/test/backend-test/monitor-conditions/test-evaluator.js index da7c7fabf..fd74fa7d2 100644 --- a/test/backend-test/monitor-conditions/test-evaluator.js +++ b/test/backend-test/monitor-conditions/test-evaluator.js @@ -1,46 +1,49 @@ + const test = require("node:test"); const assert = require("node:assert"); const { ConditionExpressionGroup, ConditionExpression, LOGICAL } = require("../../../server/monitor-conditions/expression.js"); const { evaluateExpressionGroup, evaluateExpression } = require("../../../server/monitor-conditions/evaluator.js"); -test("Test evaluateExpression", async (t) => { - const expr = new ConditionExpression("record", "contains", "mx1.example.com"); - assert.strictEqual(true, evaluateExpression(expr, { record: "mx1.example.com" })); - assert.strictEqual(false, evaluateExpression(expr, { record: "mx2.example.com" })); -}); +test("evaluateExpression", async (t) => { + await t.test("Test evaluateExpression", async (t) => { + const expr = new ConditionExpression("record", "contains", "mx1.example.com"); + assert.strictEqual(true, evaluateExpression(expr, { record: "mx1.example.com" })); + assert.strictEqual(false, evaluateExpression(expr, { record: "mx2.example.com" })); + }); -test("Test evaluateExpressionGroup with logical AND", async (t) => { - const group = new ConditionExpressionGroup([ - new ConditionExpression("record", "contains", "mx1."), - new ConditionExpression("record", "contains", "example.com", LOGICAL.AND), - ]); - assert.strictEqual(true, evaluateExpressionGroup(group, { record: "mx1.example.com" })); - assert.strictEqual(false, evaluateExpressionGroup(group, { record: "mx1." })); - assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.com" })); -}); + await t.test("Test evaluateExpressionGroup with logical AND", async (t) => { + const group = new ConditionExpressionGroup([ + new ConditionExpression("record", "contains", "mx1."), + new ConditionExpression("record", "contains", "example.com", LOGICAL.AND), + ]); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "mx1.example.com" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "mx1." })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.com" })); + }); -test("Test evaluateExpressionGroup with logical OR", async (t) => { - const group = new ConditionExpressionGroup([ - new ConditionExpression("record", "contains", "example.com"), - new ConditionExpression("record", "contains", "example.org", LOGICAL.OR), - ]); - assert.strictEqual(true, evaluateExpressionGroup(group, { record: "example.com" })); - assert.strictEqual(true, evaluateExpressionGroup(group, { record: "example.org" })); - assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.net" })); -}); - -test("Test evaluateExpressionGroup with nested group", async (t) => { - const group = new ConditionExpressionGroup([ - new ConditionExpression("record", "contains", "mx1."), - new ConditionExpressionGroup([ + await t.test("Test evaluateExpressionGroup with logical OR", async (t) => { + const group = new ConditionExpressionGroup([ new ConditionExpression("record", "contains", "example.com"), new ConditionExpression("record", "contains", "example.org", LOGICAL.OR), - ]), - ]); - assert.strictEqual(false, evaluateExpressionGroup(group, { record: "mx1." })); - assert.strictEqual(true, evaluateExpressionGroup(group, { record: "mx1.example.com" })); - assert.strictEqual(true, evaluateExpressionGroup(group, { record: "mx1.example.org" })); - assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.com" })); - assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.org" })); - assert.strictEqual(false, evaluateExpressionGroup(group, { record: "mx1.example.net" })); + ]); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "example.com" })); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "example.org" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.net" })); + }); + + await t.test("Test evaluateExpressionGroup with nested group", async (t) => { + const group = new ConditionExpressionGroup([ + new ConditionExpression("record", "contains", "mx1."), + new ConditionExpressionGroup([ + new ConditionExpression("record", "contains", "example.com"), + new ConditionExpression("record", "contains", "example.org", LOGICAL.OR), + ]), + ]); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "mx1." })); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "mx1.example.com" })); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "mx1.example.org" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.com" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.org" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "mx1.example.net" })); + }); }); diff --git a/test/backend-test/monitor-conditions/test-operators.js b/test/backend-test/monitor-conditions/test-operators.js index e663c9a50..66bf07e67 100644 --- a/test/backend-test/monitor-conditions/test-operators.js +++ b/test/backend-test/monitor-conditions/test-operators.js @@ -2,107 +2,109 @@ const test = require("node:test"); const assert = require("node:assert"); const { operatorMap, OP_CONTAINS, OP_NOT_CONTAINS, OP_LT, OP_GT, OP_LTE, OP_GTE, OP_STR_EQUALS, OP_STR_NOT_EQUALS, OP_NUM_EQUALS, OP_NUM_NOT_EQUALS, OP_STARTS_WITH, OP_ENDS_WITH, OP_NOT_STARTS_WITH, OP_NOT_ENDS_WITH } = require("../../../server/monitor-conditions/operators.js"); -test("Test StringEqualsOperator", async (t) => { - const op = operatorMap.get(OP_STR_EQUALS); - assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.com")); - assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.org")); - assert.strictEqual(false, op.test("1", 1)); // strict equality -}); +test("expressionOperators", async (t) => { + await t.test("Test StringEqualsOperator", async (t) => { + const op = operatorMap.get(OP_STR_EQUALS); + assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.com")); + assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.org")); + assert.strictEqual(false, op.test("1", 1)); // strict equality + }); -test("Test StringNotEqualsOperator", async (t) => { - const op = operatorMap.get(OP_STR_NOT_EQUALS); - assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.org")); - assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.com")); - assert.strictEqual(true, op.test(1, "1")); // variable is not typecasted (strict equality) -}); + await t.test("Test StringNotEqualsOperator", async (t) => { + const op = operatorMap.get(OP_STR_NOT_EQUALS); + assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.org")); + assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.com")); + assert.strictEqual(true, op.test(1, "1")); // variable is not typecasted (strict equality) + }); -test("Test ContainsOperator with scalar", async (t) => { - const op = operatorMap.get(OP_CONTAINS); - assert.strictEqual(true, op.test("mx1.example.org", "example.org")); - assert.strictEqual(false, op.test("mx1.example.org", "example.com")); -}); + await t.test("Test ContainsOperator with scalar", async (t) => { + const op = operatorMap.get(OP_CONTAINS); + assert.strictEqual(true, op.test("mx1.example.org", "example.org")); + assert.strictEqual(false, op.test("mx1.example.org", "example.com")); + }); -test("Test ContainsOperator with array", async (t) => { - const op = operatorMap.get(OP_CONTAINS); - assert.strictEqual(true, op.test([ "example.org" ], "example.org")); - assert.strictEqual(false, op.test([ "example.org" ], "example.com")); -}); + await t.test("Test ContainsOperator with array", async (t) => { + const op = operatorMap.get(OP_CONTAINS); + assert.strictEqual(true, op.test([ "example.org" ], "example.org")); + assert.strictEqual(false, op.test([ "example.org" ], "example.com")); + }); -test("Test NotContainsOperator with scalar", async (t) => { - const op = operatorMap.get(OP_NOT_CONTAINS); - assert.strictEqual(true, op.test("example.org", ".com")); - assert.strictEqual(false, op.test("example.org", ".org")); -}); + await t.test("Test NotContainsOperator with scalar", async (t) => { + const op = operatorMap.get(OP_NOT_CONTAINS); + assert.strictEqual(true, op.test("example.org", ".com")); + assert.strictEqual(false, op.test("example.org", ".org")); + }); -test("Test NotContainsOperator with array", async (t) => { - const op = operatorMap.get(OP_NOT_CONTAINS); - assert.strictEqual(true, op.test([ "example.org" ], "example.com")); - assert.strictEqual(false, op.test([ "example.org" ], "example.org")); -}); + await t.test("Test NotContainsOperator with array", async (t) => { + const op = operatorMap.get(OP_NOT_CONTAINS); + assert.strictEqual(true, op.test([ "example.org" ], "example.com")); + assert.strictEqual(false, op.test([ "example.org" ], "example.org")); + }); -test("Test StartsWithOperator", async (t) => { - const op = operatorMap.get(OP_STARTS_WITH); - assert.strictEqual(true, op.test("mx1.example.com", "mx1")); - assert.strictEqual(false, op.test("mx1.example.com", "mx2")); -}); + await t.test("Test StartsWithOperator", async (t) => { + const op = operatorMap.get(OP_STARTS_WITH); + assert.strictEqual(true, op.test("mx1.example.com", "mx1")); + assert.strictEqual(false, op.test("mx1.example.com", "mx2")); + }); -test("Test NotStartsWithOperator", async (t) => { - const op = operatorMap.get(OP_NOT_STARTS_WITH); - assert.strictEqual(true, op.test("mx1.example.com", "mx2")); - assert.strictEqual(false, op.test("mx1.example.com", "mx1")); -}); + await t.test("Test NotStartsWithOperator", async (t) => { + const op = operatorMap.get(OP_NOT_STARTS_WITH); + assert.strictEqual(true, op.test("mx1.example.com", "mx2")); + assert.strictEqual(false, op.test("mx1.example.com", "mx1")); + }); -test("Test EndsWithOperator", async (t) => { - const op = operatorMap.get(OP_ENDS_WITH); - assert.strictEqual(true, op.test("mx1.example.com", "example.com")); - assert.strictEqual(false, op.test("mx1.example.com", "example.net")); -}); + await t.test("Test EndsWithOperator", async (t) => { + const op = operatorMap.get(OP_ENDS_WITH); + assert.strictEqual(true, op.test("mx1.example.com", "example.com")); + assert.strictEqual(false, op.test("mx1.example.com", "example.net")); + }); -test("Test NotEndsWithOperator", async (t) => { - const op = operatorMap.get(OP_NOT_ENDS_WITH); - assert.strictEqual(true, op.test("mx1.example.com", "example.net")); - assert.strictEqual(false, op.test("mx1.example.com", "example.com")); -}); + await t.test("Test NotEndsWithOperator", async (t) => { + const op = operatorMap.get(OP_NOT_ENDS_WITH); + assert.strictEqual(true, op.test("mx1.example.com", "example.net")); + assert.strictEqual(false, op.test("mx1.example.com", "example.com")); + }); -test("Test NumberEqualsOperator", async (t) => { - const op = operatorMap.get(OP_NUM_EQUALS); - assert.strictEqual(true, op.test(1, 1)); - assert.strictEqual(true, op.test(1, "1")); - assert.strictEqual(false, op.test(1, "2")); -}); + await t.test("Test NumberEqualsOperator", async (t) => { + const op = operatorMap.get(OP_NUM_EQUALS); + assert.strictEqual(true, op.test(1, 1)); + assert.strictEqual(true, op.test(1, "1")); + assert.strictEqual(false, op.test(1, "2")); + }); -test("Test NumberNotEqualsOperator", async (t) => { - const op = operatorMap.get(OP_NUM_NOT_EQUALS); - assert.strictEqual(true, op.test(1, "2")); - assert.strictEqual(false, op.test(1, "1")); -}); + await t.test("Test NumberNotEqualsOperator", async (t) => { + const op = operatorMap.get(OP_NUM_NOT_EQUALS); + assert.strictEqual(true, op.test(1, "2")); + assert.strictEqual(false, op.test(1, "1")); + }); -test("Test LessThanOperator", async (t) => { - const op = operatorMap.get(OP_LT); - assert.strictEqual(true, op.test(1, 2)); - assert.strictEqual(true, op.test(1, "2")); - assert.strictEqual(false, op.test(1, 1)); -}); + await t.test("Test LessThanOperator", async (t) => { + const op = operatorMap.get(OP_LT); + assert.strictEqual(true, op.test(1, 2)); + assert.strictEqual(true, op.test(1, "2")); + assert.strictEqual(false, op.test(1, 1)); + }); -test("Test GreaterThanOperator", async (t) => { - const op = operatorMap.get(OP_GT); - assert.strictEqual(true, op.test(2, 1)); - assert.strictEqual(true, op.test(2, "1")); - assert.strictEqual(false, op.test(1, 1)); -}); + await t.test("Test GreaterThanOperator", async (t) => { + const op = operatorMap.get(OP_GT); + assert.strictEqual(true, op.test(2, 1)); + assert.strictEqual(true, op.test(2, "1")); + assert.strictEqual(false, op.test(1, 1)); + }); -test("Test LessThanOrEqualToOperator", async (t) => { - const op = operatorMap.get(OP_LTE); - assert.strictEqual(true, op.test(1, 1)); - assert.strictEqual(true, op.test(1, 2)); - assert.strictEqual(true, op.test(1, "2")); - assert.strictEqual(false, op.test(1, 0)); -}); + await t.test("Test LessThanOrEqualToOperator", async (t) => { + const op = operatorMap.get(OP_LTE); + assert.strictEqual(true, op.test(1, 1)); + assert.strictEqual(true, op.test(1, 2)); + assert.strictEqual(true, op.test(1, "2")); + assert.strictEqual(false, op.test(1, 0)); + }); -test("Test GreaterThanOrEqualToOperator", async (t) => { - const op = operatorMap.get(OP_GTE); - assert.strictEqual(true, op.test(1, 1)); - assert.strictEqual(true, op.test(2, 1)); - assert.strictEqual(true, op.test(2, "2")); - assert.strictEqual(false, op.test(2, 3)); -}); + await t.test("Test GreaterThanOrEqualToOperator", async (t) => { + const op = operatorMap.get(OP_GTE); + assert.strictEqual(true, op.test(1, 1)); + assert.strictEqual(true, op.test(2, 1)); + assert.strictEqual(true, op.test(2, "2")); + assert.strictEqual(false, op.test(2, 3)); + }); +}); \ No newline at end of file From 0901434c9fbba1b44662fce431d6686c027208e1 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:16:45 +0100 Subject: [PATCH 05/21] fix the conditions tests being weirder than they need to --- .../monitor-conditions/test-evaluator.js | 10 +++--- .../monitor-conditions/test-operators.js | 36 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/test/backend-test/monitor-conditions/test-evaluator.js b/test/backend-test/monitor-conditions/test-evaluator.js index fd74fa7d2..edd051abd 100644 --- a/test/backend-test/monitor-conditions/test-evaluator.js +++ b/test/backend-test/monitor-conditions/test-evaluator.js @@ -4,14 +4,14 @@ const assert = require("node:assert"); const { ConditionExpressionGroup, ConditionExpression, LOGICAL } = require("../../../server/monitor-conditions/expression.js"); const { evaluateExpressionGroup, evaluateExpression } = require("../../../server/monitor-conditions/evaluator.js"); -test("evaluateExpression", async (t) => { - await t.test("Test evaluateExpression", async (t) => { +test("Expression Evaluator", async (t) => { + await t.test("evaluateExpression() returns true when condition matches and false otherwise", async (t) => { const expr = new ConditionExpression("record", "contains", "mx1.example.com"); assert.strictEqual(true, evaluateExpression(expr, { record: "mx1.example.com" })); assert.strictEqual(false, evaluateExpression(expr, { record: "mx2.example.com" })); }); - await t.test("Test evaluateExpressionGroup with logical AND", async (t) => { + await t.test("evaluateExpressionGroup() with AND logic requires all conditions to be true", async (t) => { const group = new ConditionExpressionGroup([ new ConditionExpression("record", "contains", "mx1."), new ConditionExpression("record", "contains", "example.com", LOGICAL.AND), @@ -21,7 +21,7 @@ test("evaluateExpression", async (t) => { assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.com" })); }); - await t.test("Test evaluateExpressionGroup with logical OR", async (t) => { + await t.test("evaluateExpressionGroup() with OR logic requires at least one condition to be true", async (t) => { const group = new ConditionExpressionGroup([ new ConditionExpression("record", "contains", "example.com"), new ConditionExpression("record", "contains", "example.org", LOGICAL.OR), @@ -31,7 +31,7 @@ test("evaluateExpression", async (t) => { assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.net" })); }); - await t.test("Test evaluateExpressionGroup with nested group", async (t) => { + await t.test("evaluateExpressionGroup() evaluates nested groups correctly", async (t) => { const group = new ConditionExpressionGroup([ new ConditionExpression("record", "contains", "mx1."), new ConditionExpressionGroup([ diff --git a/test/backend-test/monitor-conditions/test-operators.js b/test/backend-test/monitor-conditions/test-operators.js index 66bf07e67..ef10d63a2 100644 --- a/test/backend-test/monitor-conditions/test-operators.js +++ b/test/backend-test/monitor-conditions/test-operators.js @@ -2,97 +2,97 @@ const test = require("node:test"); const assert = require("node:assert"); const { operatorMap, OP_CONTAINS, OP_NOT_CONTAINS, OP_LT, OP_GT, OP_LTE, OP_GTE, OP_STR_EQUALS, OP_STR_NOT_EQUALS, OP_NUM_EQUALS, OP_NUM_NOT_EQUALS, OP_STARTS_WITH, OP_ENDS_WITH, OP_NOT_STARTS_WITH, OP_NOT_ENDS_WITH } = require("../../../server/monitor-conditions/operators.js"); -test("expressionOperators", async (t) => { - await t.test("Test StringEqualsOperator", async (t) => { +test("Expression Operators", async (t) => { + await t.test("StringEqualsOperator returns true for identical strings and false otherwise", async (t) => { const op = operatorMap.get(OP_STR_EQUALS); assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.com")); assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.org")); assert.strictEqual(false, op.test("1", 1)); // strict equality }); - await t.test("Test StringNotEqualsOperator", async (t) => { + await t.test("StringNotEqualsOperator returns true for different strings and false for identical strings", async (t) => { const op = operatorMap.get(OP_STR_NOT_EQUALS); assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.org")); assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.com")); assert.strictEqual(true, op.test(1, "1")); // variable is not typecasted (strict equality) }); - await t.test("Test ContainsOperator with scalar", async (t) => { + await t.test("ContainsOperator returns true when scalar contains substring", async (t) => { const op = operatorMap.get(OP_CONTAINS); assert.strictEqual(true, op.test("mx1.example.org", "example.org")); assert.strictEqual(false, op.test("mx1.example.org", "example.com")); }); - await t.test("Test ContainsOperator with array", async (t) => { + await t.test("ContainsOperator returns true when array contains element", async (t) => { const op = operatorMap.get(OP_CONTAINS); assert.strictEqual(true, op.test([ "example.org" ], "example.org")); assert.strictEqual(false, op.test([ "example.org" ], "example.com")); }); - await t.test("Test NotContainsOperator with scalar", async (t) => { + await t.test("NotContainsOperator returns true when scalar does not contain substring", async (t) => { const op = operatorMap.get(OP_NOT_CONTAINS); assert.strictEqual(true, op.test("example.org", ".com")); assert.strictEqual(false, op.test("example.org", ".org")); }); - await t.test("Test NotContainsOperator with array", async (t) => { + await t.test("NotContainsOperator returns true when array does not contain element", async (t) => { const op = operatorMap.get(OP_NOT_CONTAINS); assert.strictEqual(true, op.test([ "example.org" ], "example.com")); assert.strictEqual(false, op.test([ "example.org" ], "example.org")); }); - await t.test("Test StartsWithOperator", async (t) => { + await t.test("StartsWithOperator returns true when string starts with prefix", async (t) => { const op = operatorMap.get(OP_STARTS_WITH); assert.strictEqual(true, op.test("mx1.example.com", "mx1")); assert.strictEqual(false, op.test("mx1.example.com", "mx2")); }); - await t.test("Test NotStartsWithOperator", async (t) => { + await t.test("NotStartsWithOperator returns true when string does not start with prefix", async (t) => { const op = operatorMap.get(OP_NOT_STARTS_WITH); assert.strictEqual(true, op.test("mx1.example.com", "mx2")); assert.strictEqual(false, op.test("mx1.example.com", "mx1")); }); - await t.test("Test EndsWithOperator", async (t) => { + await t.test("EndsWithOperator returns true when string ends with suffix", async (t) => { const op = operatorMap.get(OP_ENDS_WITH); assert.strictEqual(true, op.test("mx1.example.com", "example.com")); assert.strictEqual(false, op.test("mx1.example.com", "example.net")); }); - await t.test("Test NotEndsWithOperator", async (t) => { + await t.test("NotEndsWithOperator returns true when string does not end with suffix", async (t) => { const op = operatorMap.get(OP_NOT_ENDS_WITH); assert.strictEqual(true, op.test("mx1.example.com", "example.net")); assert.strictEqual(false, op.test("mx1.example.com", "example.com")); }); - await t.test("Test NumberEqualsOperator", async (t) => { + await t.test("NumberEqualsOperator returns true for equal numbers with type coercion", async (t) => { const op = operatorMap.get(OP_NUM_EQUALS); assert.strictEqual(true, op.test(1, 1)); assert.strictEqual(true, op.test(1, "1")); assert.strictEqual(false, op.test(1, "2")); }); - await t.test("Test NumberNotEqualsOperator", async (t) => { + await t.test("NumberNotEqualsOperator returns true for different numbers", async (t) => { const op = operatorMap.get(OP_NUM_NOT_EQUALS); assert.strictEqual(true, op.test(1, "2")); assert.strictEqual(false, op.test(1, "1")); }); - await t.test("Test LessThanOperator", async (t) => { + await t.test("LessThanOperator returns true when first number is less than second", async (t) => { const op = operatorMap.get(OP_LT); assert.strictEqual(true, op.test(1, 2)); assert.strictEqual(true, op.test(1, "2")); assert.strictEqual(false, op.test(1, 1)); }); - await t.test("Test GreaterThanOperator", async (t) => { + await t.test("GreaterThanOperator returns true when first number is greater than second", async (t) => { const op = operatorMap.get(OP_GT); assert.strictEqual(true, op.test(2, 1)); assert.strictEqual(true, op.test(2, "1")); assert.strictEqual(false, op.test(1, 1)); }); - await t.test("Test LessThanOrEqualToOperator", async (t) => { + await t.test("LessThanOrEqualToOperator returns true when first number is less than or equal to second", async (t) => { const op = operatorMap.get(OP_LTE); assert.strictEqual(true, op.test(1, 1)); assert.strictEqual(true, op.test(1, 2)); @@ -100,11 +100,11 @@ test("expressionOperators", async (t) => { assert.strictEqual(false, op.test(1, 0)); }); - await t.test("Test GreaterThanOrEqualToOperator", async (t) => { + await t.test("GreaterThanOrEqualToOperator returns true when first number is greater than or equal to second", async (t) => { const op = operatorMap.get(OP_GTE); assert.strictEqual(true, op.test(1, 1)); assert.strictEqual(true, op.test(2, 1)); assert.strictEqual(true, op.test(2, "2")); assert.strictEqual(false, op.test(2, 3)); }); -}); \ No newline at end of file +}); From e4c347be0b61ede6fa18227f4417455315b58a1c Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:22:17 +0100 Subject: [PATCH 06/21] make sure we use test suites --- .../monitor-conditions/test-evaluator.js | 12 +++---- .../monitor-conditions/test-operators.js | 36 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/backend-test/monitor-conditions/test-evaluator.js b/test/backend-test/monitor-conditions/test-evaluator.js index edd051abd..f2e967b81 100644 --- a/test/backend-test/monitor-conditions/test-evaluator.js +++ b/test/backend-test/monitor-conditions/test-evaluator.js @@ -1,17 +1,17 @@ -const test = require("node:test"); +const { describe, test } = require("node:test"); const assert = require("node:assert"); const { ConditionExpressionGroup, ConditionExpression, LOGICAL } = require("../../../server/monitor-conditions/expression.js"); const { evaluateExpressionGroup, evaluateExpression } = require("../../../server/monitor-conditions/evaluator.js"); -test("Expression Evaluator", async (t) => { - await t.test("evaluateExpression() returns true when condition matches and false otherwise", async (t) => { +describe("Expression Evaluator", () => { + test("evaluateExpression() returns true when condition matches and false otherwise", () => { const expr = new ConditionExpression("record", "contains", "mx1.example.com"); assert.strictEqual(true, evaluateExpression(expr, { record: "mx1.example.com" })); assert.strictEqual(false, evaluateExpression(expr, { record: "mx2.example.com" })); }); - await t.test("evaluateExpressionGroup() with AND logic requires all conditions to be true", async (t) => { + test("evaluateExpressionGroup() with AND logic requires all conditions to be true", () => { const group = new ConditionExpressionGroup([ new ConditionExpression("record", "contains", "mx1."), new ConditionExpression("record", "contains", "example.com", LOGICAL.AND), @@ -21,7 +21,7 @@ test("Expression Evaluator", async (t) => { assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.com" })); }); - await t.test("evaluateExpressionGroup() with OR logic requires at least one condition to be true", async (t) => { + test("evaluateExpressionGroup() with OR logic requires at least one condition to be true", () => { const group = new ConditionExpressionGroup([ new ConditionExpression("record", "contains", "example.com"), new ConditionExpression("record", "contains", "example.org", LOGICAL.OR), @@ -31,7 +31,7 @@ test("Expression Evaluator", async (t) => { assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.net" })); }); - await t.test("evaluateExpressionGroup() evaluates nested groups correctly", async (t) => { + test("evaluateExpressionGroup() evaluates nested groups correctly", () => { const group = new ConditionExpressionGroup([ new ConditionExpression("record", "contains", "mx1."), new ConditionExpressionGroup([ diff --git a/test/backend-test/monitor-conditions/test-operators.js b/test/backend-test/monitor-conditions/test-operators.js index ef10d63a2..6a6739631 100644 --- a/test/backend-test/monitor-conditions/test-operators.js +++ b/test/backend-test/monitor-conditions/test-operators.js @@ -1,98 +1,98 @@ -const test = require("node:test"); +const { describe, test } = require("node:test"); const assert = require("node:assert"); const { operatorMap, OP_CONTAINS, OP_NOT_CONTAINS, OP_LT, OP_GT, OP_LTE, OP_GTE, OP_STR_EQUALS, OP_STR_NOT_EQUALS, OP_NUM_EQUALS, OP_NUM_NOT_EQUALS, OP_STARTS_WITH, OP_ENDS_WITH, OP_NOT_STARTS_WITH, OP_NOT_ENDS_WITH } = require("../../../server/monitor-conditions/operators.js"); -test("Expression Operators", async (t) => { - await t.test("StringEqualsOperator returns true for identical strings and false otherwise", async (t) => { +describe("Expression Operators", () => { + test("StringEqualsOperator returns true for identical strings and false otherwise", () => { const op = operatorMap.get(OP_STR_EQUALS); assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.com")); assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.org")); assert.strictEqual(false, op.test("1", 1)); // strict equality }); - await t.test("StringNotEqualsOperator returns true for different strings and false for identical strings", async (t) => { + test("StringNotEqualsOperator returns true for different strings and false for identical strings", () => { const op = operatorMap.get(OP_STR_NOT_EQUALS); assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.org")); assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.com")); assert.strictEqual(true, op.test(1, "1")); // variable is not typecasted (strict equality) }); - await t.test("ContainsOperator returns true when scalar contains substring", async (t) => { + test("ContainsOperator returns true when scalar contains substring", () => { const op = operatorMap.get(OP_CONTAINS); assert.strictEqual(true, op.test("mx1.example.org", "example.org")); assert.strictEqual(false, op.test("mx1.example.org", "example.com")); }); - await t.test("ContainsOperator returns true when array contains element", async (t) => { + test("ContainsOperator returns true when array contains element", () => { const op = operatorMap.get(OP_CONTAINS); assert.strictEqual(true, op.test([ "example.org" ], "example.org")); assert.strictEqual(false, op.test([ "example.org" ], "example.com")); }); - await t.test("NotContainsOperator returns true when scalar does not contain substring", async (t) => { + test("NotContainsOperator returns true when scalar does not contain substring", () => { const op = operatorMap.get(OP_NOT_CONTAINS); assert.strictEqual(true, op.test("example.org", ".com")); assert.strictEqual(false, op.test("example.org", ".org")); }); - await t.test("NotContainsOperator returns true when array does not contain element", async (t) => { + test("NotContainsOperator returns true when array does not contain element", () => { const op = operatorMap.get(OP_NOT_CONTAINS); assert.strictEqual(true, op.test([ "example.org" ], "example.com")); assert.strictEqual(false, op.test([ "example.org" ], "example.org")); }); - await t.test("StartsWithOperator returns true when string starts with prefix", async (t) => { + test("StartsWithOperator returns true when string starts with prefix", () => { const op = operatorMap.get(OP_STARTS_WITH); assert.strictEqual(true, op.test("mx1.example.com", "mx1")); assert.strictEqual(false, op.test("mx1.example.com", "mx2")); }); - await t.test("NotStartsWithOperator returns true when string does not start with prefix", async (t) => { + test("NotStartsWithOperator returns true when string does not start with prefix", () => { const op = operatorMap.get(OP_NOT_STARTS_WITH); assert.strictEqual(true, op.test("mx1.example.com", "mx2")); assert.strictEqual(false, op.test("mx1.example.com", "mx1")); }); - await t.test("EndsWithOperator returns true when string ends with suffix", async (t) => { + test("EndsWithOperator returns true when string ends with suffix", () => { const op = operatorMap.get(OP_ENDS_WITH); assert.strictEqual(true, op.test("mx1.example.com", "example.com")); assert.strictEqual(false, op.test("mx1.example.com", "example.net")); }); - await t.test("NotEndsWithOperator returns true when string does not end with suffix", async (t) => { + test("NotEndsWithOperator returns true when string does not end with suffix", () => { const op = operatorMap.get(OP_NOT_ENDS_WITH); assert.strictEqual(true, op.test("mx1.example.com", "example.net")); assert.strictEqual(false, op.test("mx1.example.com", "example.com")); }); - await t.test("NumberEqualsOperator returns true for equal numbers with type coercion", async (t) => { + test("NumberEqualsOperator returns true for equal numbers with type coercion", () => { const op = operatorMap.get(OP_NUM_EQUALS); assert.strictEqual(true, op.test(1, 1)); assert.strictEqual(true, op.test(1, "1")); assert.strictEqual(false, op.test(1, "2")); }); - await t.test("NumberNotEqualsOperator returns true for different numbers", async (t) => { + test("NumberNotEqualsOperator returns true for different numbers", () => { const op = operatorMap.get(OP_NUM_NOT_EQUALS); assert.strictEqual(true, op.test(1, "2")); assert.strictEqual(false, op.test(1, "1")); }); - await t.test("LessThanOperator returns true when first number is less than second", async (t) => { + test("LessThanOperator returns true when first number is less than second", () => { const op = operatorMap.get(OP_LT); assert.strictEqual(true, op.test(1, 2)); assert.strictEqual(true, op.test(1, "2")); assert.strictEqual(false, op.test(1, 1)); }); - await t.test("GreaterThanOperator returns true when first number is greater than second", async (t) => { + test("GreaterThanOperator returns true when first number is greater than second", () => { const op = operatorMap.get(OP_GT); assert.strictEqual(true, op.test(2, 1)); assert.strictEqual(true, op.test(2, "1")); assert.strictEqual(false, op.test(1, 1)); }); - await t.test("LessThanOrEqualToOperator returns true when first number is less than or equal to second", async (t) => { + test("LessThanOrEqualToOperator returns true when first number is less than or equal to second", () => { const op = operatorMap.get(OP_LTE); assert.strictEqual(true, op.test(1, 1)); assert.strictEqual(true, op.test(1, 2)); @@ -100,7 +100,7 @@ test("Expression Operators", async (t) => { assert.strictEqual(false, op.test(1, 0)); }); - await t.test("GreaterThanOrEqualToOperator returns true when first number is greater than or equal to second", async (t) => { + test("GreaterThanOrEqualToOperator returns true when first number is greater than or equal to second", () => { const op = operatorMap.get(OP_GTE); assert.strictEqual(true, op.test(1, 1)); assert.strictEqual(true, op.test(2, 1)); From b6d5dd690d76a34ff199b7c98e43a503e935c716 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:25:18 +0100 Subject: [PATCH 07/21] make sure the calculator uses a suite --- test/backend-test/test-uptime-calculator.js | 680 ++++++++++---------- 1 file changed, 339 insertions(+), 341 deletions(-) diff --git a/test/backend-test/test-uptime-calculator.js b/test/backend-test/test-uptime-calculator.js index 4f2f05efe..622bb8f02 100644 --- a/test/backend-test/test-uptime-calculator.js +++ b/test/backend-test/test-uptime-calculator.js @@ -1,4 +1,4 @@ -const test = require("node:test"); +const { describe, test } = require("node:test"); const assert = require("node:assert"); const { UptimeCalculator } = require("../../server/uptime-calculator"); const dayjs = require("dayjs"); @@ -7,344 +7,403 @@ dayjs.extend(require("dayjs/plugin/utc")); dayjs.extend(require("../../server/modules/dayjs/plugin/timezone")); dayjs.extend(require("dayjs/plugin/customParseFormat")); -test("Test Uptime Calculator - custom date", async (t) => { - let c1 = new UptimeCalculator(); +describe("Uptime Calculator", () => { + test("getCurrentDate() returns custom date when set", () => { + let c1 = new UptimeCalculator(); - // Test custom date - UptimeCalculator.currentDate = dayjs.utc("2021-01-01T00:00:00.000Z"); - assert.strictEqual(c1.getCurrentDate().unix(), dayjs.utc("2021-01-01T00:00:00.000Z").unix()); -}); + // Test custom date + UptimeCalculator.currentDate = dayjs.utc("2021-01-01T00:00:00.000Z"); + assert.strictEqual(c1.getCurrentDate().unix(), dayjs.utc("2021-01-01T00:00:00.000Z").unix()); + }); -test("Test update - UP", async (t) => { - UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); - let c2 = new UptimeCalculator(); - let date = await c2.update(UP); - assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:46:59").unix()); -}); + test("update() with UP status returns correct timestamp", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + let c2 = new UptimeCalculator(); + let date = await c2.update(UP); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:46:59").unix()); + }); -test("Test update - MAINTENANCE", async (t) => { - UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); - let c2 = new UptimeCalculator(); - let date = await c2.update(MAINTENANCE); - assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); -}); + test("update() with MAINTENANCE status returns correct timestamp", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); + let c2 = new UptimeCalculator(); + let date = await c2.update(MAINTENANCE); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); + }); -test("Test update - DOWN", async (t) => { - UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); - let c2 = new UptimeCalculator(); - let date = await c2.update(DOWN); - assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); -}); + test("update() with DOWN status returns correct timestamp", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); + let c2 = new UptimeCalculator(); + let date = await c2.update(DOWN); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); + }); -test("Test update - PENDING", async (t) => { - UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); - let c2 = new UptimeCalculator(); - let date = await c2.update(PENDING); - assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); -}); + test("update() with PENDING status returns correct timestamp", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); + let c2 = new UptimeCalculator(); + let date = await c2.update(PENDING); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); + }); -test("Test flatStatus", async (t) => { - let c2 = new UptimeCalculator(); - assert.strictEqual(c2.flatStatus(UP), UP); - //assert.strictEqual(c2.flatStatus(MAINTENANCE), UP); - assert.strictEqual(c2.flatStatus(DOWN), DOWN); - assert.strictEqual(c2.flatStatus(PENDING), DOWN); -}); + test("flatStatus() converts statuses correctly", () => { + let c2 = new UptimeCalculator(); + assert.strictEqual(c2.flatStatus(UP), UP); + //assert.strictEqual(c2.flatStatus(MAINTENANCE), UP); + assert.strictEqual(c2.flatStatus(DOWN), DOWN); + assert.strictEqual(c2.flatStatus(PENDING), DOWN); + }); -test("Test getMinutelyKey", async (t) => { - let c2 = new UptimeCalculator(); - let divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:00")); - assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); + test("getMinutelyKey() returns correct timestamp for start of minute", () => { + let c2 = new UptimeCalculator(); + let divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:00")); + assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); - // Edge case 1 - c2 = new UptimeCalculator(); - divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:01")); - assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); + // Edge case 1 + c2 = new UptimeCalculator(); + divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:01")); + assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); - // Edge case 2 - c2 = new UptimeCalculator(); - divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:59")); - assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); -}); + // Edge case 2 + c2 = new UptimeCalculator(); + divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:59")); + assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); + }); -test("Test getDailyKey", async (t) => { - let c2 = new UptimeCalculator(); - let dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 20:46:00")); - assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + test("getDailyKey() returns correct timestamp for start of day", () => { + let c2 = new UptimeCalculator(); + let dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 20:46:00")); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); - c2 = new UptimeCalculator(); - dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:45:30")); - assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:45:30")); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); - // Edge case 1 - c2 = new UptimeCalculator(); - dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:59:59")); - assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + // Edge case 1 + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:59:59")); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); - // Edge case 2 - c2 = new UptimeCalculator(); - dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 00:00:00")); - assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + // Edge case 2 + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 00:00:00")); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); - // Test timezone - c2 = new UptimeCalculator(); - dailyKey = c2.getDailyKey(dayjs("Sat Dec 23 2023 05:38:39 GMT+0800 (Hong Kong Standard Time)")); - assert.strictEqual(dailyKey, dayjs.utc("2023-12-22").unix()); -}); + // Test timezone + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs("Sat Dec 23 2023 05:38:39 GMT+0800 (Hong Kong Standard Time)")); + assert.strictEqual(dailyKey, dayjs.utc("2023-12-22").unix()); + }); -test("Test lastDailyUptimeData", async (t) => { - let c2 = new UptimeCalculator(); - await c2.update(UP); - assert.strictEqual(c2.lastDailyUptimeData.up, 1); -}); + test("lastDailyUptimeData tracks UP status correctly", async () => { + let c2 = new UptimeCalculator(); + await c2.update(UP); + assert.strictEqual(c2.lastDailyUptimeData.up, 1); + }); -test("Test get24Hour Uptime and Avg Ping", async (t) => { - UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + test("get24Hour() calculates uptime and average ping correctly", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); - // No data - let c2 = new UptimeCalculator(); - let data = c2.get24Hour(); - assert.strictEqual(data.uptime, 0); - assert.strictEqual(data.avgPing, null); + // No data + let c2 = new UptimeCalculator(); + let data = c2.get24Hour(); + assert.strictEqual(data.uptime, 0); + assert.strictEqual(data.avgPing, null); - // 1 Up - c2 = new UptimeCalculator(); - await c2.update(UP, 100); - let uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 1); - assert.strictEqual(c2.get24Hour().avgPing, 100); + // 1 Up + c2 = new UptimeCalculator(); + await c2.update(UP, 100); + let uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 1); + assert.strictEqual(c2.get24Hour().avgPing, 100); - // 2 Up - c2 = new UptimeCalculator(); - await c2.update(UP, 100); - await c2.update(UP, 200); - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 1); - assert.strictEqual(c2.get24Hour().avgPing, 150); + // 2 Up + c2 = new UptimeCalculator(); + await c2.update(UP, 100); + await c2.update(UP, 200); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 1); + assert.strictEqual(c2.get24Hour().avgPing, 150); - // 3 Up - c2 = new UptimeCalculator(); - await c2.update(UP, 0); - await c2.update(UP, 100); - await c2.update(UP, 400); - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 1); - assert.strictEqual(c2.get24Hour().avgPing, 166.66666666666666); + // 3 Up + c2 = new UptimeCalculator(); + await c2.update(UP, 0); + await c2.update(UP, 100); + await c2.update(UP, 400); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 1); + assert.strictEqual(c2.get24Hour().avgPing, 166.66666666666666); - // 1 MAINTENANCE - c2 = new UptimeCalculator(); - await c2.update(MAINTENANCE); - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 0); - assert.strictEqual(c2.get24Hour().avgPing, null); + // 1 MAINTENANCE + c2 = new UptimeCalculator(); + await c2.update(MAINTENANCE); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); - // 1 PENDING - c2 = new UptimeCalculator(); - await c2.update(PENDING); - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 0); - assert.strictEqual(c2.get24Hour().avgPing, null); + // 1 PENDING + c2 = new UptimeCalculator(); + await c2.update(PENDING); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); - // 1 DOWN - c2 = new UptimeCalculator(); - await c2.update(DOWN); - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 0); - assert.strictEqual(c2.get24Hour().avgPing, null); + // 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); - // 2 DOWN - c2 = new UptimeCalculator(); - await c2.update(DOWN); - await c2.update(DOWN); - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 0); - assert.strictEqual(c2.get24Hour().avgPing, null); + // 2 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); - // 1 DOWN, 1 UP - c2 = new UptimeCalculator(); - await c2.update(DOWN); - await c2.update(UP, 0.5); - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 0.5); - assert.strictEqual(c2.get24Hour().avgPing, 0.5); + // 1 DOWN, 1 UP + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(UP, 0.5); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.5); + assert.strictEqual(c2.get24Hour().avgPing, 0.5); - // 1 UP, 1 DOWN - c2 = new UptimeCalculator(); - await c2.update(UP, 123); - await c2.update(DOWN); - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 0.5); - assert.strictEqual(c2.get24Hour().avgPing, 123); + // 1 UP, 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(UP, 123); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.5); + assert.strictEqual(c2.get24Hour().avgPing, 123); - // Add 24 hours - c2 = new UptimeCalculator(); - await c2.update(UP, 0); - await c2.update(UP, 0); - await c2.update(UP, 0); - await c2.update(UP, 1); - await c2.update(DOWN); - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 0.8); - assert.strictEqual(c2.get24Hour().avgPing, 0.25); + // Add 24 hours + c2 = new UptimeCalculator(); + await c2.update(UP, 0); + await c2.update(UP, 0); + await c2.update(UP, 0); + await c2.update(UP, 1); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.8); + assert.strictEqual(c2.get24Hour().avgPing, 0.25); - UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour"); + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour"); - // After 24 hours, even if there is no data, the uptime should be still 80% - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 0.8); - assert.strictEqual(c2.get24Hour().avgPing, 0.25); + // After 24 hours, even if there is no data, the uptime should be still 80% + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.8); + assert.strictEqual(c2.get24Hour().avgPing, 0.25); - // Add more 24 hours (48 hours) - UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour"); + // Add more 24 hours (48 hours) + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour"); - // After 48 hours, even if there is no data, the uptime should be still 80% - uptime = c2.get24Hour().uptime; - assert.strictEqual(uptime, 0.8); - assert.strictEqual(c2.get24Hour().avgPing, 0.25); -}); + // After 48 hours, even if there is no data, the uptime should be still 80% + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.8); + assert.strictEqual(c2.get24Hour().avgPing, 0.25); + }); -test("Test get7DayUptime", async (t) => { - UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + test("get7Day() calculates 7-day uptime correctly", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); - // No data - let c2 = new UptimeCalculator(); - let uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 0); + // No data + let c2 = new UptimeCalculator(); + let uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); - // 1 Up - c2 = new UptimeCalculator(); - await c2.update(UP); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 1); + // 1 Up + c2 = new UptimeCalculator(); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 1); - // 2 Up - c2 = new UptimeCalculator(); - await c2.update(UP); - await c2.update(UP); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 1); + // 2 Up + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 1); - // 3 Up - c2 = new UptimeCalculator(); - await c2.update(UP); - await c2.update(UP); - await c2.update(UP); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 1); + // 3 Up + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(UP); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 1); - // 1 MAINTENANCE - c2 = new UptimeCalculator(); - await c2.update(MAINTENANCE); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 0); + // 1 MAINTENANCE + c2 = new UptimeCalculator(); + await c2.update(MAINTENANCE); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); - // 1 PENDING - c2 = new UptimeCalculator(); - await c2.update(PENDING); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 0); + // 1 PENDING + c2 = new UptimeCalculator(); + await c2.update(PENDING); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); - // 1 DOWN - c2 = new UptimeCalculator(); - await c2.update(DOWN); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 0); + // 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); - // 2 DOWN - c2 = new UptimeCalculator(); - await c2.update(DOWN); - await c2.update(DOWN); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 0); + // 2 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); - // 1 DOWN, 1 UP - c2 = new UptimeCalculator(); - await c2.update(DOWN); - await c2.update(UP); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 0.5); + // 1 DOWN, 1 UP + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.5); - // 1 UP, 1 DOWN - c2 = new UptimeCalculator(); - await c2.update(UP); - await c2.update(DOWN); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 0.5); + // 1 UP, 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.5); - // Add 7 days - c2 = new UptimeCalculator(); - await c2.update(UP); - await c2.update(UP); - await c2.update(UP); - await c2.update(UP); - await c2.update(DOWN); - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 0.8); - UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(7, "day"); + // Add 7 days + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(UP); + await c2.update(UP); + await c2.update(UP); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.8); + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(7, "day"); - // After 7 days, even if there is no data, the uptime should be still 80% - uptime = c2.get7Day().uptime; - assert.strictEqual(uptime, 0.8); + // After 7 days, even if there is no data, the uptime should be still 80% + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.8); + }); -}); + test("get30Day() calculates 30-day uptime correctly with 1 check per day", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); -test("Test get30DayUptime (1 check per day)", async (t) => { - UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + let c2 = new UptimeCalculator(); + let uptime = c2.get30Day().uptime; + assert.strictEqual(uptime, 0); - let c2 = new UptimeCalculator(); - let uptime = c2.get30Day().uptime; - assert.strictEqual(uptime, 0); + let up = 0; + let down = 0; + let flip = true; + for (let i = 0; i < 30; i++) { + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day"); - let up = 0; - let down = 0; - let flip = true; - for (let i = 0; i < 30; i++) { - UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day"); + if (flip) { + await c2.update(UP); + up++; + } else { + await c2.update(DOWN); + down++; + } - if (flip) { - await c2.update(UP); - up++; - } else { - await c2.update(DOWN); - down++; + uptime = c2.get30Day().uptime; + assert.strictEqual(uptime, up / (up + down)); + + flip = !flip; } - uptime = c2.get30Day().uptime; - assert.strictEqual(uptime, up / (up + down)); + // Last 7 days + // Down, Up, Down, Up, Down, Up, Down + // So 3 UP + assert.strictEqual(c2.get7Day().uptime, 3 / 7); + }); - flip = !flip; - } + test("get1Year() calculates 1-year uptime correctly with 1 check per day", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); - // Last 7 days - // Down, Up, Down, Up, Down, Up, Down - // So 3 UP - assert.strictEqual(c2.get7Day().uptime, 3 / 7); -}); + let c2 = new UptimeCalculator(); + let uptime = c2.get1Year().uptime; + assert.strictEqual(uptime, 0); -test("Test get1YearUptime (1 check per day)", async (t) => { - UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + let flip = true; + for (let i = 0; i < 365; i++) { + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day"); - let c2 = new UptimeCalculator(); - let uptime = c2.get1Year().uptime; - assert.strictEqual(uptime, 0); + if (flip) { + await c2.update(UP); + } else { + await c2.update(DOWN); + } - let flip = true; - for (let i = 0; i < 365; i++) { - UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day"); - - if (flip) { - await c2.update(UP); - } else { - await c2.update(DOWN); + uptime = c2.get30Day().time; + flip = !flip; } - uptime = c2.get30Day().time; - flip = !flip; - } + assert.strictEqual(c2.get1Year().uptime, 183 / 365); + assert.strictEqual(c2.get30Day().uptime, 15 / 30); + assert.strictEqual(c2.get7Day().uptime, 4 / 7); + }); - assert.strictEqual(c2.get1Year().uptime, 183 / 365); - assert.strictEqual(c2.get30Day().uptime, 15 / 30); - assert.strictEqual(c2.get7Day().uptime, 4 / 7); + describe("Worst case scenario", () => { + test("handles year-long simulation with various statuses", { + skip: process.env.GITHUB_ACTIONS // Not stable on GitHub Actions" + }, async (t) => { + console.log("Memory usage before preparation", memoryUsage()); + + let c = new UptimeCalculator(); + let up = 0; + let down = 0; + let interval = 20; + + await t.test("Prepare data", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + // Since 2023-08-12 will be out of 365 range, it starts from 2023-08-13 actually + let actualStartDate = dayjs.utc("2023-08-13 00:00:00").unix(); + + // Simulate 1s interval for a year + for (let i = 0; i < 365 * 24 * 60 * 60; i += interval) { + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(interval, "second"); + + //Randomly UP, DOWN, MAINTENANCE, PENDING + let rand = Math.random(); + if (rand < 0.25) { + c.update(UP); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + up++; + } + } else if (rand < 0.5) { + c.update(DOWN); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + down++; + } + } else if (rand < 0.75) { + c.update(MAINTENANCE); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + //up++; + } + } else { + c.update(PENDING); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + down++; + } + } + } + console.log("Final Date: ", UptimeCalculator.currentDate.format("YYYY-MM-DD HH:mm:ss")); + console.log("Memory usage before preparation", memoryUsage()); + + assert.strictEqual(c.minutelyUptimeDataList.length(), 1440); + assert.strictEqual(c.dailyUptimeDataList.length(), 365); + }); + + await t.test("get1YearUptime()", async () => { + assert.strictEqual(c.get1Year().uptime, up / (up + down)); + }); + }); + }); }); /** @@ -362,64 +421,3 @@ function memoryUsage() { external: `${formatMemoryUsage(memoryData.external)} -> V8 external memory`, }; } - -test("Worst case", async (t) => { - - // Disable on GitHub Actions, as it is not stable on it - if (process.env.GITHUB_ACTIONS) { - return; - } - - console.log("Memory usage before preparation", memoryUsage()); - - let c = new UptimeCalculator(); - let up = 0; - let down = 0; - let interval = 20; - - await t.test("Prepare data", async () => { - UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); - - // Since 2023-08-12 will be out of 365 range, it starts from 2023-08-13 actually - let actualStartDate = dayjs.utc("2023-08-13 00:00:00").unix(); - - // Simulate 1s interval for a year - for (let i = 0; i < 365 * 24 * 60 * 60; i += interval) { - UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(interval, "second"); - - //Randomly UP, DOWN, MAINTENANCE, PENDING - let rand = Math.random(); - if (rand < 0.25) { - c.update(UP); - if (UptimeCalculator.currentDate.unix() > actualStartDate) { - up++; - } - } else if (rand < 0.5) { - c.update(DOWN); - if (UptimeCalculator.currentDate.unix() > actualStartDate) { - down++; - } - } else if (rand < 0.75) { - c.update(MAINTENANCE); - if (UptimeCalculator.currentDate.unix() > actualStartDate) { - //up++; - } - } else { - c.update(PENDING); - if (UptimeCalculator.currentDate.unix() > actualStartDate) { - down++; - } - } - } - console.log("Final Date: ", UptimeCalculator.currentDate.format("YYYY-MM-DD HH:mm:ss")); - console.log("Memory usage before preparation", memoryUsage()); - - assert.strictEqual(c.minutelyUptimeDataList.length(), 1440); - assert.strictEqual(c.dailyUptimeDataList.length(), 365); - }); - - await t.test("get1YearUptime()", async () => { - assert.strictEqual(c.get1Year().uptime, up / (up + down)); - }); - -}); From b91b73a4eb155eaaf8cfeeae23340e08e0cc3780 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:27:37 +0100 Subject: [PATCH 08/21] move monitoring tests to better folder --- test/backend-test/{ => monitors}/test-grpc.js | 0 test/backend-test/{ => monitors}/test-mqtt.js | 0 test/backend-test/{ => monitors}/test-mssql.js | 0 test/backend-test/{ => monitors}/test-postgres.js | 0 test/backend-test/{ => monitors}/test-rabbitmq.js | 0 test/backend-test/{ => monitors}/test-tcp.js | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename test/backend-test/{ => monitors}/test-grpc.js (100%) rename test/backend-test/{ => monitors}/test-mqtt.js (100%) rename test/backend-test/{ => monitors}/test-mssql.js (100%) rename test/backend-test/{ => monitors}/test-postgres.js (100%) rename test/backend-test/{ => monitors}/test-rabbitmq.js (100%) rename test/backend-test/{ => monitors}/test-tcp.js (100%) diff --git a/test/backend-test/test-grpc.js b/test/backend-test/monitors/test-grpc.js similarity index 100% rename from test/backend-test/test-grpc.js rename to test/backend-test/monitors/test-grpc.js diff --git a/test/backend-test/test-mqtt.js b/test/backend-test/monitors/test-mqtt.js similarity index 100% rename from test/backend-test/test-mqtt.js rename to test/backend-test/monitors/test-mqtt.js diff --git a/test/backend-test/test-mssql.js b/test/backend-test/monitors/test-mssql.js similarity index 100% rename from test/backend-test/test-mssql.js rename to test/backend-test/monitors/test-mssql.js diff --git a/test/backend-test/test-postgres.js b/test/backend-test/monitors/test-postgres.js similarity index 100% rename from test/backend-test/test-postgres.js rename to test/backend-test/monitors/test-postgres.js diff --git a/test/backend-test/test-rabbitmq.js b/test/backend-test/monitors/test-rabbitmq.js similarity index 100% rename from test/backend-test/test-rabbitmq.js rename to test/backend-test/monitors/test-rabbitmq.js diff --git a/test/backend-test/test-tcp.js b/test/backend-test/monitors/test-tcp.js similarity index 100% rename from test/backend-test/test-tcp.js rename to test/backend-test/monitors/test-tcp.js From 9abded0baf3cf359cb80080b83ca9e8efbb97bcf Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:34:05 +0100 Subject: [PATCH 09/21] fix imports --- server/model/domain_expiry.js | 4 ++-- test/backend-test/monitors/test-grpc.js | 4 ++-- test/backend-test/monitors/test-mqtt.js | 4 ++-- test/backend-test/monitors/test-mssql.js | 4 ++-- test/backend-test/monitors/test-postgres.js | 4 ++-- test/backend-test/monitors/test-rabbitmq.js | 4 ++-- test/backend-test/monitors/test-tcp.js | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/server/model/domain_expiry.js b/server/model/domain_expiry.js index b796a81d1..d365c03ab 100644 --- a/server/model/domain_expiry.js +++ b/server/model/domain_expiry.js @@ -172,9 +172,9 @@ class DomainExpiry extends BeanModel { } /** - * @returns {(Date|null)} Expiry date from RDAP + * @returns {Promise<(Date|null)>} Expiry date from RDAP */ - getExpiryDate() { + async getExpiryDate() { return getRdapDomainExpiryDate(this.domain); } diff --git a/test/backend-test/monitors/test-grpc.js b/test/backend-test/monitors/test-grpc.js index 31b588cff..24edfb81f 100644 --- a/test/backend-test/monitors/test-grpc.js +++ b/test/backend-test/monitors/test-grpc.js @@ -2,8 +2,8 @@ const { describe, test } = require("node:test"); const assert = require("node:assert"); const grpc = require("@grpc/grpc-js"); const protoLoader = require("@grpc/proto-loader"); -const { GrpcKeywordMonitorType } = require("../../server/monitor-types/grpc"); -const { UP, PENDING } = require("../../src/util"); +const { GrpcKeywordMonitorType } = require("../../../server/monitor-types/grpc"); +const { UP, PENDING } = require("../../../src/util"); const fs = require("fs"); const path = require("path"); const os = require("os"); diff --git a/test/backend-test/monitors/test-mqtt.js b/test/backend-test/monitors/test-mqtt.js index 921df48fc..dbcf56a83 100644 --- a/test/backend-test/monitors/test-mqtt.js +++ b/test/backend-test/monitors/test-mqtt.js @@ -2,8 +2,8 @@ const { describe, test } = require("node:test"); const assert = require("node:assert"); const { HiveMQContainer } = require("@testcontainers/hivemq"); const mqtt = require("mqtt"); -const { MqttMonitorType } = require("../../server/monitor-types/mqtt"); -const { UP, PENDING } = require("../../src/util"); +const { MqttMonitorType } = require("../../../server/monitor-types/mqtt"); +const { UP, PENDING } = require("../../../src/util"); /** * Runs an MQTT test with the diff --git a/test/backend-test/monitors/test-mssql.js b/test/backend-test/monitors/test-mssql.js index afdd1faf8..091f01acc 100644 --- a/test/backend-test/monitors/test-mssql.js +++ b/test/backend-test/monitors/test-mssql.js @@ -1,8 +1,8 @@ const { describe, test } = require("node:test"); const assert = require("node:assert"); const { MSSQLServerContainer } = require("@testcontainers/mssqlserver"); -const { MssqlMonitorType } = require("../../server/monitor-types/mssql"); -const { UP, PENDING } = require("../../src/util"); +const { MssqlMonitorType } = require("../../../server/monitor-types/mssql"); +const { UP, PENDING } = require("../../../src/util"); /** * Helper function to create and start a MSSQL container diff --git a/test/backend-test/monitors/test-postgres.js b/test/backend-test/monitors/test-postgres.js index 71d26d3a1..505e0fc90 100644 --- a/test/backend-test/monitors/test-postgres.js +++ b/test/backend-test/monitors/test-postgres.js @@ -1,8 +1,8 @@ const { describe, test } = require("node:test"); const assert = require("node:assert"); const { PostgreSqlContainer } = require("@testcontainers/postgresql"); -const { PostgresMonitorType } = require("../../server/monitor-types/postgres"); -const { UP, PENDING } = require("../../src/util"); +const { PostgresMonitorType } = require("../../../server/monitor-types/postgres"); +const { UP, PENDING } = require("../../../src/util"); describe( "Postgres Single Node", diff --git a/test/backend-test/monitors/test-rabbitmq.js b/test/backend-test/monitors/test-rabbitmq.js index 31f018aa9..88a04afa1 100644 --- a/test/backend-test/monitors/test-rabbitmq.js +++ b/test/backend-test/monitors/test-rabbitmq.js @@ -1,8 +1,8 @@ const { describe, test } = require("node:test"); const assert = require("node:assert"); const { RabbitMQContainer } = require("@testcontainers/rabbitmq"); -const { RabbitMqMonitorType } = require("../../server/monitor-types/rabbitmq"); -const { UP, PENDING } = require("../../src/util"); +const { RabbitMqMonitorType } = require("../../../server/monitor-types/rabbitmq"); +const { UP, PENDING } = require("../../../src/util"); describe("RabbitMQ Single Node", { skip: !!process.env.CI && (process.platform !== "linux" || process.arch !== "x64"), diff --git a/test/backend-test/monitors/test-tcp.js b/test/backend-test/monitors/test-tcp.js index 98f168c24..71f0f2f87 100644 --- a/test/backend-test/monitors/test-tcp.js +++ b/test/backend-test/monitors/test-tcp.js @@ -1,7 +1,7 @@ const { describe, test } = require("node:test"); const assert = require("node:assert"); -const { TCPMonitorType } = require("../../server/monitor-types/tcp"); -const { UP, PENDING } = require("../../src/util"); +const { TCPMonitorType } = require("../../../server/monitor-types/tcp"); +const { UP, PENDING } = require("../../../src/util"); const net = require("net"); /** From 49eb0a34a10b6789ec1e495056cc4bb7496976f6 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:34:31 +0100 Subject: [PATCH 10/21] migrate domains to use test suites --- test/backend-test/test-domain.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/test/backend-test/test-domain.js b/test/backend-test/test-domain.js index 535029531..d254244bf 100644 --- a/test/backend-test/test-domain.js +++ b/test/backend-test/test-domain.js @@ -1,6 +1,6 @@ process.env.UPTIME_KUMA_HIDE_LOG = [ "info_db", "info_server" ].join(","); -const test = require("node:test"); +const { describe, test } = require("node:test"); const assert = require("node:assert"); const DomainExpiry = require("../../server/model/domain_expiry"); const mockWebhook = require("../mock-webhook"); @@ -12,30 +12,34 @@ const { setSetting } = require("../../server/util-server"); const testDb = new TestDB(); -test("Domain Expiry", async (t) => { - await testDb.create(); - Notification.init(); - +describe("Domain Expiry", () => { const monHttpCom = { type: "http", url: "https://www.google.com", domainExpiryNotification: true }; - await t.test("Should get expiry date for .wiki with no A record", async () => { + + test("getExpiryDate() returns correct expiry date for .wiki domain with no A record", async () => { + await testDb.create(); + Notification.init(); + const d = DomainExpiry.createByName("google.wiki"); assert.deepEqual(await d.getExpiryDate(), new Date("2026-11-26T23:59:59.000Z")); }); - await t.test("Should get expiration date for .com from RDAP", async () => { + + test("forMonitor() retrieves expiration date for .com domain from RDAP", async () => { const domain = await DomainExpiry.forMonitor(monHttpCom); const expiryFromRdap = await domain.getExpiryDate(); // from RDAP assert.deepEqual(expiryFromRdap, new Date("2028-09-14T04:00:00.000Z")); }); - await t.test("Should have expiration date cached in database", async () => { + + test("checkExpiry() caches expiration date in database", async () => { await DomainExpiry.checkExpiry(monHttpCom); // RDAP -> Cache const domain = await DomainExpiry.findByName("google.com"); assert(Date.now() - domain.lastCheck < 5 * 1000); }); - await t.test("Should trigger notify for expiring domain", async () => { + + test("sendNotifications() triggers notification for expiring domain", async () => { await DomainExpiry.findByName("google.com"); const hook = { "port": 3010, @@ -60,10 +64,10 @@ test("Domain Expiry", async (t) => { mockWebhook(hook.port, hook.url) ]); assert.match(data.msg, /will expire in/); + + setTimeout(async () => { + Settings.stopCacheCleaner(); + await testDb.destroy(); + }, 200); }); -}).finally(() => { - setTimeout(async () => { - Settings.stopCacheCleaner(); - await testDb.destroy(); - }, 200); }); From afef0c0e61e3830f9153f93113ac182b4eddd28c Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:35:26 +0100 Subject: [PATCH 11/21] move websokcet --- test/backend-test/{ => monitors}/test-websocket.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename test/backend-test/{ => monitors}/test-websocket.js (98%) diff --git a/test/backend-test/test-websocket.js b/test/backend-test/monitors/test-websocket.js similarity index 98% rename from test/backend-test/test-websocket.js rename to test/backend-test/monitors/test-websocket.js index 3eeeb3243..545cad466 100644 --- a/test/backend-test/test-websocket.js +++ b/test/backend-test/monitors/test-websocket.js @@ -1,8 +1,8 @@ const { WebSocketServer } = require("ws"); const { describe, test } = require("node:test"); const assert = require("node:assert"); -const { WebSocketMonitorType } = require("../../server/monitor-types/websocket-upgrade"); -const { UP, PENDING } = require("../../src/util"); +const { WebSocketMonitorType } = require("../../../server/monitor-types/websocket-upgrade"); +const { UP, PENDING } = require("../../../src/util"); const net = require("node:net"); /** From 625456ec76b554f4473e404227bb9c78341f1fca Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:37:03 +0100 Subject: [PATCH 12/21] migrate maintenance to use suites --- test/backend-test/test-maintenance.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/test/backend-test/test-maintenance.js b/test/backend-test/test-maintenance.js index a23bd77bb..bb8304d93 100644 --- a/test/backend-test/test-maintenance.js +++ b/test/backend-test/test-maintenance.js @@ -1,4 +1,4 @@ -const test = require("node:test"); +const { describe, test } = require("node:test"); const assert = require("node:assert"); const dayjs = require("dayjs"); const { SQL_DATETIME_FORMAT } = require("../../src/util"); @@ -11,20 +11,19 @@ dayjs.extend(require("dayjs/plugin/customParseFormat")); * Issue: MariaDB rejects ISO format dates like '2025-12-19T01:04:02.129Z' * Fix: Use SQL_DATETIME_FORMAT ('YYYY-MM-DD HH:mm:ss') instead of toISOString() */ -test("Maintenance Date Format - MariaDB Compatibility", async (t) => { - - await t.test("SQL_DATETIME_FORMAT constant should match MariaDB format", async () => { +describe("Maintenance Date Format - MariaDB Compatibility", () => { + test("SQL_DATETIME_FORMAT constant should match MariaDB format", () => { assert.strictEqual(SQL_DATETIME_FORMAT, "YYYY-MM-DD HH:mm:ss"); }); - await t.test("Format date using SQL_DATETIME_FORMAT", async () => { + test("format() produces SQL-compatible datetime string", () => { const current = dayjs.utc("2025-12-19T01:04:02.129Z"); const sqlFormat = current.utc().format(SQL_DATETIME_FORMAT); assert.strictEqual(sqlFormat, "2025-12-19 01:04:02"); - }); + });move websokcet - await t.test("SQL format should not contain ISO markers (T, Z)", async () => { + test("SQL format does not contain ISO markers (T, Z)", () => { const current = dayjs.utc("2025-12-19T01:04:02.129Z"); const sqlFormat = current.utc().format(SQL_DATETIME_FORMAT); @@ -32,7 +31,7 @@ test("Maintenance Date Format - MariaDB Compatibility", async (t) => { assert.strictEqual(sqlFormat.includes("Z"), false, "SQL format should not contain 'Z'"); }); - await t.test("SQL format should match YYYY-MM-DD HH:mm:ss pattern", async () => { + test("SQL format matches YYYY-MM-DD HH:mm:ss pattern", () => { const current = dayjs.utc("2025-12-19T01:04:02.129Z"); const sqlFormat = current.utc().format(SQL_DATETIME_FORMAT); const sqlDateTimeRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/; @@ -40,7 +39,7 @@ test("Maintenance Date Format - MariaDB Compatibility", async (t) => { assert.strictEqual(sqlDateTimeRegex.test(sqlFormat), true); }); - await t.test("Parse SQL datetime back to dayjs preserves timestamp", async () => { + test("parsing SQL datetime back to dayjs preserves timestamp", () => { const originalDate = dayjs.utc("2025-12-19T01:04:02.000Z"); const sqlFormat = originalDate.utc().format(SQL_DATETIME_FORMAT); const parsedDate = dayjs.utc(sqlFormat, SQL_DATETIME_FORMAT); @@ -48,18 +47,17 @@ test("Maintenance Date Format - MariaDB Compatibility", async (t) => { assert.strictEqual(parsedDate.unix(), originalDate.unix()); }); - await t.test("Edge case: midnight timestamp", async () => { + test("formats midnight timestamp correctly", () => { const midnight = dayjs.utc("2025-01-01T00:00:00.000Z"); const sqlFormat = midnight.utc().format(SQL_DATETIME_FORMAT); assert.strictEqual(sqlFormat, "2025-01-01 00:00:00"); }); - await t.test("Edge case: end of day timestamp", async () => { + test("formats end of day timestamp correctly", () => { const endOfDay = dayjs.utc("2025-12-31T23:59:59.999Z"); const sqlFormat = endOfDay.utc().format(SQL_DATETIME_FORMAT); assert.strictEqual(sqlFormat, "2025-12-31 23:59:59"); }); - }); From 19880f479e22b0f6abf10d5c55742d4e762f3fb3 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:39:00 +0100 Subject: [PATCH 13/21] cert hostname --- test/backend-test/test-cert-hostname-match.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/backend-test/test-cert-hostname-match.js b/test/backend-test/test-cert-hostname-match.js index fb7c822ed..f35b7aa76 100644 --- a/test/backend-test/test-cert-hostname-match.js +++ b/test/backend-test/test-cert-hostname-match.js @@ -1,4 +1,4 @@ -const { test } = require("node:test"); +const { describe, test } = require("node:test"); const assert = require("node:assert"); @@ -36,12 +36,14 @@ iPenGDCg1awOyRnvxNq1MtMDkR9AHwksukzwiYNexYjyvE2t0UzXhFXwazQ3 -----END CERTIFICATE----- `; -test("Certificate and hostname match", () => { - const result = checkCertificateHostname(testCert, "www.eff.org"); - assert.strictEqual(result, true); -}); +describe("Certificate Hostname Validation", () => { + test("checkCertificateHostname() returns true when certificate matches hostname", () => { + const result = checkCertificateHostname(testCert, "www.eff.org"); + assert.strictEqual(result, true); + }); -test("Certificate and hostname mismatch", () => { - const result = checkCertificateHostname(testCert, "example.com"); - assert.strictEqual(result, false); + test("checkCertificateHostname() returns false when certificate does not match hostname", () => { + const result = checkCertificateHostname(testCert, "example.com"); + assert.strictEqual(result, false); + }); }); From 453e468b77a0ab24861dbf8aacc4fe9a967892fc Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:42:56 +0100 Subject: [PATCH 14/21] refactor the system utilities test --- test/backend-test/test-util.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/test/backend-test/test-util.js b/test/backend-test/test-util.js index 470113329..63660d4fb 100644 --- a/test/backend-test/test-util.js +++ b/test/backend-test/test-util.js @@ -1,18 +1,26 @@ -const test = require("node:test"); +const { describe, test } = require("node:test"); const assert = require("node:assert"); const { getDaysRemaining, getDaysBetween } = require("../../server/util-server"); -test("Test getDaysBetween", async (t) => { - let days = getDaysBetween(new Date(2025, 9, 7), new Date(2025, 9, 10)); - assert.strictEqual(days, 3); - days = getDaysBetween(new Date(2024, 9, 7), new Date(2025, 9, 10)); - assert.strictEqual(days, 368); -}); +describe("Server Utilities", () => { + test("getDaysBetween() calculates days between dates within same month", () => { + const days = getDaysBetween(new Date(2025, 9, 7), new Date(2025, 9, 10)); + assert.strictEqual(days, 3); + }); -test("Test getDaysRemaining", async (t) => { - let days = getDaysRemaining(new Date(2025, 9, 7), new Date(2025, 9, 10)); - assert.strictEqual(days, 3); - days = getDaysRemaining(new Date(2025, 9, 10), new Date(2025, 9, 7)); - assert.strictEqual(days, -3); + test("getDaysBetween() calculates days between dates across years", () => { + const days = getDaysBetween(new Date(2024, 9, 7), new Date(2025, 9, 10)); + assert.strictEqual(days, 368); + }); + + test("getDaysRemaining() returns positive value when target date is in future", () => { + const days = getDaysRemaining(new Date(2025, 9, 7), new Date(2025, 9, 10)); + assert.strictEqual(days, 3); + }); + + test("getDaysRemaining() returns negative value when target date is in past", () => { + const days = getDaysRemaining(new Date(2025, 9, 10), new Date(2025, 9, 7)); + assert.strictEqual(days, -3); + }); }); From ac1c43d2ccecddc67d5eb9ec152edc3059dcb405 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:47:31 +0100 Subject: [PATCH 15/21] move the webhook to a better place --- .../notification-providers}/mock-webhook.js | 56 +++++++++---------- test/backend-test/test-domain.js | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) rename test/{ => backend-test/notification-providers}/mock-webhook.js (96%) diff --git a/test/mock-webhook.js b/test/backend-test/notification-providers/mock-webhook.js similarity index 96% rename from test/mock-webhook.js rename to test/backend-test/notification-providers/mock-webhook.js index 23bf192c7..70cc5fdf9 100644 --- a/test/mock-webhook.js +++ b/test/backend-test/notification-providers/mock-webhook.js @@ -1,28 +1,28 @@ -const express = require("express"); -const bodyParser = require("body-parser"); - -/** - * @param {number} port Port number - * @param {string} url Webhook URL - * @param {number} timeout Timeout - * @returns {Promise} Webhook data - */ -async function mockWebhook(port, url, timeout = 2500) { - return new Promise((resolve, reject) => { - const app = express(); - const tmo = setTimeout(() => { - server.close(); - reject({ reason: "Timeout" }); - }, timeout); - app.use(bodyParser.json()); // Middleware to parse JSON bodies - app.post(`/${url}`, (req, res) => { - res.status(200).send("OK"); - server.close(); - tmo && clearTimeout(tmo); - resolve(req.body); - }); - const server = app.listen(port); - }); -} - -module.exports = mockWebhook; +const express = require("express"); +const bodyParser = require("body-parser"); + +/** + * @param {number} port Port number + * @param {string} url Webhook URL + * @param {number} timeout Timeout + * @returns {Promise} Webhook data + */ +async function mockWebhook(port, url, timeout = 2500) { + return new Promise((resolve, reject) => { + const app = express(); + const tmo = setTimeout(() => { + server.close(); + reject({ reason: "Timeout" }); + }, timeout); + app.use(bodyParser.json()); // Middleware to parse JSON bodies + app.post(`/${url}`, (req, res) => { + res.status(200).send("OK"); + server.close(); + tmo && clearTimeout(tmo); + resolve(req.body); + }); + const server = app.listen(port); + }); +} + +module.exports = mockWebhook; diff --git a/test/backend-test/test-domain.js b/test/backend-test/test-domain.js index d254244bf..451cdfdc9 100644 --- a/test/backend-test/test-domain.js +++ b/test/backend-test/test-domain.js @@ -3,7 +3,7 @@ process.env.UPTIME_KUMA_HIDE_LOG = [ "info_db", "info_server" ].join(","); const { describe, test } = require("node:test"); const assert = require("node:assert"); const DomainExpiry = require("../../server/model/domain_expiry"); -const mockWebhook = require("../mock-webhook"); +const mockWebhook = require("./notification-providers/mock-webhook"); const TestDB = require("../mock-testdb"); const { R } = require("redbean-node"); const { Notification } = require("../../server/notification"); From d493291025432f01cb69036deea9c63bc0779449 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:51:02 +0100 Subject: [PATCH 16/21] update the readme --- test/backend-test/README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/backend-test/README.md b/test/backend-test/README.md index 775ffb7a8..8822562ab 100644 --- a/test/backend-test/README.md +++ b/test/backend-test/README.md @@ -4,14 +4,26 @@ Documentation: https://nodejs.org/api/test.html Create a test file in this directory with the name `*.js`. +> [!TIP] +> Writing great tests is hard. +> +> You can make our live much simpler by following this guidance: +> - Use `describe()` to group related tests +> - Use `test()` for individual test cases +> - One test per scenario +> - Use descriptive test names: `function() [behavior] [condition]` +> - Don't prefix with "Test" or "Should" + ## Template ```js -const test = require("node:test"); +const { describe, test } = require("node:test"); const assert = require("node:assert"); -test("Test name", async (t) => { - assert.strictEqual(1, 1); +describe("Feature Name", () => { + test("function() returns expected value when condition is met", () => { + assert.strictEqual(1, 1); + }); }); ``` From a27b4eb93f0e06233c05079feb2c0d5ab2eb7b52 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 16:54:34 +0100 Subject: [PATCH 17/21] fix fmt --- test/backend-test/monitor-conditions/test-evaluator.js | 1 - test/backend-test/test-maintenance.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/backend-test/monitor-conditions/test-evaluator.js b/test/backend-test/monitor-conditions/test-evaluator.js index f2e967b81..c731a6792 100644 --- a/test/backend-test/monitor-conditions/test-evaluator.js +++ b/test/backend-test/monitor-conditions/test-evaluator.js @@ -1,4 +1,3 @@ - const { describe, test } = require("node:test"); const assert = require("node:assert"); const { ConditionExpressionGroup, ConditionExpression, LOGICAL } = require("../../../server/monitor-conditions/expression.js"); diff --git a/test/backend-test/test-maintenance.js b/test/backend-test/test-maintenance.js index bb8304d93..27f3ff6b9 100644 --- a/test/backend-test/test-maintenance.js +++ b/test/backend-test/test-maintenance.js @@ -21,7 +21,7 @@ describe("Maintenance Date Format - MariaDB Compatibility", () => { const sqlFormat = current.utc().format(SQL_DATETIME_FORMAT); assert.strictEqual(sqlFormat, "2025-12-19 01:04:02"); - });move websokcet + }); test("SQL format does not contain ISO markers (T, Z)", () => { const current = dayjs.utc("2025-12-19T01:04:02.129Z"); From f426e5819ac99a5cf0670ba5db440dd99bbd0619 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 17:01:32 +0100 Subject: [PATCH 18/21] reword more of our testsuite names --- test/backend-test/monitors/test-grpc.js | 12 +++---- test/backend-test/monitors/test-mqtt.js | 30 ++++++++-------- test/backend-test/monitors/test-mssql.js | 16 ++++----- test/backend-test/monitors/test-postgres.js | 4 +-- test/backend-test/monitors/test-rabbitmq.js | 4 +-- test/backend-test/monitors/test-tcp.js | 36 ++++---------------- test/backend-test/monitors/test-websocket.js | 28 +++++++-------- test/backend-test/test-migration.js | 2 +- 8 files changed, 55 insertions(+), 77 deletions(-) diff --git a/test/backend-test/monitors/test-grpc.js b/test/backend-test/monitors/test-grpc.js index 24edfb81f..def839044 100644 --- a/test/backend-test/monitors/test-grpc.js +++ b/test/backend-test/monitors/test-grpc.js @@ -82,7 +82,7 @@ async function createTestGrpcServer(port, methodHandlers) { describe("GrpcKeywordMonitorType", { skip: !!process.env.CI && (process.platform !== "linux" || process.arch !== "x64"), }, () => { - test("gRPC keyword found in response", async () => { + test("check() sets status to UP when keyword is found in response", async () => { const port = 50051; const server = await createTestGrpcServer(port, { Echo: (call, callback) => { @@ -118,7 +118,7 @@ describe("GrpcKeywordMonitorType", { } }); - test("gRPC keyword not found in response", async () => { + test("check() rejects when keyword is not found in response", async () => { const port = 50052; const server = await createTestGrpcServer(port, { Echo: (call, callback) => { @@ -158,7 +158,7 @@ describe("GrpcKeywordMonitorType", { } }); - test("gRPC inverted keyword - keyword present (should fail)", async () => { + test("check() rejects when inverted keyword is present in response", async () => { const port = 50053; const server = await createTestGrpcServer(port, { Echo: (call, callback) => { @@ -198,7 +198,7 @@ describe("GrpcKeywordMonitorType", { } }); - test("gRPC inverted keyword - keyword not present (should pass)", async () => { + test("check() sets status to UP when inverted keyword is not present in response", async () => { const port = 50054; const server = await createTestGrpcServer(port, { Echo: (call, callback) => { @@ -234,7 +234,7 @@ describe("GrpcKeywordMonitorType", { } }); - test("gRPC connection failure", async () => { + test("check() rejects when gRPC server is unreachable", async () => { const grpcMonitor = new GrpcKeywordMonitorType(); const monitor = { grpcUrl: "localhost:50099", @@ -262,7 +262,7 @@ describe("GrpcKeywordMonitorType", { ); }); - test("gRPC response truncation for long messages", async () => { + test("check() truncates long response messages in error output", async () => { const port = 50055; const longMessage = "A".repeat(100) + " with SUCCESS keyword"; diff --git a/test/backend-test/monitors/test-mqtt.js b/test/backend-test/monitors/test-mqtt.js index dbcf56a83..a361e868e 100644 --- a/test/backend-test/monitors/test-mqtt.js +++ b/test/backend-test/monitors/test-mqtt.js @@ -58,91 +58,91 @@ describe("MqttMonitorType", { concurrency: 4, skip: !!process.env.CI && (process.platform !== "linux" || process.arch !== "x64") }, () => { - test("valid keywords (type=default)", async () => { + test("check() sets status to UP when keyword is found in message (type=default)", async () => { const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-"); assert.strictEqual(heartbeat.status, UP); assert.strictEqual(heartbeat.msg, "Topic: test; Message: -> KEYWORD <-"); }); - test("valid nested topic", async () => { + test("check() sets status to UP when keyword is found in nested topic", async () => { const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/b/c", "a/b/c"); assert.strictEqual(heartbeat.status, UP); assert.strictEqual(heartbeat.msg, "Topic: a/b/c; Message: -> KEYWORD <-"); }); - test("valid nested topic (with special chars)", async () => { + test("check() sets status to UP when keyword is found in nested topic with special characters", async () => { const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/'/$/./*/%", "a/'/$/./*/%"); assert.strictEqual(heartbeat.status, UP); assert.strictEqual(heartbeat.msg, "Topic: a/'/$/./*/%; Message: -> KEYWORD <-"); }); - test("valid wildcard topic (with #)", async () => { + test("check() sets status to UP when keyword is found using # wildcard", async () => { const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/#", "a/b/c"); assert.strictEqual(heartbeat.status, UP); assert.strictEqual(heartbeat.msg, "Topic: a/b/c; Message: -> KEYWORD <-"); }); - test("valid wildcard topic (with +)", async () => { + test("check() sets status to UP when keyword is found using + wildcard", async () => { const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/+/c", "a/b/c"); assert.strictEqual(heartbeat.status, UP); assert.strictEqual(heartbeat.msg, "Topic: a/b/c; Message: -> KEYWORD <-"); }); - test("valid wildcard topic (with + and #)", async () => { + test("check() sets status to UP when keyword is found using + and # wildcards", async () => { const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/+/c/#", "a/b/c/d/e"); assert.strictEqual(heartbeat.status, UP); assert.strictEqual(heartbeat.msg, "Topic: a/b/c/d/e; Message: -> KEYWORD <-"); }); - test("invalid topic", async () => { + test("check() rejects with timeout when topic does not match", async () => { await assert.rejects( testMqtt("keyword will not be checked anyway", null, "message", "x/y/z", "a/b/c"), new Error("Timeout, Message not received"), ); }); - test("invalid wildcard topic (with #)", async () => { + test("check() rejects with timeout when # wildcard is not last character", async () => { await assert.rejects( testMqtt("", null, "# should be last character", "#/c", "a/b/c"), new Error("Timeout, Message not received"), ); }); - test("invalid wildcard topic (with +)", async () => { + test("check() rejects with timeout when + wildcard topic does not match", async () => { await assert.rejects( testMqtt("", null, "message", "x/+/z", "a/b/c"), new Error("Timeout, Message not received"), ); }); - test("valid keywords (type=keyword)", async () => { + test("check() sets status to UP when keyword is found in message (type=keyword)", async () => { const heartbeat = await testMqtt("KEYWORD", "keyword", "-> KEYWORD <-"); assert.strictEqual(heartbeat.status, UP); assert.strictEqual(heartbeat.msg, "Topic: test; Message: -> KEYWORD <-"); }); - test("invalid keywords (type=default)", async () => { + test("check() rejects when keyword is not found in message (type=default)", async () => { await assert.rejects( testMqtt("NOT_PRESENT", null, "-> KEYWORD <-"), new Error("Message Mismatch - Topic: test; Message: -> KEYWORD <-"), ); }); - test("invalid keyword (type=keyword)", async () => { + test("check() rejects when keyword is not found in message (type=keyword)", async () => { await assert.rejects( testMqtt("NOT_PRESENT", "keyword", "-> KEYWORD <-"), new Error("Message Mismatch - Topic: test; Message: -> KEYWORD <-"), ); }); - test("valid json-query", async () => { + test("check() sets status to UP when json-query finds expected value", async () => { // works because the monitors' jsonPath is hard-coded to "firstProp" const heartbeat = await testMqtt("present", "json-query", "{\"firstProp\":\"present\"}"); assert.strictEqual(heartbeat.status, UP); assert.strictEqual(heartbeat.msg, "Message received, expected value is found"); }); - test("invalid (because query fails) json-query", async () => { + test("check() rejects when json-query path returns undefined", async () => { // works because the monitors' jsonPath is hard-coded to "firstProp" await assert.rejects( testMqtt("[not_relevant]", "json-query", "{}"), @@ -150,7 +150,7 @@ describe("MqttMonitorType", { ); }); - test("invalid (because successMessage fails) json-query", async () => { + test("check() rejects when json-query value does not match expected value", async () => { // works because the monitors' jsonPath is hard-coded to "firstProp" await assert.rejects( testMqtt("[wrong_success_messsage]", "json-query", "{\"firstProp\":\"present\"}"), diff --git a/test/backend-test/monitors/test-mssql.js b/test/backend-test/monitors/test-mssql.js index 091f01acc..f265bcdff 100644 --- a/test/backend-test/monitors/test-mssql.js +++ b/test/backend-test/monitors/test-mssql.js @@ -26,7 +26,7 @@ describe( (process.platform !== "linux" || process.arch !== "x64"), }, () => { - test("MSSQL is running", async () => { + test("check() sets status to UP when MSSQL server is reachable", async () => { let mssqlContainer; try { @@ -69,7 +69,7 @@ describe( } }); - test("MSSQL with custom query returning single value", async () => { + test("check() sets status to UP when custom query returns single value", async () => { const mssqlContainer = await createAndStartMSSQLContainer(); const mssqlMonitor = new MssqlMonitorType(); @@ -97,7 +97,7 @@ describe( } }); - test("MSSQL with custom query and condition that passes", async () => { + test("check() sets status to UP when custom query result meets condition", async () => { const mssqlContainer = await createAndStartMSSQLContainer(); const mssqlMonitor = new MssqlMonitorType(); @@ -133,7 +133,7 @@ describe( } }); - test("MSSQL with custom query and condition that fails", async () => { + test("check() rejects when custom query result does not meet condition", async () => { const mssqlContainer = await createAndStartMSSQLContainer(); const mssqlMonitor = new MssqlMonitorType(); @@ -174,7 +174,7 @@ describe( } }); - test("MSSQL query returns no results", async () => { + test("check() rejects when query returns no results", async () => { const mssqlContainer = await createAndStartMSSQLContainer(); const mssqlMonitor = new MssqlMonitorType(); @@ -207,7 +207,7 @@ describe( } }); - test("MSSQL query returns multiple rows", async () => { + test("check() rejects when query returns multiple rows", async () => { const mssqlContainer = await createAndStartMSSQLContainer(); const mssqlMonitor = new MssqlMonitorType(); @@ -240,7 +240,7 @@ describe( } }); - test("MSSQL query returns multiple columns", async () => { + test("check() rejects when query returns multiple columns", async () => { const mssqlContainer = await createAndStartMSSQLContainer(); const mssqlMonitor = new MssqlMonitorType(); @@ -273,7 +273,7 @@ describe( } }); - test("MSSQL is not running", async () => { + test("check() rejects when MSSQL server is not reachable", async () => { const mssqlMonitor = new MssqlMonitorType(); const monitor = { databaseConnectionString: diff --git a/test/backend-test/monitors/test-postgres.js b/test/backend-test/monitors/test-postgres.js index 505e0fc90..098c862a1 100644 --- a/test/backend-test/monitors/test-postgres.js +++ b/test/backend-test/monitors/test-postgres.js @@ -12,7 +12,7 @@ describe( (process.platform !== "linux" || process.arch !== "x64"), }, () => { - test("Postgres is running", async () => { + test("check() sets status to UP when Postgres server is reachable", async () => { // The default timeout of 30 seconds might not be enough for the container to start const postgresContainer = await new PostgreSqlContainer( "postgres:latest" @@ -37,7 +37,7 @@ describe( } }); - test("Postgres is not running", async () => { + test("check() rejects when Postgres server is not reachable", async () => { const postgresMonitor = new PostgresMonitorType(); const monitor = { databaseConnectionString: "http://localhost:15432", diff --git a/test/backend-test/monitors/test-rabbitmq.js b/test/backend-test/monitors/test-rabbitmq.js index 88a04afa1..63d358dfd 100644 --- a/test/backend-test/monitors/test-rabbitmq.js +++ b/test/backend-test/monitors/test-rabbitmq.js @@ -7,7 +7,7 @@ const { UP, PENDING } = require("../../../src/util"); describe("RabbitMQ Single Node", { skip: !!process.env.CI && (process.platform !== "linux" || process.arch !== "x64"), }, () => { - test("RabbitMQ is running", async () => { + test("check() sets status to UP when RabbitMQ server is reachable", async () => { // The default timeout of 30 seconds might not be enough for the container to start const rabbitMQContainer = await new RabbitMQContainer().withStartupTimeout(60000).start(); const rabbitMQMonitor = new RabbitMqMonitorType(); @@ -33,7 +33,7 @@ describe("RabbitMQ Single Node", { } }); - test("RabbitMQ is not running", async () => { + test("check() rejects when RabbitMQ server is not reachable", async () => { const rabbitMQMonitor = new RabbitMqMonitorType(); const monitor = { rabbitmqNodes: JSON.stringify([ "http://localhost:15672" ]), diff --git a/test/backend-test/monitors/test-tcp.js b/test/backend-test/monitors/test-tcp.js index 71f0f2f87..76d093ff4 100644 --- a/test/backend-test/monitors/test-tcp.js +++ b/test/backend-test/monitors/test-tcp.js @@ -4,17 +4,7 @@ const { TCPMonitorType } = require("../../../server/monitor-types/tcp"); const { UP, PENDING } = require("../../../src/util"); const net = require("net"); -/** - * Test suite for TCP Monitor functionality - * This test suite checks the behavior of the TCPMonitorType class - * under different network connection scenarios. - */ describe("TCP Monitor", () => { - /** - * Creates a TCP server on a specified port - * @param {number} port - The port number to listen on - * @returns {Promise} A promise that resolves with the created server - */ async function createTCPServer(port) { return new Promise((resolve, reject) => { const server = net.createServer(); @@ -29,11 +19,7 @@ describe("TCP Monitor", () => { }); } - /** - * Test case to verify TCP monitor works when a server is running - * Checks that the monitor correctly identifies an active TCP server - */ - test("TCP server is running", async () => { + test("check() sets status to UP when TCP server is reachable", async () => { const port = 12345; const server = await createTCPServer(port); @@ -59,11 +45,7 @@ describe("TCP Monitor", () => { } }); - /** - * Test case to verify TCP monitor handles non-running servers - * Checks that the monitor correctly identifies an inactive TCP server - */ - test("TCP server is not running", async () => { + test("check() rejects with connection failed when TCP server is not running", async () => { const tcpMonitor = new TCPMonitorType(); const monitor = { @@ -83,11 +65,7 @@ describe("TCP Monitor", () => { ); }); - /** - * Test case to verify TCP monitor handles servers with expired or invalid TLS certificates - * Checks that the monitor correctly identifies TLS certificate issues - */ - test("TCP server with expired or invalid TLS certificate", async t => { + test("check() rejects when TLS certificate is expired or invalid", async () => { const tcpMonitor = new TCPMonitorType(); const monitor = { @@ -114,7 +92,7 @@ describe("TCP Monitor", () => { ); }); - test("TCP server with valid TLS certificate (SSL)", async t => { + test("check() sets status to UP when TLS certificate is valid (SSL)", async () => { const tcpMonitor = new TCPMonitorType(); const monitor = { @@ -137,7 +115,7 @@ describe("TCP Monitor", () => { assert.strictEqual(heartbeat.status, UP); }); - test("TCP server with valid TLS certificate (STARTTLS)", async t => { + test("check() sets status to UP when TLS certificate is valid (STARTTLS)", async () => { const tcpMonitor = new TCPMonitorType(); const monitor = { @@ -160,7 +138,7 @@ describe("TCP Monitor", () => { assert.strictEqual(heartbeat.status, UP); }); - test("TCP server with valid but name mismatching TLS certificate (STARTTLS)", async t => { + test("check() rejects when TLS certificate hostname does not match (STARTTLS)", async () => { const tcpMonitor = new TCPMonitorType(); const monitor = { @@ -185,7 +163,7 @@ describe("TCP Monitor", () => { regex ); }); - test("XMPP server with valid certificate (STARTTLS)", async t => { + test("check() sets status to UP for XMPP server with valid certificate (STARTTLS)", async () => { const tcpMonitor = new TCPMonitorType(); const monitor = { diff --git a/test/backend-test/monitors/test-websocket.js b/test/backend-test/monitors/test-websocket.js index 545cad466..d2923f1e7 100644 --- a/test/backend-test/monitors/test-websocket.js +++ b/test/backend-test/monitors/test-websocket.js @@ -22,9 +22,9 @@ function nonCompliantWS(port = 8080) { return new Promise((resolve) => srv.listen(port, () => resolve(srv))); } -describe("Websocket Test", { +describe("WebSocket Monitor", { }, () => { - test("Non WS Server", {}, async () => { + test("check() rejects with unexpected server response when connecting to non-WebSocket server", {}, async () => { const websocketMonitor = new WebSocketMonitorType(); const monitor = { @@ -44,7 +44,7 @@ describe("Websocket Test", { ); }); - test("Secure WS", async () => { + test("check() sets status to UP when connecting to secure WebSocket server", async () => { const websocketMonitor = new WebSocketMonitorType(); const monitor = { @@ -68,7 +68,7 @@ describe("Websocket Test", { assert.deepStrictEqual(heartbeat, expected); }); - test("Insecure WS", async (t) => { + test("check() sets status to UP when connecting to insecure WebSocket server", async (t) => { t.after(() => wss.close()); const websocketMonitor = new WebSocketMonitorType(); const wss = new WebSocketServer({ port: 8080 }); @@ -94,7 +94,7 @@ describe("Websocket Test", { assert.deepStrictEqual(heartbeat, expected); }); - test("Non compliant WS Server wrong status code", async () => { + test("check() rejects when status code does not match expected value", async () => { const websocketMonitor = new WebSocketMonitorType(); const monitor = { @@ -115,7 +115,7 @@ describe("Websocket Test", { ); }); - test("Secure WS Server no status code", async () => { + test("check() rejects when expected status code is empty", async () => { const websocketMonitor = new WebSocketMonitorType(); const monitor = { @@ -136,7 +136,7 @@ describe("Websocket Test", { ); }); - test("Non compliant WS server without IgnoreSecWebsocket", async (t) => { + test("check() rejects when Sec-WebSocket-Accept header is invalid", async (t) => { t.after(() => wss.close()); const websocketMonitor = new WebSocketMonitorType(); const wss = await nonCompliantWS(); @@ -159,7 +159,7 @@ describe("Websocket Test", { ); }); - test("Non compliant WS server with IgnoreSecWebsocket", async (t) => { + test("check() sets status to UP when ignoring invalid Sec-WebSocket-Accept header", async (t) => { t.after(() => wss.close()); const websocketMonitor = new WebSocketMonitorType(); const wss = await nonCompliantWS(); @@ -185,7 +185,7 @@ describe("Websocket Test", { assert.deepStrictEqual(heartbeat, expected); }); - test("Compliant WS server with IgnoreSecWebsocket", async () => { + test("check() sets status to UP for compliant WebSocket server when ignoring Sec-WebSocket-Accept", async () => { const websocketMonitor = new WebSocketMonitorType(); const monitor = { @@ -209,7 +209,7 @@ describe("Websocket Test", { assert.deepStrictEqual(heartbeat, expected); }); - test("Non WS server with IgnoreSecWebsocket", async () => { + test("check() rejects non-WebSocket server even when ignoring Sec-WebSocket-Accept", async () => { const websocketMonitor = new WebSocketMonitorType(); const monitor = { @@ -230,7 +230,7 @@ describe("Websocket Test", { ); }); - test("Secure WS no subprotocol support", async () => { + test("check() rejects when server does not support requested subprotocol", async () => { const websocketMonitor = new WebSocketMonitorType(); const monitor = { @@ -252,7 +252,7 @@ describe("Websocket Test", { ); }); - test("Multiple subprotocols invalid input", async () => { + test("check() rejects when multiple subprotocols contain invalid characters", async () => { const websocketMonitor = new WebSocketMonitorType(); const monitor = { @@ -274,7 +274,7 @@ describe("Websocket Test", { ); }); - test("Insecure WS subprotocol multiple spaces", async (t) => { + test("check() sets status to UP when subprotocol with multiple spaces is accepted", async (t) => { t.after(() => wss.close()); const websocketMonitor = new WebSocketMonitorType(); const wss = new WebSocketServer({ port: 8080, @@ -305,7 +305,7 @@ describe("Websocket Test", { assert.deepStrictEqual(heartbeat, expected); }); - test("Insecure WS supports one subprotocol", async (t) => { + test("check() sets status to UP when server supports requested subprotocol", async (t) => { t.after(() => wss.close()); const websocketMonitor = new WebSocketMonitorType(); const wss = new WebSocketServer({ port: 8080, diff --git a/test/backend-test/test-migration.js b/test/backend-test/test-migration.js index 87e5ff9df..19b00dad7 100644 --- a/test/backend-test/test-migration.js +++ b/test/backend-test/test-migration.js @@ -3,7 +3,7 @@ const fs = require("fs"); const path = require("path"); const { GenericContainer, Wait } = require("testcontainers"); -describe("Database Migration - Optimize Important Indexes", () => { +describe("Database Migration", () => { test("SQLite: All migrations run successfully", async () => { const testDbPath = path.join(__dirname, "../../data/test-migration.db"); const testDbDir = path.dirname(testDbPath); From 31285004baf97b9b027df14e282a9c1a184191d3 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 17:06:05 +0100 Subject: [PATCH 19/21] move the maintenance to the utils test --- test/backend-test/test-maintenance.js | 63 --------------------------- test/backend-test/test-util.js | 20 +++++++++ 2 files changed, 20 insertions(+), 63 deletions(-) delete mode 100644 test/backend-test/test-maintenance.js diff --git a/test/backend-test/test-maintenance.js b/test/backend-test/test-maintenance.js deleted file mode 100644 index 27f3ff6b9..000000000 --- a/test/backend-test/test-maintenance.js +++ /dev/null @@ -1,63 +0,0 @@ -const { describe, test } = require("node:test"); -const assert = require("node:assert"); -const dayjs = require("dayjs"); -const { SQL_DATETIME_FORMAT } = require("../../src/util"); - -dayjs.extend(require("dayjs/plugin/utc")); -dayjs.extend(require("dayjs/plugin/customParseFormat")); - -/** - * Tests for maintenance date formatting to ensure compatibility with MariaDB/MySQL. - * Issue: MariaDB rejects ISO format dates like '2025-12-19T01:04:02.129Z' - * Fix: Use SQL_DATETIME_FORMAT ('YYYY-MM-DD HH:mm:ss') instead of toISOString() - */ -describe("Maintenance Date Format - MariaDB Compatibility", () => { - test("SQL_DATETIME_FORMAT constant should match MariaDB format", () => { - assert.strictEqual(SQL_DATETIME_FORMAT, "YYYY-MM-DD HH:mm:ss"); - }); - - test("format() produces SQL-compatible datetime string", () => { - const current = dayjs.utc("2025-12-19T01:04:02.129Z"); - const sqlFormat = current.utc().format(SQL_DATETIME_FORMAT); - - assert.strictEqual(sqlFormat, "2025-12-19 01:04:02"); - }); - - test("SQL format does not contain ISO markers (T, Z)", () => { - const current = dayjs.utc("2025-12-19T01:04:02.129Z"); - const sqlFormat = current.utc().format(SQL_DATETIME_FORMAT); - - assert.strictEqual(sqlFormat.includes("T"), false, "SQL format should not contain 'T'"); - assert.strictEqual(sqlFormat.includes("Z"), false, "SQL format should not contain 'Z'"); - }); - - test("SQL format matches YYYY-MM-DD HH:mm:ss pattern", () => { - const current = dayjs.utc("2025-12-19T01:04:02.129Z"); - const sqlFormat = current.utc().format(SQL_DATETIME_FORMAT); - const sqlDateTimeRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/; - - assert.strictEqual(sqlDateTimeRegex.test(sqlFormat), true); - }); - - test("parsing SQL datetime back to dayjs preserves timestamp", () => { - const originalDate = dayjs.utc("2025-12-19T01:04:02.000Z"); - const sqlFormat = originalDate.utc().format(SQL_DATETIME_FORMAT); - const parsedDate = dayjs.utc(sqlFormat, SQL_DATETIME_FORMAT); - - assert.strictEqual(parsedDate.unix(), originalDate.unix()); - }); - - test("formats midnight timestamp correctly", () => { - const midnight = dayjs.utc("2025-01-01T00:00:00.000Z"); - const sqlFormat = midnight.utc().format(SQL_DATETIME_FORMAT); - - assert.strictEqual(sqlFormat, "2025-01-01 00:00:00"); - }); - - test("formats end of day timestamp correctly", () => { - const endOfDay = dayjs.utc("2025-12-31T23:59:59.999Z"); - const sqlFormat = endOfDay.utc().format(SQL_DATETIME_FORMAT); - - assert.strictEqual(sqlFormat, "2025-12-31 23:59:59"); - }); -}); diff --git a/test/backend-test/test-util.js b/test/backend-test/test-util.js index 63660d4fb..a034df11c 100644 --- a/test/backend-test/test-util.js +++ b/test/backend-test/test-util.js @@ -1,7 +1,12 @@ const { describe, test } = require("node:test"); const assert = require("node:assert"); +const dayjs = require("dayjs"); const { getDaysRemaining, getDaysBetween } = require("../../server/util-server"); +const { SQL_DATETIME_FORMAT } = require("../../src/util"); + +dayjs.extend(require("dayjs/plugin/utc")); +dayjs.extend(require("dayjs/plugin/customParseFormat")); describe("Server Utilities", () => { test("getDaysBetween() calculates days between dates within same month", () => { @@ -23,4 +28,19 @@ describe("Server Utilities", () => { const days = getDaysRemaining(new Date(2025, 9, 10), new Date(2025, 9, 7)); assert.strictEqual(days, -3); }); + + test("SQL_DATETIME_FORMAT constant matches MariaDB/MySQL format", () => { + assert.strictEqual(SQL_DATETIME_FORMAT, "YYYY-MM-DD HH:mm:ss"); + }); + + test("SQL_DATETIME_FORMAT produces valid SQL datetime string", () => { + const current = dayjs.utc("2025-12-19T01:04:02.129Z"); + const sqlFormat = current.utc().format(SQL_DATETIME_FORMAT); + + assert.strictEqual(sqlFormat, "2025-12-19 01:04:02"); + + // Verify it can be parsed back + const parsedDate = dayjs.utc(sqlFormat, SQL_DATETIME_FORMAT); + assert.strictEqual(parsedDate.unix(), current.unix()); + }); }); From 5f0b1f5e4d4ac59b5f10618beecce5edf896a01b Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 17:06:29 +0100 Subject: [PATCH 20/21] improve naming for migration tests --- test/backend-test/test-migration.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/backend-test/test-migration.js b/test/backend-test/test-migration.js index 19b00dad7..0ed494fae 100644 --- a/test/backend-test/test-migration.js +++ b/test/backend-test/test-migration.js @@ -4,7 +4,7 @@ const path = require("path"); const { GenericContainer, Wait } = require("testcontainers"); describe("Database Migration", () => { - test("SQLite: All migrations run successfully", async () => { + test("SQLite migrations run successfully from fresh database", async () => { const testDbPath = path.join(__dirname, "../../data/test-migration.db"); const testDbDir = path.dirname(testDbPath); @@ -57,7 +57,7 @@ describe("Database Migration", () => { }); test( - "MariaDB: All migrations run successfully", + "MariaDB migrations run successfully from fresh database", { skip: !!process.env.CI && From cf1e17f5875fb819490a9a18b0a3381bd174aa79 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 1 Jan 2026 17:08:36 +0100 Subject: [PATCH 21/21] fix lint --- test/backend-test/monitors/test-tcp.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/backend-test/monitors/test-tcp.js b/test/backend-test/monitors/test-tcp.js index 76d093ff4..4626fe34f 100644 --- a/test/backend-test/monitors/test-tcp.js +++ b/test/backend-test/monitors/test-tcp.js @@ -5,6 +5,11 @@ const { UP, PENDING } = require("../../../src/util"); const net = require("net"); describe("TCP Monitor", () => { + /** + * Creates a TCP server on a specified port + * @param {number} port - The port number to listen on + * @returns {Promise} A promise that resolves with the created server + */ async function createTCPServer(port) { return new Promise((resolve, reject) => { const server = net.createServer();