mirror of
https://github.com/YuzuZensai/Cloudflare-DDNS-Updater.git
synced 2026-07-15 14:26:39 +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 {
|
||||
arraysEqual,
|
||||
hasExactKeys,
|
||||
isCloudflareConfig,
|
||||
isEnviromentTokenPlaceholder,
|
||||
isZoneConfig,
|
||||
parseEnvironmentTokenPlaceholderName,
|
||||
} from "./config-validation";
|
||||
|
||||
describe("arraysEqual", () => {
|
||||
test("returns true for identical arrays", () => {
|
||||
expect(arraysEqual(["a", "b"], ["a", "b"])).toBe(true);
|
||||
describe("hasExactKeys", () => {
|
||||
test("returns true when the object has exactly the given keys", () => {
|
||||
expect(hasExactKeys({ a: 1, b: 2 }, ["a", "b"])).toBe(true);
|
||||
});
|
||||
|
||||
test("returns false for different length arrays", () => {
|
||||
expect(arraysEqual(["a"], ["a", "b"])).toBe(false);
|
||||
test("is order independent", () => {
|
||||
expect(hasExactKeys({ b: 2, a: 1 }, ["a", "b"])).toBe(true);
|
||||
});
|
||||
|
||||
test("returns false for different order", () => {
|
||||
expect(arraysEqual(["a", "b"], ["b", "a"])).toBe(false);
|
||||
test("returns false when a key is missing", () => {
|
||||
expect(hasExactKeys({ a: 1 }, ["a", "b"])).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false when either input is nullish", () => {
|
||||
expect(arraysEqual(null, ["a"])).toBe(false);
|
||||
expect(arraysEqual(["a"], undefined)).toBe(false);
|
||||
test("returns false when there is an extra key", () => {
|
||||
expect(hasExactKeys({ a: 1, b: 2, c: 3 }, ["a", "b"])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -74,6 +73,11 @@ describe("isZoneConfig", () => {
|
||||
expect(isZoneConfig(null)).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", () => {
|
||||
@@ -113,6 +117,11 @@ describe("isCloudflareConfig", () => {
|
||||
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", () => {
|
||||
delete process.env.MISSING_TOKEN_VAR;
|
||||
const onMissing = () => {};
|
||||
|
||||
@@ -13,18 +13,11 @@ export interface CloudflareConfig {
|
||||
zone: Array<ZoneConfig>;
|
||||
}
|
||||
|
||||
export function arraysEqual(
|
||||
a: unknown[] | null | undefined,
|
||||
b: unknown[] | null | undefined,
|
||||
): boolean {
|
||||
if (a === b) return true;
|
||||
if (a == null || b == null) return false;
|
||||
if (a.length !== b.length) return false;
|
||||
export function hasExactKeys(object: Record<string, unknown>, keys: string[]): boolean {
|
||||
const objectKeys = Object.keys(object);
|
||||
if (objectKeys.length !== keys.length) return false;
|
||||
|
||||
for (let i = 0; i < a.length; ++i) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
return keys.every((key) => objectKeys.includes(key));
|
||||
}
|
||||
|
||||
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>;
|
||||
|
||||
if (!arraysEqual(Object.keys(object), ["id", "type", "name", "content", "ttl", "proxied"]))
|
||||
return false;
|
||||
if (!hasExactKeys(object, ["id", "type", "name", "content", "ttl", "proxied"])) return false;
|
||||
|
||||
return (
|
||||
!!object.id &&
|
||||
@@ -67,7 +59,7 @@ export function isCloudflareConfig(
|
||||
|
||||
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 =
|
||||
!!object.token &&
|
||||
|
||||
@@ -97,13 +97,13 @@ class Updater {
|
||||
.createRecord({
|
||||
type: zone.type,
|
||||
name: zone.name,
|
||||
content: IPv4,
|
||||
content: newContent,
|
||||
ttl: zone.ttl,
|
||||
proxied: zone.proxied,
|
||||
})
|
||||
.catch((err) => {
|
||||
Logger.error(`Unable to create record: ${err.message}`);
|
||||
return err;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (result) Logger.info(`Created [${zone.type}] (${zone.name} -> ${newContent})`);
|
||||
@@ -129,7 +129,7 @@ class Updater {
|
||||
})
|
||||
.catch((err) => {
|
||||
Logger.error(`Unable to update record: ${err.message}`);
|
||||
return err;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (updateResult)
|
||||
@@ -148,8 +148,7 @@ class Updater {
|
||||
})
|
||||
.catch((err) => {
|
||||
Logger.error(`Unable to update record: ${err.message}`);
|
||||
console.log(err);
|
||||
return;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (updateResult)
|
||||
|
||||
Reference in New Issue
Block a user