mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-07-18 23:34:04 +02:00
Write tests, more optimizations
This commit is contained in:
parent
a670386281
commit
b0bd8eeac5
6 changed files with 292 additions and 51 deletions
|
@ -4,7 +4,11 @@ const dayjs = require("dayjs");
|
||||||
const { dnsResolve } = require("../util-server");
|
const { dnsResolve } = require("../util-server");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
const { ConditionVariable } = require("../monitor-conditions/variables");
|
const { ConditionVariable } = require("../monitor-conditions/variables");
|
||||||
const { defaultStringOperators, defaultNumberOperators, defaultArrayOperators } = require("../monitor-conditions/operators");
|
const {
|
||||||
|
defaultStringOperators,
|
||||||
|
defaultNumberOperators,
|
||||||
|
defaultArrayOperators
|
||||||
|
} = require("../monitor-conditions/operators");
|
||||||
const { ConditionExpressionGroup } = require("../monitor-conditions/expression");
|
const { ConditionExpressionGroup } = require("../monitor-conditions/expression");
|
||||||
const { evaluateExpressionGroup } = require("../monitor-conditions/evaluator");
|
const { evaluateExpressionGroup } = require("../monitor-conditions/evaluator");
|
||||||
|
|
||||||
|
@ -14,9 +18,9 @@ class DnsMonitorType extends MonitorType {
|
||||||
supportsConditions = true;
|
supportsConditions = true;
|
||||||
|
|
||||||
conditionVariables = [
|
conditionVariables = [
|
||||||
// A, AAAA, TXT, PTR, NS
|
// A, AAAA, TXT, NS
|
||||||
new ConditionVariable("records", defaultArrayOperators),
|
new ConditionVariable("records", defaultArrayOperators),
|
||||||
// CNAME
|
// PTR, CNAME
|
||||||
new ConditionVariable("hostname", defaultStringOperators),
|
new ConditionVariable("hostname", defaultStringOperators),
|
||||||
// CAA
|
// CAA
|
||||||
new ConditionVariable("flags", defaultStringOperators),
|
new ConditionVariable("flags", defaultStringOperators),
|
||||||
|
@ -43,10 +47,11 @@ class DnsMonitorType extends MonitorType {
|
||||||
name: monitor.hostname,
|
name: monitor.hostname,
|
||||||
rrtype: monitor.dnsResolveType,
|
rrtype: monitor.dnsResolveType,
|
||||||
dnssec: true, // Request DNSSEC information in the response
|
dnssec: true, // Request DNSSEC information in the response
|
||||||
dnssecCheckingDisabled: monitor.skip_remote_dnssec,
|
dnssecCheckingDisabled: monitor.skipRemoteDnssec,
|
||||||
};
|
};
|
||||||
const transportData = {
|
const transportData = {
|
||||||
type: monitor.dnsTransport,
|
type: monitor.dnsTransport,
|
||||||
|
timeout: monitor.timeout,
|
||||||
ignoreCertErrors: monitor.ignoreTls,
|
ignoreCertErrors: monitor.ignoreTls,
|
||||||
dohQueryPath: monitor.dohQueryPath,
|
dohQueryPath: monitor.dohQueryPath,
|
||||||
dohUsePost: monitor.method === "POST",
|
dohUsePost: monitor.method === "POST",
|
||||||
|
@ -62,32 +67,37 @@ class DnsMonitorType extends MonitorType {
|
||||||
const conditions = ConditionExpressionGroup.fromMonitor(monitor);
|
const conditions = ConditionExpressionGroup.fromMonitor(monitor);
|
||||||
let conditionsResult = true;
|
let conditionsResult = true;
|
||||||
const handleConditions = (data) => conditions ? evaluateExpressionGroup(conditions, data) : true;
|
const handleConditions = (data) => conditions ? evaluateExpressionGroup(conditions, data) : true;
|
||||||
|
const checkRecord = (results, record) => {
|
||||||
const records = dnsRes.answers.reduce((results, record) => {
|
|
||||||
// Omit records that are not the same as the requested rrtype
|
// Omit records that are not the same as the requested rrtype
|
||||||
if (record.type === monitor.dnsResolveType) {
|
if (record.type === monitor.dnsResolveType) {
|
||||||
|
// Add the record to the array
|
||||||
results.push(Buffer.isBuffer(record.data) ? record.data.toString() : record.data);
|
results.push(Buffer.isBuffer(record.data) ? record.data.toString() : record.data);
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
|
const records = dnsRes.answers.reduce(checkRecord, []);
|
||||||
// Return down status if no records are provided
|
// Return down status if no records are provided
|
||||||
if (records.length === 0) {
|
if (records.length === 0) {
|
||||||
rrtype = null;
|
if (dnsRes.authorities.map(auth => auth.type).includes(monitor.dnsResolveType)) {
|
||||||
dnsMessage = "No records found";
|
records.push(...dnsRes.authorities.reduce(checkRecord, []));
|
||||||
conditionsResult = false;
|
} else {
|
||||||
|
rrtype = null;
|
||||||
|
dnsMessage = "No records found";
|
||||||
|
conditionsResult = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (rrtype) {
|
switch (rrtype) {
|
||||||
case "A":
|
case "A":
|
||||||
case "AAAA":
|
case "AAAA":
|
||||||
case "TXT":
|
case "TXT":
|
||||||
case "PTR":
|
|
||||||
case "NS":
|
case "NS":
|
||||||
dnsMessage = records.join(" | ");
|
dnsMessage = records.join(" | ");
|
||||||
conditionsResult = handleConditions({ records: records });
|
conditionsResult = handleConditions({ records: records });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "PTR":
|
||||||
case "CNAME":
|
case "CNAME":
|
||||||
dnsMessage = records[0];
|
dnsMessage = records[0];
|
||||||
conditionsResult = handleConditions({ hostname: records[0].value });
|
conditionsResult = handleConditions({ hostname: records[0].value });
|
||||||
|
@ -134,7 +144,7 @@ class DnsMonitorType extends MonitorType {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monitor.dnsLastResult !== dnsMessage && dnsMessage !== undefined) {
|
if (monitor.dnsLastResult !== dnsMessage && dnsMessage !== undefined && monitor.id !== undefined) {
|
||||||
await R.exec("UPDATE `monitor` SET dns_last_result = ? WHERE id = ? ", [ dnsMessage, monitor.id ]);
|
await R.exec("UPDATE `monitor` SET dns_last_result = ? WHERE id = ? ", [ dnsMessage, monitor.id ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -521,6 +521,8 @@ exports.dnsResolve = function (opts, resolverServer, resolverPort, transport) {
|
||||||
client.on("error", (err) => {
|
client.on("error", (err) => {
|
||||||
if (err.code === "ETIMEDOUT") {
|
if (err.code === "ETIMEDOUT") {
|
||||||
err.message = `Connection to ${socketName} timed out`;
|
err.message = `Connection to ${socketName} timed out`;
|
||||||
|
} else if (err.code === "ECONNREFUSED") {
|
||||||
|
err.message = `Connection to ${socketName} refused`;
|
||||||
}
|
}
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
@ -638,7 +640,15 @@ exports.dnsResolve = function (opts, resolverServer, resolverPort, transport) {
|
||||||
});
|
});
|
||||||
const req = client.request(headers);
|
const req = client.request(headers);
|
||||||
req.on("error", (err) => {
|
req.on("error", (err) => {
|
||||||
err.message = "HTTP/2: " + err.message;
|
if (err.cause.code === "ETIMEDOUT") {
|
||||||
|
err = err.cause;
|
||||||
|
err.message = `Connection to ${socketName} timed out`;
|
||||||
|
} else if (err.cause.code === "ECONNREFUSED") {
|
||||||
|
err = err.cause;
|
||||||
|
err.message = `Connection to ${socketName} refused`;
|
||||||
|
} else {
|
||||||
|
err.message = "HTTP/2: " + err.message;
|
||||||
|
}
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
req.on("response", (resHeaders) => {
|
req.on("response", (resHeaders) => {
|
||||||
|
@ -670,6 +680,11 @@ exports.dnsResolve = function (opts, resolverServer, resolverPort, transport) {
|
||||||
client.end();
|
client.end();
|
||||||
}
|
}
|
||||||
client.on("error", (err) => {
|
client.on("error", (err) => {
|
||||||
|
if (err.code === "ETIMEDOUT") {
|
||||||
|
err.message = `Connection to ${socketName} timed out`;
|
||||||
|
} else if (err.code === "ECONNREFUSED") {
|
||||||
|
err.message = `Connection to ${socketName} refused`;
|
||||||
|
}
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
client.on("close", () => {
|
client.on("close", () => {
|
||||||
|
|
|
@ -629,6 +629,10 @@ $shadow-box-padding: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#doh-method {
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 770px) {
|
@media (max-width: 770px) {
|
||||||
.toast-container {
|
.toast-container {
|
||||||
margin-bottom: 100px !important;
|
margin-bottom: 100px !important;
|
||||||
|
|
|
@ -371,38 +371,16 @@
|
||||||
<template v-if="monitor.type === 'dns'">
|
<template v-if="monitor.type === 'dns'">
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="dns-resolve-server" class="form-label">{{ $t("Resolver Server") }}</label>
|
<label for="dns-resolve-server" class="form-label">{{ $t("Resolver Server") }}</label>
|
||||||
<input id="dns-resolve-server" ref="dns-resolve-server" v-model="monitor.dnsResolveServer" type="text" class="form-control" :pattern="dnsResolverRegex" required>
|
<input id="dns-resolve-server" ref="dns-resolve-server" v-model="monitor.dnsResolveServer" type="text" class="form-control" :pattern="dnsResolverRegex" required data-testid="resolve-server-input">
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
{{ $t("resolverserverDescription") }}
|
{{ $t("resolverserverDescription") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO center selected option text -->
|
|
||||||
<div class="my-3">
|
|
||||||
<label for="dns-transport" class="form-label">{{ $t("Transport Method") }}</label>
|
|
||||||
<VueMultiselect
|
|
||||||
id="dns-transport"
|
|
||||||
v-model="monitor.dnsTransport"
|
|
||||||
:options="dnsTransportOptions"
|
|
||||||
:multiple="false"
|
|
||||||
:close-on-select="true"
|
|
||||||
:clear-on-select="false"
|
|
||||||
:preserve-search="false"
|
|
||||||
:placeholder="$t('Select the transport method...')"
|
|
||||||
:preselect-first="false"
|
|
||||||
:max-height="500"
|
|
||||||
:taggable="false"
|
|
||||||
data-testid="resolve-type-select"
|
|
||||||
></VueMultiselect>
|
|
||||||
<div class="form-text">
|
|
||||||
{{ $t("dnsTransportDescription") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Port -->
|
<!-- Port -->
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="port" class="form-label">{{ $t("Port") }}</label>
|
<label for="port" class="form-label">{{ $t("Port") }}</label>
|
||||||
<input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
|
<input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1" data-testid="port-input">
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
{{ $t("dnsPortDescription") }}
|
{{ $t("dnsPortDescription") }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -412,7 +390,6 @@
|
||||||
<label for="dns-resolve-type" class="form-label">{{ $t("Resource Record Type") }}</label>
|
<label for="dns-resolve-type" class="form-label">{{ $t("Resource Record Type") }}</label>
|
||||||
|
|
||||||
<!-- :allow-empty="false" is not working, set a default value instead https://github.com/shentao/vue-multiselect/issues/336 -->
|
<!-- :allow-empty="false" is not working, set a default value instead https://github.com/shentao/vue-multiselect/issues/336 -->
|
||||||
<!-- TODO center selected option text -->
|
|
||||||
<VueMultiselect
|
<VueMultiselect
|
||||||
id="dns-resolve-type"
|
id="dns-resolve-type"
|
||||||
v-model="monitor.dnsResolveType"
|
v-model="monitor.dnsResolveType"
|
||||||
|
@ -432,6 +409,27 @@
|
||||||
{{ $t("rrtypeDescription") }}
|
{{ $t("rrtypeDescription") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="dns-transport" class="form-label">{{ $t("Transport Method") }}</label>
|
||||||
|
<VueMultiselect
|
||||||
|
id="dns-transport"
|
||||||
|
v-model="monitor.dnsTransport"
|
||||||
|
:options="dnsTransportOptions"
|
||||||
|
:multiple="false"
|
||||||
|
:close-on-select="true"
|
||||||
|
:clear-on-select="false"
|
||||||
|
:preserve-search="false"
|
||||||
|
:placeholder="$t('Select the transport method...')"
|
||||||
|
:preselect-first="false"
|
||||||
|
:max-height="500"
|
||||||
|
:taggable="false"
|
||||||
|
data-testid="transport-method-select"
|
||||||
|
></VueMultiselect>
|
||||||
|
<div class="form-text">
|
||||||
|
{{ $t("dnsTransportDescription") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Docker Container Name / ID -->
|
<!-- Docker Container Name / ID -->
|
||||||
|
@ -673,11 +671,11 @@
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="my-3 flex-column flex-fill">
|
<div class="my-3 flex-column flex-fill">
|
||||||
<div>
|
<div>
|
||||||
<label for="method" class="form-label">{{ $t("Method") }}</label>
|
<label for="doh-method" class="form-label">{{ $t("Method") }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<div class="d-inline-flex">
|
<div class="d-inline-flex">
|
||||||
<select id="method" v-model="monitor.method" class="form-select">
|
<select id="doh-method" v-model="monitor.method" class="form-select" data-testid="method-select">
|
||||||
<option value="GET">
|
<option value="GET">
|
||||||
GET
|
GET
|
||||||
</option>
|
</option>
|
||||||
|
@ -703,7 +701,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-3 form-check">
|
<div class="my-3 form-check">
|
||||||
<input id="force-http2" v-model="monitor.forceHttp2" class="form-check-input" type="checkbox">
|
<input id="force-http2" v-model="monitor.forceHttp2" class="form-check-input" type="checkbox" data-testid="http2-check">
|
||||||
<label class="form-check-label" for="force-http2">
|
<label class="form-check-label" for="force-http2">
|
||||||
{{ $t("Force HTTP2") }}
|
{{ $t("Force HTTP2") }}
|
||||||
</label>
|
</label>
|
||||||
|
@ -1516,19 +1514,18 @@ message HealthCheckResponse {
|
||||||
},
|
},
|
||||||
|
|
||||||
conditionVariables() {
|
conditionVariables() {
|
||||||
|
// For DNS monitor, the variables depend on rrtype. Conditions for
|
||||||
|
// all rrtypes are added to an array in the DnsMonitorType class in
|
||||||
|
// order to pass to Vue, then identified based on index.
|
||||||
if (this.monitor.type === "dns") {
|
if (this.monitor.type === "dns") {
|
||||||
// When the monitor type is DNS, the conditions depend on
|
|
||||||
// record type. Condition variables are all added to a single
|
|
||||||
// array defined in server\monitor-types\dns.js in order to
|
|
||||||
// pass to Vue, then sliced below based on index.
|
|
||||||
const dnsConditionVariables = this.$root.monitorTypeList["dns"]?.conditionVariables;
|
const dnsConditionVariables = this.$root.monitorTypeList["dns"]?.conditionVariables;
|
||||||
switch (this.monitor.dnsResolveType) {
|
switch (this.monitor.dnsResolveType) {
|
||||||
case "A":
|
case "A":
|
||||||
case "AAAA":
|
case "AAAA":
|
||||||
case "TXT":
|
case "TXT":
|
||||||
case "PTR":
|
|
||||||
case "NS":
|
case "NS":
|
||||||
return dnsConditionVariables.slice(0, 1);
|
return dnsConditionVariables.slice(0, 1);
|
||||||
|
case "PTR":
|
||||||
case "CNAME":
|
case "CNAME":
|
||||||
return dnsConditionVariables.slice(1, 2);
|
return dnsConditionVariables.slice(1, 2);
|
||||||
case "CAA":
|
case "CAA":
|
||||||
|
|
181
test/backend-test/test-dns.js
Normal file
181
test/backend-test/test-dns.js
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
const { describe, test } = require("node:test");
|
||||||
|
const assert = require("node:assert");
|
||||||
|
const { DnsMonitorType } = require("../../server/monitor-types/dns");
|
||||||
|
const { UP, PENDING } = require("../../src/util");
|
||||||
|
|
||||||
|
const queryName = ".";
|
||||||
|
const rrtype = "NS";
|
||||||
|
const sampleRecordRegex = /a\.root-servers\.net/;
|
||||||
|
const bogusServer = "0.0.0.0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs DNS query and checks the result
|
||||||
|
* @param {object} monitorOpts the parameters for the monitor
|
||||||
|
* @returns {Promise<Heartbeat>} the heartbeat produced by the check
|
||||||
|
*/
|
||||||
|
async function testDns(monitorOpts) {
|
||||||
|
if (!monitorOpts.hostname) {
|
||||||
|
monitorOpts.hostname = queryName;
|
||||||
|
}
|
||||||
|
if (!monitorOpts.dnsResolveType) {
|
||||||
|
monitorOpts.dnsResolveType = rrtype;
|
||||||
|
}
|
||||||
|
if (!monitorOpts.conditions) {
|
||||||
|
monitorOpts.conditions = "[]";
|
||||||
|
}
|
||||||
|
const heartbeat = {
|
||||||
|
msg: "",
|
||||||
|
status: PENDING,
|
||||||
|
};
|
||||||
|
const dnsMonitor = new DnsMonitorType();
|
||||||
|
|
||||||
|
await dnsMonitor.check(monitorOpts, heartbeat, {});
|
||||||
|
|
||||||
|
return heartbeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("DNS monitor transport methods", {
|
||||||
|
concurrency: true
|
||||||
|
}, () => {
|
||||||
|
test("DNS (UDP)", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: "1.1.1.1",
|
||||||
|
port: 53,
|
||||||
|
dnsTransport: "UDP",
|
||||||
|
};
|
||||||
|
|
||||||
|
const heartbeat = await testDns(monitor);
|
||||||
|
assert.strictEqual(heartbeat.status, UP);
|
||||||
|
assert.match(heartbeat.msg, sampleRecordRegex);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DNS (UDP) timeout", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: bogusServer,
|
||||||
|
port: 53,
|
||||||
|
dnsTransport: "UDP",
|
||||||
|
timeout: 10000,
|
||||||
|
};
|
||||||
|
|
||||||
|
await assert.rejects(testDns(monitor), {
|
||||||
|
message: /Query to .* timed out/
|
||||||
|
}, "Expected query timeout error");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DNS (TCP)", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: "1.1.1.1",
|
||||||
|
port: 53,
|
||||||
|
dnsTransport: "TCP",
|
||||||
|
};
|
||||||
|
|
||||||
|
const heartbeat = await testDns(monitor);
|
||||||
|
assert.strictEqual(heartbeat.status, UP);
|
||||||
|
assert.match(heartbeat.msg, sampleRecordRegex);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DNS (TCP) timeout", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: bogusServer,
|
||||||
|
port: 53,
|
||||||
|
dnsTransport: "TCP",
|
||||||
|
timeout: 10000,
|
||||||
|
};
|
||||||
|
|
||||||
|
await assert.rejects(testDns(monitor), {
|
||||||
|
message: /Connection to .* (timed out|refused)/
|
||||||
|
}, "Expected connection timeout error");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DNS over TLS", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: "one.one.one.one",
|
||||||
|
port: 853,
|
||||||
|
dnsTransport: "DoT",
|
||||||
|
};
|
||||||
|
|
||||||
|
const heartbeat = await testDns(monitor);
|
||||||
|
assert.strictEqual(heartbeat.status, UP);
|
||||||
|
assert.match(heartbeat.msg, sampleRecordRegex);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DNS over TLS timeout", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: bogusServer,
|
||||||
|
port: 853,
|
||||||
|
dnsTransport: "DoT",
|
||||||
|
timeout: 10000,
|
||||||
|
};
|
||||||
|
|
||||||
|
await assert.rejects(testDns(monitor), {
|
||||||
|
message: /Connection to .* (timed out|refused)/
|
||||||
|
}, "Expected connection timeout error");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DNS over HTTPS (GET)", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: "cloudflare-dns.com",
|
||||||
|
port: 443,
|
||||||
|
dnsTransport: "DoH",
|
||||||
|
dohQueryPath: "dns-query",
|
||||||
|
method: "GET",
|
||||||
|
};
|
||||||
|
|
||||||
|
const heartbeat = await testDns(monitor);
|
||||||
|
assert.strictEqual(heartbeat.status, UP);
|
||||||
|
assert.match(heartbeat.msg, sampleRecordRegex);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DNS over HTTPS timeout", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: bogusServer,
|
||||||
|
port: 443,
|
||||||
|
dnsTransport: "DoH",
|
||||||
|
timeout: 10000,
|
||||||
|
};
|
||||||
|
|
||||||
|
await assert.rejects(testDns(monitor), {
|
||||||
|
message: /Connection to .* (timed out|refused)/
|
||||||
|
}, "Expected connection timeout error");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DNS over HTTP/2 (POST)", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: "cloudflare-dns.com",
|
||||||
|
port: 443,
|
||||||
|
dnsTransport: "DoH",
|
||||||
|
dohQueryPath: "dns-query",
|
||||||
|
method: "POST",
|
||||||
|
forceHttp2: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const heartbeat = await testDns(monitor);
|
||||||
|
assert.strictEqual(heartbeat.status, UP);
|
||||||
|
assert.match(heartbeat.msg, sampleRecordRegex);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DNS over HTTP/2 timeout", async () => {
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
dnsResolveServer: bogusServer,
|
||||||
|
port: 443,
|
||||||
|
dnsTransport: "DoH",
|
||||||
|
timeout: 10000,
|
||||||
|
forceHttp2: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
await assert.rejects(testDns(monitor), {
|
||||||
|
message: /Connection to .* (timed out|refused)/
|
||||||
|
}, "Expected connection timeout error");
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -53,7 +53,7 @@ test.describe("Monitor Form", () => {
|
||||||
|
|
||||||
const friendlyName = "Example DNS NS";
|
const friendlyName = "Example DNS NS";
|
||||||
await page.getByTestId("friendly-name-input").fill(friendlyName);
|
await page.getByTestId("friendly-name-input").fill(friendlyName);
|
||||||
await page.getByTestId("hostname-input").fill("example.com");
|
await page.getByTestId("hostname-input").fill(".");
|
||||||
|
|
||||||
const resolveTypeSelect = page.getByTestId("resolve-type-select");
|
const resolveTypeSelect = page.getByTestId("resolve-type-select");
|
||||||
await resolveTypeSelect.click();
|
await resolveTypeSelect.click();
|
||||||
|
@ -65,9 +65,9 @@ test.describe("Monitor Form", () => {
|
||||||
await page.getByTestId("add-condition-button").click();
|
await page.getByTestId("add-condition-button").click();
|
||||||
expect(await page.getByTestId("condition").count()).toEqual(2); // 2 explicitly added
|
expect(await page.getByTestId("condition").count()).toEqual(2); // 2 explicitly added
|
||||||
|
|
||||||
await page.getByTestId("condition-value").nth(0).fill("a.iana-servers.net");
|
await page.getByTestId("condition-value").nth(0).fill("a.root-servers.net");
|
||||||
await page.getByTestId("condition-and-or").nth(0).selectOption("or");
|
await page.getByTestId("condition-and-or").nth(0).selectOption("or");
|
||||||
await page.getByTestId("condition-value").nth(1).fill("b.iana-servers.net");
|
await page.getByTestId("condition-value").nth(1).fill("b.root-servers.net");
|
||||||
|
|
||||||
await screenshot(testInfo, page);
|
await screenshot(testInfo, page);
|
||||||
await page.getByTestId("save-button").click();
|
await page.getByTestId("save-button").click();
|
||||||
|
@ -86,7 +86,7 @@ test.describe("Monitor Form", () => {
|
||||||
|
|
||||||
const friendlyName = "Example DNS NS";
|
const friendlyName = "Example DNS NS";
|
||||||
await page.getByTestId("friendly-name-input").fill(friendlyName);
|
await page.getByTestId("friendly-name-input").fill(friendlyName);
|
||||||
await page.getByTestId("hostname-input").fill("example.com");
|
await page.getByTestId("hostname-input").fill(".");
|
||||||
|
|
||||||
const resolveTypeSelect = page.getByTestId("resolve-type-select");
|
const resolveTypeSelect = page.getByTestId("resolve-type-select");
|
||||||
await resolveTypeSelect.click();
|
await resolveTypeSelect.click();
|
||||||
|
@ -105,4 +105,38 @@ test.describe("Monitor Form", () => {
|
||||||
|
|
||||||
await screenshot(testInfo, page);
|
await screenshot(testInfo, page);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("dns transport", async ({ page }, testInfo) => {
|
||||||
|
await page.goto("./add");
|
||||||
|
await login(page);
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
await selectMonitorType(page);
|
||||||
|
|
||||||
|
const friendlyName = "Cloudflare";
|
||||||
|
await page.getByTestId("friendly-name-input").fill(friendlyName);
|
||||||
|
await page.getByTestId("hostname-input").fill("one.one.one.one");
|
||||||
|
await page.getByTestId("resolve-server-input").fill("cloudflare-dns.com");
|
||||||
|
await page.getByTestId("port-input").fill("443");
|
||||||
|
|
||||||
|
const resolveTypeSelect = page.getByTestId("resolve-type-select");
|
||||||
|
await resolveTypeSelect.click();
|
||||||
|
await resolveTypeSelect.getByRole("option", { name: "SOA" }).click();
|
||||||
|
|
||||||
|
const transportMethodSelect = page.getByTestId("transport-method-select");
|
||||||
|
await transportMethodSelect.click();
|
||||||
|
await transportMethodSelect.getByRole("option", { name: "DoH" }).click();
|
||||||
|
|
||||||
|
const httpMethodSelect = page.getByTestId("method-select");
|
||||||
|
await httpMethodSelect.selectOption("POST");
|
||||||
|
|
||||||
|
await page.getByTestId("http2-check").check();
|
||||||
|
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
await page.getByTestId("save-button").click();
|
||||||
|
await page.waitForURL("/dashboard/*");
|
||||||
|
|
||||||
|
expect(page.getByTestId("monitor-status")).toHaveText("up", { ignoreCase: true });
|
||||||
|
|
||||||
|
await screenshot(testInfo, page);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue