From be5d27edf9a7bb704e16fa025fe9312ab02c4064 Mon Sep 17 00:00:00 2001 From: Jon <66625200+Kyro3400@users.noreply.github.com> Date: Mon, 11 Nov 2024 20:18:32 -0500 Subject: [PATCH] Create json.test.js --- tests/json.test.js | 99 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/json.test.js diff --git a/tests/json.test.js b/tests/json.test.js new file mode 100644 index 00000000..165c8326 --- /dev/null +++ b/tests/json.test.js @@ -0,0 +1,99 @@ +const t = require("ava"); +const fs = require("fs-extra"); +const path = require("path"); + +const requiredFields = { + owner: "object", + record: "object" +}; + +const optionalFields = { + proxied: "boolean", + reserved: "boolean" +}; + +const requiredOwnerFields = { + username: "string" +}; + +const optionalOwnerFields = { + email: "string" +}; + +const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; +const hostnameRegex = /^(?=.{1,253}$)(?:(?:[_a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.)+[a-zA-Z]{2,63}$/; + +const domainsPath = path.resolve("domains"); +const files = fs.readdirSync(domainsPath); + +function validateRequiredFields(t, obj, requiredFields, file) { + Object.keys(requiredFields).forEach((key) => { + t.true(obj.hasOwnProperty(key), `${file}: Missing required field: ${key}`); + t.is(typeof obj[key], requiredFields[key], `${file}: Field ${key} should be of type ${requiredFields[key]}`); + }); +} + +function validateOptionalFields(t, obj, optionalFields, file) { + Object.keys(optionalFields).forEach((key) => { + if (obj.hasOwnProperty(key)) { + t.is( + typeof obj[key], + optionalFields[key], + `${file}: Field ${key} should be of type ${optionalFields[key]}` + ); + } + }); +} + +t("All files should be valid JSON", (t) => { + files.forEach((file) => { + t.notThrows(() => fs.readJsonSync(path.join(domainsPath, file)), `${file}: Invalid JSON file`); + }); +}); + +t("All files should have valid file names", (t) => { + files.forEach((file) => { + t.true(file.endsWith(".json"), `${file}: File does not have .json extension`); + t.false(file.includes(".is-a-fullstack.dev"), `${file}: File name should not contain .is-a-fullstack.dev`); + t.false(file.includes(".is-a-cool.dev"), `${file}: File name should not contain .is-a-cool.dev`); + t.false(file.includes(".is-local.org"), `${file}: File name should not contain .is-local.org`); + t.false(file.includes(".is-not-a.dev"), `${file}: File name should not contain .is-not-a.dev`); + t.false(file.includes(".localplayer.dev"), `${file}: File name should not contain .localplayer.dev`); + t.true(file === file.toLowerCase(), `${file}: File name should be lowercase`); + + // Ignore root domain + if (file !== "@.json") { + t.regex( + file.replace(/\.json$/, "") + ".is-a-fullstack.dev|.is-a-cool.dev|.is-local.org|.is-not-a.dev|.localplayer.dev", + hostnameRegex, + `${file}: FQDN must be 1-253 characters, use letters, numbers, dots, or hyphens, and not start or end with a hyphen.` + ); + } + }); +}); + +t("All files should have the required fields", (t) => { + files.forEach((file) => { + const data = fs.readJsonSync(path.join(domainsPath, file)); + + validateRequiredFields(t, data, requiredFields, file); + validateRequiredFields(t, data.owner, requiredOwnerFields, file); + + if (!data.reserved) { + t.true(Object.keys(data.record).length > 0, `${file}: No record types found`); + } + }); +}); + +t("All files should have valid optional fields", (t) => { + files.forEach((file) => { + const data = fs.readJsonSync(path.join(domainsPath, file)); + + validateOptionalFields(t, data, optionalFields, file); + validateOptionalFields(t, data.owner, optionalOwnerFields, file); + + if (data.owner.email) { + t.regex(data.owner.email, emailRegex, `${file}: Owner email should be a valid email address`); + } + }); +});