quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 8s
quality / changes (push) Successful in 10s
quality / docker-check (push) Skipped
quality / web (push) Successful in 1m8s
quality / api (push) Successful in 1m9s
CD / quality (push) Successful in 2m31s
CD / publish (push) Successful in 1m35s
- Added new endpoints for listing and checking service certificates, improving visibility into SSL status. - Integrated certificate monitoring options into service binding updates, allowing for flexible SSL management. - Updated the service detail grid to include SSL monitoring controls, enhancing user interaction with certificate settings. - Refactored related components and schemas to support the new certificate features, ensuring consistency across the application. - Improved test coverage for certificate functionalities, validating the new features and ensuring reliability.
710 lines
19 KiB
TypeScript
710 lines
19 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { repos } from "@cfdm/db";
|
|
import {
|
|
CERT_ERROR,
|
|
CERT_MONITOR_REQUIRED,
|
|
CERT_MONITOR_SKIPPED,
|
|
} from "@cfdm/shared";
|
|
import { buildApp } from "../src/app.js";
|
|
import { loadConfig } from "../src/config.js";
|
|
import * as certificateService from "../src/services/certificate-service.js";
|
|
|
|
async function authHeaders(app: Awaited<ReturnType<typeof buildApp>>) {
|
|
const config = loadConfig();
|
|
const res = await app.inject({
|
|
method: "POST",
|
|
url: "/api/v1/auth/login",
|
|
payload: { username: config.adminUsername, password: "admin" },
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
const { token } = res.json() as { token: string };
|
|
return { authorization: `Bearer ${token}` };
|
|
}
|
|
|
|
describe("certificates", () => {
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("auto mode does not monitor DNS-only domain", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"dns-only.example.com",
|
|
"cf-zone-dns",
|
|
);
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: null,
|
|
error: "connection refused",
|
|
});
|
|
|
|
const checkRes = await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
expect(checkRes.statusCode).toBe(200);
|
|
|
|
const certs = repos.listCertificates(testApp.db);
|
|
expect(certs.find((c) => c.hostname === domain.zone_name)).toBeUndefined();
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("monitors host with enabled service binding", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"app.example.com",
|
|
"cf-zone-app",
|
|
);
|
|
const service = repos.createService(testApp.db, "Web", "web");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
const binding = repos.insertBinding(
|
|
testApp.db,
|
|
domain.id,
|
|
service.id,
|
|
"api",
|
|
null,
|
|
);
|
|
repos.updateBindingLbConfig(testApp.db, binding.id, {
|
|
health_check_enabled: true,
|
|
health_check_verify_tls: true,
|
|
});
|
|
|
|
const expiresAt = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000);
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt,
|
|
error: null,
|
|
});
|
|
|
|
await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
|
|
const certs = repos.listCertificates(testApp.db);
|
|
expect(certs.some((c) => c.hostname === "api.app.example.com")).toBe(true);
|
|
expect(certs.some((c) => c.hostname === "app.example.com")).toBe(false);
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("does not monitor binding when health-check is off", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"rkns.example.com",
|
|
"cf-zone-imsk",
|
|
);
|
|
const service = repos.createService(testApp.db, "Cname", "cname");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
const binding = repos.insertBinding(
|
|
testApp.db,
|
|
domain.id,
|
|
service.id,
|
|
"imsk",
|
|
null,
|
|
);
|
|
repos.setBindingCnameTarget(testApp.db, binding.id, "ihome.rkns.example.com");
|
|
repos.updateBindingLbConfig(testApp.db, binding.id, {
|
|
health_check_enabled: false,
|
|
});
|
|
|
|
repos.upsertCertificateCheck(
|
|
testApp.db,
|
|
domain.id,
|
|
null,
|
|
"imsk.rkns.example.com",
|
|
null,
|
|
CERT_ERROR,
|
|
"stale",
|
|
);
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
|
|
error: null,
|
|
});
|
|
|
|
await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
|
|
expect(
|
|
repos.listCertificates(testApp.db).some(
|
|
(c) => c.hostname === "imsk.rkns.example.com",
|
|
),
|
|
).toBe(false);
|
|
expect(certificateService.checkHostname).not.toHaveBeenCalled();
|
|
|
|
const listRes = await testApp.inject({
|
|
method: "GET",
|
|
url: "/api/v1/certificates",
|
|
headers,
|
|
});
|
|
expect(listRes.statusCode).toBe(200);
|
|
expect(
|
|
(listRes.json() as { hostname: string }[]).some(
|
|
(c) => c.hostname === "imsk.rkns.example.com",
|
|
),
|
|
).toBe(false);
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("does not monitor host when service is disabled", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"off.example.com",
|
|
"cf-zone-off",
|
|
);
|
|
const service = repos.createService(testApp.db, "Off", "off");
|
|
repos.insertBinding(testApp.db, domain.id, service.id, "@", null);
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
|
|
error: null,
|
|
});
|
|
|
|
await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
|
|
expect(
|
|
repos.listCertificates(testApp.db).some((c) => c.hostname === domain.zone_name),
|
|
).toBe(false);
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("required binding is monitored without TLS health gate", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"required.example.com",
|
|
"cf-zone-req",
|
|
);
|
|
const service = repos.createService(testApp.db, "Req", "req");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
const binding = repos.insertBinding(
|
|
testApp.db,
|
|
domain.id,
|
|
service.id,
|
|
"@",
|
|
null,
|
|
);
|
|
repos.updateBindingLbConfig(testApp.db, binding.id, {
|
|
cert_monitoring: CERT_MONITOR_REQUIRED,
|
|
health_check_enabled: false,
|
|
});
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
|
|
error: null,
|
|
});
|
|
|
|
await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
|
|
expect(
|
|
repos.listCertificates(testApp.db).some(
|
|
(c) => c.hostname === domain.zone_name,
|
|
),
|
|
).toBe(true);
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("skipped binding removes stale certificate on check", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"skipped.example.com",
|
|
"cf-zone-skip",
|
|
);
|
|
const service = repos.createService(testApp.db, "Skip", "skip");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
const binding = repos.insertBinding(
|
|
testApp.db,
|
|
domain.id,
|
|
service.id,
|
|
"@",
|
|
null,
|
|
);
|
|
repos.updateBindingLbConfig(testApp.db, binding.id, {
|
|
cert_monitoring: CERT_MONITOR_SKIPPED,
|
|
health_check_enabled: true,
|
|
health_check_verify_tls: true,
|
|
});
|
|
repos.upsertCertificateCheck(
|
|
testApp.db,
|
|
domain.id,
|
|
null,
|
|
domain.zone_name,
|
|
null,
|
|
CERT_ERROR,
|
|
"stale",
|
|
service.id,
|
|
);
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: null,
|
|
error: "should not be called",
|
|
});
|
|
|
|
await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
|
|
expect(repos.listCertificates(testApp.db)).toHaveLength(0);
|
|
expect(certificateService.checkHostname).not.toHaveBeenCalled();
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("TLS failure on monitored host is stored as error", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"broken.example.com",
|
|
"cf-zone-broken",
|
|
);
|
|
const service = repos.createService(testApp.db, "Broken", "broken");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
const binding = repos.insertBinding(
|
|
testApp.db,
|
|
domain.id,
|
|
service.id,
|
|
"@",
|
|
null,
|
|
);
|
|
repos.updateBindingLbConfig(testApp.db, binding.id, {
|
|
cert_monitoring: CERT_MONITOR_REQUIRED,
|
|
});
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: null,
|
|
error: "certificate has expired",
|
|
});
|
|
|
|
await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
|
|
const cert = repos.listCertificates(testApp.db)[0];
|
|
expect(cert?.status).toBe(CERT_ERROR);
|
|
expect(cert?.last_error).toBeTruthy();
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("skips SSL monitoring when group health is on without TLS verify", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"rkns.example.com",
|
|
"cf-zone-rkns",
|
|
);
|
|
const group = repos.createServiceGroup(
|
|
testApp.db,
|
|
"TG Proxy",
|
|
"vpn",
|
|
null,
|
|
"gt.rkns.example.com",
|
|
{
|
|
health_check_enabled: true,
|
|
health_check_type: "http",
|
|
health_check_port: 443,
|
|
health_check_verify_tls: false,
|
|
},
|
|
);
|
|
const service = repos.createService(testApp.db, "Node", "node");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
repos.setServiceGroup(testApp.db, service.id, group.id);
|
|
repos.insertBinding(testApp.db, domain.id, service.id, "rutg", null);
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
|
|
error: null,
|
|
});
|
|
|
|
await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
|
|
const certs = repos.listCertificates(testApp.db);
|
|
expect(certs.some((c) => c.hostname === "gt.rkns.example.com")).toBe(false);
|
|
expect(certs.some((c) => c.hostname === "rutg.rkns.example.com")).toBe(
|
|
false,
|
|
);
|
|
expect(certificateService.checkHostname).not.toHaveBeenCalled();
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("monitors group hosts when health TLS verify is on", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"ok.example.com",
|
|
"cf-zone-ok",
|
|
);
|
|
const group = repos.createServiceGroup(
|
|
testApp.db,
|
|
"LB",
|
|
"vpn",
|
|
null,
|
|
"lb.ok.example.com",
|
|
{
|
|
health_check_enabled: true,
|
|
health_check_type: "http",
|
|
health_check_port: 443,
|
|
health_check_verify_tls: true,
|
|
},
|
|
);
|
|
const service = repos.createService(testApp.db, "Edge", "edge");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
repos.setServiceGroup(testApp.db, service.id, group.id);
|
|
repos.insertBinding(testApp.db, domain.id, service.id, "edge", null);
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
|
|
error: null,
|
|
});
|
|
|
|
await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
|
|
const certs = repos.listCertificates(testApp.db);
|
|
expect(certs.some((c) => c.hostname === "lb.ok.example.com")).toBe(false);
|
|
expect(certs.some((c) => c.hostname === "edge.ok.example.com")).toBe(true);
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("required mode still monitors when group skips TLS verify", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"force.example.com",
|
|
"cf-zone-force",
|
|
);
|
|
const group = repos.createServiceGroup(
|
|
testApp.db,
|
|
"Proxy",
|
|
"vpn",
|
|
null,
|
|
"force.example.com",
|
|
{
|
|
health_check_enabled: true,
|
|
health_check_verify_tls: false,
|
|
},
|
|
);
|
|
const service = repos.createService(testApp.db, "Force", "force");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
repos.setServiceGroup(testApp.db, service.id, group.id);
|
|
const binding = repos.insertBinding(
|
|
testApp.db,
|
|
domain.id,
|
|
service.id,
|
|
"@",
|
|
null,
|
|
);
|
|
repos.updateBindingLbConfig(testApp.db, binding.id, {
|
|
cert_monitoring: CERT_MONITOR_REQUIRED,
|
|
});
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
|
|
error: null,
|
|
});
|
|
|
|
await testApp.inject({
|
|
method: "POST",
|
|
url: "/api/v1/certificates/check",
|
|
headers,
|
|
});
|
|
|
|
expect(
|
|
repos.listCertificates(testApp.db).some(
|
|
(c) => c.hostname === domain.zone_name,
|
|
),
|
|
).toBe(true);
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("GET /certificates prunes stale rows without running check", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"stale-list.example.com",
|
|
"cf-zone-stale-list",
|
|
);
|
|
const group = repos.createServiceGroup(
|
|
testApp.db,
|
|
"Stale",
|
|
"vpn",
|
|
null,
|
|
"gt.stale-list.example.com",
|
|
{
|
|
health_check_enabled: true,
|
|
health_check_verify_tls: false,
|
|
},
|
|
);
|
|
const service = repos.createService(testApp.db, "S", "s");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
repos.setServiceGroup(testApp.db, service.id, group.id);
|
|
repos.insertBinding(testApp.db, domain.id, service.id, "rutg", null);
|
|
|
|
repos.upsertCertificateCheck(
|
|
testApp.db,
|
|
domain.id,
|
|
null,
|
|
"gt.stale-list.example.com",
|
|
null,
|
|
CERT_ERROR,
|
|
"stale group domain",
|
|
);
|
|
repos.upsertCertificateCheck(
|
|
testApp.db,
|
|
domain.id,
|
|
null,
|
|
"rutg.stale-list.example.com",
|
|
null,
|
|
CERT_ERROR,
|
|
"stale binding",
|
|
);
|
|
|
|
expect(repos.listCertificates(testApp.db)).toHaveLength(2);
|
|
|
|
const listRes = await testApp.inject({
|
|
method: "GET",
|
|
url: "/api/v1/certificates",
|
|
headers,
|
|
});
|
|
expect(listRes.statusCode).toBe(200);
|
|
expect(listRes.json()).toEqual([]);
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("GET /services/:id/certificates lists binding FQDNs", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"svc.example.com",
|
|
"cf-zone-svc",
|
|
);
|
|
const service = repos.createService(testApp.db, "Api", "api");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
repos.insertBinding(testApp.db, domain.id, service.id, "www", null);
|
|
|
|
const res = await testApp.inject({
|
|
method: "GET",
|
|
url: `/api/v1/services/${service.id}/certificates`,
|
|
headers,
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
const rows = res.json() as Array<{
|
|
hostname: string;
|
|
cert_monitoring: string;
|
|
status: string;
|
|
}>;
|
|
expect(rows).toHaveLength(1);
|
|
expect(rows[0]?.hostname).toBe("www.svc.example.com");
|
|
expect(rows[0]?.cert_monitoring).toBe("auto");
|
|
expect(rows[0]?.status).toBe("unknown");
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("PATCH /service-bindings/:id updates cert_monitoring", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"patch.example.com",
|
|
"cf-zone-patch",
|
|
);
|
|
const service = repos.createService(testApp.db, "Patch", "patch");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
const binding = repos.insertBinding(
|
|
testApp.db,
|
|
domain.id,
|
|
service.id,
|
|
"api",
|
|
null,
|
|
);
|
|
|
|
const res = await testApp.inject({
|
|
method: "PATCH",
|
|
url: `/api/v1/service-bindings/${binding.id}`,
|
|
headers,
|
|
payload: { cert_monitoring: CERT_MONITOR_REQUIRED },
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
expect((res.json() as { cert_monitoring: string }).cert_monitoring).toBe(
|
|
CERT_MONITOR_REQUIRED,
|
|
);
|
|
expect(repos.getBinding(testApp.db, binding.id).cert_monitoring).toBe(
|
|
CERT_MONITOR_REQUIRED,
|
|
);
|
|
|
|
await testApp.close();
|
|
});
|
|
|
|
it("POST /services/:id/certificates/check only checks that service", async () => {
|
|
const testApp = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(testApp);
|
|
|
|
const domain = repos.createDomain(
|
|
testApp.db,
|
|
null,
|
|
"check.example.com",
|
|
"cf-zone-check",
|
|
);
|
|
const service = repos.createService(testApp.db, "One", "one");
|
|
const other = repos.createService(testApp.db, "Two", "two");
|
|
repos.setServiceEnabled(testApp.db, service.id, true);
|
|
repos.setServiceEnabled(testApp.db, other.id, true);
|
|
const binding = repos.insertBinding(
|
|
testApp.db,
|
|
domain.id,
|
|
service.id,
|
|
"one",
|
|
null,
|
|
);
|
|
const otherBinding = repos.insertBinding(
|
|
testApp.db,
|
|
domain.id,
|
|
other.id,
|
|
"two",
|
|
null,
|
|
);
|
|
repos.updateBindingLbConfig(testApp.db, binding.id, {
|
|
cert_monitoring: CERT_MONITOR_REQUIRED,
|
|
});
|
|
repos.updateBindingLbConfig(testApp.db, otherBinding.id, {
|
|
cert_monitoring: CERT_MONITOR_REQUIRED,
|
|
});
|
|
|
|
vi.spyOn(certificateService, "checkHostname").mockResolvedValue({
|
|
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
|
|
error: null,
|
|
});
|
|
|
|
const res = await testApp.inject({
|
|
method: "POST",
|
|
url: `/api/v1/services/${service.id}/certificates/check`,
|
|
headers,
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
expect((res.json() as { checked: number }).checked).toBe(1);
|
|
const certs = repos.listCertificates(testApp.db);
|
|
expect(certs.some((c) => c.hostname === "one.check.example.com")).toBe(true);
|
|
expect(certs.some((c) => c.hostname === "two.check.example.com")).toBe(false);
|
|
|
|
await testApp.close();
|
|
});
|
|
});
|