mirror of
https://github.com/certd/certd.git
synced 2026-05-14 20:17:32 +08:00
chore: 补充单元测试
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
/// <reference types="mocha" />
|
||||
|
||||
import { expect } from "chai";
|
||||
|
||||
import { isDev } from "./util.env.js";
|
||||
|
||||
describe("isDev", () => {
|
||||
const originalNodeEnv = process.env.NODE_ENV;
|
||||
|
||||
afterEach(() => {
|
||||
if (originalNodeEnv == null) {
|
||||
delete process.env.NODE_ENV;
|
||||
} else {
|
||||
process.env.NODE_ENV = originalNodeEnv;
|
||||
}
|
||||
});
|
||||
|
||||
it("treats missing NODE_ENV as development", () => {
|
||||
delete process.env.NODE_ENV;
|
||||
|
||||
expect(isDev()).to.equal(true);
|
||||
});
|
||||
|
||||
it("detects development-like NODE_ENV values", () => {
|
||||
process.env.NODE_ENV = "development";
|
||||
expect(isDev()).to.equal(true);
|
||||
|
||||
process.env.NODE_ENV = "local";
|
||||
expect(isDev()).to.equal(true);
|
||||
|
||||
process.env.NODE_ENV = "dev-test";
|
||||
expect(isDev()).to.equal(true);
|
||||
});
|
||||
|
||||
it("rejects production-like NODE_ENV values", () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
expect(isDev()).to.equal(false);
|
||||
|
||||
process.env.NODE_ENV = "test";
|
||||
expect(isDev()).to.equal(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/// <reference types="mocha" />
|
||||
|
||||
import { expect } from "chai";
|
||||
|
||||
import { hashUtils } from "./util.hash.js";
|
||||
|
||||
describe("hashUtils", () => {
|
||||
describe("digest helpers", () => {
|
||||
it("generates md5 and sha256 hex digests by default", () => {
|
||||
expect(hashUtils.md5("certd")).to.equal("3f3d9f715fcc63d54a4a224e0939a233");
|
||||
expect(hashUtils.sha256("certd")).to.equal("26a6366060d2a6477185c05075155769cb438c6c71f61f509535b8516594ad92");
|
||||
});
|
||||
|
||||
it("supports alternate digest encodings", () => {
|
||||
expect(hashUtils.md5("certd", "base64")).to.equal("Pz2fcV/MY9VKSiJOCTmiMw==");
|
||||
expect(hashUtils.sha256("certd", "base64")).to.equal("JqY2YGDSpkdxhcBQdRVXactDjGxx9h9QlTW4UWWUrZI=");
|
||||
});
|
||||
});
|
||||
|
||||
describe("hmac helpers", () => {
|
||||
it("signs data with a provided key", () => {
|
||||
expect(hashUtils.hmacSha256WithKey("secret", "certd")).to.equal("kh/kUD/Ji8FHfpt4vYUHZx+1BZvKSyyklZIiuS+Rzlg=");
|
||||
});
|
||||
|
||||
it("uses an empty payload when only the key is provided", () => {
|
||||
expect(hashUtils.hmacSha256("secret")).to.equal("+eZuF5tnR65UEI+C+K3os8Jddv0wr95sOVgixTAZYWk=");
|
||||
});
|
||||
});
|
||||
|
||||
describe("encoding helpers", () => {
|
||||
it("round trips base64 values", () => {
|
||||
const encoded = hashUtils.base64("证书-certd");
|
||||
|
||||
expect(encoded).to.equal("6K+B5LmmLWNlcnRk");
|
||||
expect(hashUtils.base64Decode(encoded)).to.equal("证书-certd");
|
||||
});
|
||||
|
||||
it("converts strings and numbers to hex", () => {
|
||||
expect(hashUtils.toHex("certd")).to.equal("6365727464");
|
||||
expect(hashUtils.hexToStr("6365727464")).to.equal("certd");
|
||||
expect(hashUtils.toHex(255)).to.equal("ff");
|
||||
expect(hashUtils.hexToNumber("ff")).to.equal(255);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/// <reference types="mocha" />
|
||||
|
||||
import { expect } from "chai";
|
||||
|
||||
import { randomNumber, simpleNanoId } from "./util.id.js";
|
||||
|
||||
describe("id utils", () => {
|
||||
it("generates a four digit random number string", () => {
|
||||
expect(randomNumber()).to.match(/^\d{4}$/);
|
||||
});
|
||||
|
||||
it("generates a twelve character simple nano id", () => {
|
||||
const id = simpleNanoId();
|
||||
|
||||
expect(id).to.have.length(12);
|
||||
expect(id).to.match(/^[0-9a-zA-Z]+$/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
/// <reference types="mocha" />
|
||||
|
||||
import { expect } from "chai";
|
||||
|
||||
import { mergeUtils, UnMergeable } from "./util.merge.js";
|
||||
|
||||
describe("mergeUtils", () => {
|
||||
describe("merge", () => {
|
||||
it("deep merges plain objects", () => {
|
||||
const target = { acme: { email: "admin@example.com" }, deploy: { retries: 1 } };
|
||||
|
||||
const result = mergeUtils.merge(target, { acme: { dnsProvider: "cloudflare" } });
|
||||
|
||||
expect(result).to.equal(target);
|
||||
expect(result).to.deep.equal({
|
||||
acme: { email: "admin@example.com", dnsProvider: "cloudflare" },
|
||||
deploy: { retries: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
it("replaces arrays instead of merging them by index", () => {
|
||||
const result = mergeUtils.merge({ domains: ["old.example.com", "legacy.example.com"] }, { domains: ["new.example.com"] });
|
||||
|
||||
expect(result).to.deep.equal({ domains: ["new.example.com"] });
|
||||
});
|
||||
|
||||
it("allows null to clear nested values", () => {
|
||||
const result = mergeUtils.merge({ cert: { name: "certd" } }, { cert: null });
|
||||
|
||||
expect(result).to.deep.equal({ cert: null });
|
||||
});
|
||||
|
||||
it("keeps undefined sources from overwriting existing nested values", () => {
|
||||
const result = mergeUtils.merge({ cert: { name: "certd" } }, { cert: undefined });
|
||||
|
||||
expect(result).to.deep.equal({ cert: { name: "certd" } });
|
||||
});
|
||||
|
||||
it("returns an UnMergeable source directly when it is merged at the top level", () => {
|
||||
const source = new UnMergeable();
|
||||
|
||||
const result = mergeUtils.merge({ enabled: true }, source);
|
||||
|
||||
expect(result).to.equal(source);
|
||||
});
|
||||
|
||||
it("replaces nested values marked as UnMergeable", () => {
|
||||
const source = new UnMergeable();
|
||||
|
||||
const result = mergeUtils.merge({ plugin: { enabled: true } }, { plugin: source });
|
||||
|
||||
expect(result.plugin).to.equal(source);
|
||||
});
|
||||
});
|
||||
|
||||
describe("cloneDeep", () => {
|
||||
it("deep clones plain values", () => {
|
||||
const source = { acme: { email: "admin@example.com" }, domains: ["example.com"] };
|
||||
|
||||
const result = mergeUtils.cloneDeep(source);
|
||||
|
||||
expect(result).to.deep.equal(source);
|
||||
expect(result).not.to.equal(source);
|
||||
expect(result.acme).not.to.equal(source.acme);
|
||||
expect(result.domains).not.to.equal(source.domains);
|
||||
});
|
||||
|
||||
it("preserves references marked as not cloneable", () => {
|
||||
const uncloneable = new UnMergeable();
|
||||
const source = { plugin: uncloneable };
|
||||
|
||||
const result = mergeUtils.cloneDeep(source);
|
||||
|
||||
expect(result).not.to.equal(source);
|
||||
expect(result.plugin).to.equal(uncloneable);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
/// <reference types="mocha" />
|
||||
|
||||
import { expect } from "chai";
|
||||
|
||||
import { logger } from "./util.log.js";
|
||||
import { promises } from "./util.promise.js";
|
||||
|
||||
describe("promises", () => {
|
||||
describe("TimeoutPromise", () => {
|
||||
it("resolves when the callback finishes before the timeout", async () => {
|
||||
let completed = false;
|
||||
|
||||
await promises.TimeoutPromise(async () => {
|
||||
completed = true;
|
||||
}, 50);
|
||||
|
||||
expect(completed).to.equal(true);
|
||||
});
|
||||
|
||||
it("rejects when the callback exceeds the timeout", async () => {
|
||||
try {
|
||||
await promises.TimeoutPromise(() => new Promise<void>(resolve => setTimeout(resolve, 30)), 5);
|
||||
expect.fail("expected TimeoutPromise to reject");
|
||||
} catch (e: any) {
|
||||
expect(e.message).to.equal("Task timeout in 5 ms");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("safePromise", () => {
|
||||
it("resolves values provided by the callback", async () => {
|
||||
const result = await promises.safePromise<string>(resolve => {
|
||||
resolve("ok");
|
||||
});
|
||||
|
||||
expect(result).to.equal("ok");
|
||||
});
|
||||
|
||||
it("rejects synchronous errors thrown by the callback", async () => {
|
||||
const oldLevel = logger.level;
|
||||
logger.level = "fatal";
|
||||
try {
|
||||
await promises.safePromise(() => {
|
||||
throw new Error("boom");
|
||||
});
|
||||
expect.fail("expected safePromise to reject");
|
||||
} catch (e: any) {
|
||||
expect(e.message).to.equal("boom");
|
||||
} finally {
|
||||
logger.level = oldLevel;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("promisify", () => {
|
||||
it("resolves callback data", async () => {
|
||||
const readValue = promises.promisify((prefix: string, callback: (err: Error | null, data?: string) => void) => {
|
||||
callback(null, `${prefix}-value`);
|
||||
});
|
||||
|
||||
expect(await readValue("certd")).to.equal("certd-value");
|
||||
});
|
||||
|
||||
it("rejects callback errors", async () => {
|
||||
const failing = promises.promisify((callback: (err: Error) => void) => {
|
||||
callback(new Error("callback failed"));
|
||||
});
|
||||
|
||||
try {
|
||||
await failing();
|
||||
expect.fail("expected promisified function to reject");
|
||||
} catch (e: any) {
|
||||
expect(e.message).to.equal("callback failed");
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects synchronous errors", async () => {
|
||||
const failing = promises.promisify(() => {
|
||||
throw new Error("sync failed");
|
||||
});
|
||||
|
||||
try {
|
||||
await failing();
|
||||
expect.fail("expected promisified function to reject");
|
||||
} catch (e: any) {
|
||||
expect(e.message).to.equal("sync failed");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user