mirror of
https://github.com/YuzuZensai/Cloudflare-DDNS-Updater.git
synced 2026-07-29 17:00:56 +00:00
🐛 fix: AAAA records with the right address, fix silent update-failure logging, accept configs regardless of key order
This commit is contained in:
@@ -1,28 +1,27 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import {
|
import {
|
||||||
arraysEqual,
|
hasExactKeys,
|
||||||
isCloudflareConfig,
|
isCloudflareConfig,
|
||||||
isEnviromentTokenPlaceholder,
|
isEnviromentTokenPlaceholder,
|
||||||
isZoneConfig,
|
isZoneConfig,
|
||||||
parseEnvironmentTokenPlaceholderName,
|
parseEnvironmentTokenPlaceholderName,
|
||||||
} from "./config-validation";
|
} from "./config-validation";
|
||||||
|
|
||||||
describe("arraysEqual", () => {
|
describe("hasExactKeys", () => {
|
||||||
test("returns true for identical arrays", () => {
|
test("returns true when the object has exactly the given keys", () => {
|
||||||
expect(arraysEqual(["a", "b"], ["a", "b"])).toBe(true);
|
expect(hasExactKeys({ a: 1, b: 2 }, ["a", "b"])).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("returns false for different length arrays", () => {
|
test("is order independent", () => {
|
||||||
expect(arraysEqual(["a"], ["a", "b"])).toBe(false);
|
expect(hasExactKeys({ b: 2, a: 1 }, ["a", "b"])).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("returns false for different order", () => {
|
test("returns false when a key is missing", () => {
|
||||||
expect(arraysEqual(["a", "b"], ["b", "a"])).toBe(false);
|
expect(hasExactKeys({ a: 1 }, ["a", "b"])).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("returns false when either input is nullish", () => {
|
test("returns false when there is an extra key", () => {
|
||||||
expect(arraysEqual(null, ["a"])).toBe(false);
|
expect(hasExactKeys({ a: 1, b: 2, c: 3 }, ["a", "b"])).toBe(false);
|
||||||
expect(arraysEqual(["a"], undefined)).toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -74,6 +73,11 @@ describe("isZoneConfig", () => {
|
|||||||
expect(isZoneConfig(null)).toBe(false);
|
expect(isZoneConfig(null)).toBe(false);
|
||||||
expect(isZoneConfig("not-an-object")).toBe(false);
|
expect(isZoneConfig("not-an-object")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("accepts a zone config whose keys are in a different order", () => {
|
||||||
|
const { proxied, ttl, ...rest } = validZone;
|
||||||
|
expect(isZoneConfig({ ...rest, proxied, ttl })).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("isCloudflareConfig", () => {
|
describe("isCloudflareConfig", () => {
|
||||||
@@ -113,6 +117,11 @@ describe("isCloudflareConfig", () => {
|
|||||||
expect(isCloudflareConfig("not-an-object")).toBe(false);
|
expect(isCloudflareConfig("not-an-object")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("accepts a config whose top-level keys are in a different order", () => {
|
||||||
|
const { zone, ...rest } = validConfig;
|
||||||
|
expect(isCloudflareConfig({ zone, ...rest })).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
test("rejects an env token placeholder whose variable is unset", () => {
|
test("rejects an env token placeholder whose variable is unset", () => {
|
||||||
delete process.env.MISSING_TOKEN_VAR;
|
delete process.env.MISSING_TOKEN_VAR;
|
||||||
const onMissing = () => {};
|
const onMissing = () => {};
|
||||||
|
|||||||
@@ -13,18 +13,11 @@ export interface CloudflareConfig {
|
|||||||
zone: Array<ZoneConfig>;
|
zone: Array<ZoneConfig>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function arraysEqual(
|
export function hasExactKeys(object: Record<string, unknown>, keys: string[]): boolean {
|
||||||
a: unknown[] | null | undefined,
|
const objectKeys = Object.keys(object);
|
||||||
b: unknown[] | null | undefined,
|
if (objectKeys.length !== keys.length) return false;
|
||||||
): boolean {
|
|
||||||
if (a === b) return true;
|
|
||||||
if (a == null || b == null) return false;
|
|
||||||
if (a.length !== b.length) return false;
|
|
||||||
|
|
||||||
for (let i = 0; i < a.length; ++i) {
|
return keys.every((key) => objectKeys.includes(key));
|
||||||
if (a[i] !== b[i]) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEnviromentTokenPlaceholder(token: string): boolean {
|
export function isEnviromentTokenPlaceholder(token: string): boolean {
|
||||||
@@ -41,8 +34,7 @@ export function isZoneConfig(input: unknown): input is ZoneConfig {
|
|||||||
|
|
||||||
const object = input as Record<string, unknown>;
|
const object = input as Record<string, unknown>;
|
||||||
|
|
||||||
if (!arraysEqual(Object.keys(object), ["id", "type", "name", "content", "ttl", "proxied"]))
|
if (!hasExactKeys(object, ["id", "type", "name", "content", "ttl", "proxied"])) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
!!object.id &&
|
!!object.id &&
|
||||||
@@ -67,7 +59,7 @@ export function isCloudflareConfig(
|
|||||||
|
|
||||||
const object = input as Record<string, unknown>;
|
const object = input as Record<string, unknown>;
|
||||||
|
|
||||||
if (!arraysEqual(Object.keys(object), ["token", "updateInterval", "zone"])) return false;
|
if (!hasExactKeys(object, ["token", "updateInterval", "zone"])) return false;
|
||||||
|
|
||||||
const res =
|
const res =
|
||||||
!!object.token &&
|
!!object.token &&
|
||||||
|
|||||||
@@ -97,13 +97,13 @@ class Updater {
|
|||||||
.createRecord({
|
.createRecord({
|
||||||
type: zone.type,
|
type: zone.type,
|
||||||
name: zone.name,
|
name: zone.name,
|
||||||
content: IPv4,
|
content: newContent,
|
||||||
ttl: zone.ttl,
|
ttl: zone.ttl,
|
||||||
proxied: zone.proxied,
|
proxied: zone.proxied,
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
Logger.error(`Unable to create record: ${err.message}`);
|
Logger.error(`Unable to create record: ${err.message}`);
|
||||||
return err;
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result) Logger.info(`Created [${zone.type}] (${zone.name} -> ${newContent})`);
|
if (result) Logger.info(`Created [${zone.type}] (${zone.name} -> ${newContent})`);
|
||||||
@@ -129,7 +129,7 @@ class Updater {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
Logger.error(`Unable to update record: ${err.message}`);
|
Logger.error(`Unable to update record: ${err.message}`);
|
||||||
return err;
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (updateResult)
|
if (updateResult)
|
||||||
@@ -148,8 +148,7 @@ class Updater {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
Logger.error(`Unable to update record: ${err.message}`);
|
Logger.error(`Unable to update record: ${err.message}`);
|
||||||
console.log(err);
|
return undefined;
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (updateResult)
|
if (updateResult)
|
||||||
|
|||||||
Reference in New Issue
Block a user