@teamscala/service-boot 0.23.385 → 0.23.386
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
import { afterEach, describe, expect, test } from "bun:test";
|
|
20
20
|
import {
|
|
21
21
|
getServerClient,
|
|
22
|
+
getServerClientCapabilityFault,
|
|
22
23
|
isServerClientRegistered,
|
|
24
|
+
recordServerClientCapability,
|
|
23
25
|
setServerClient,
|
|
24
26
|
} from "./server-client.ts";
|
|
25
27
|
|
|
@@ -66,3 +68,45 @@ describe("page-server server client", () => {
|
|
|
66
68
|
expect(isServerClientRegistered()).toBe(true);
|
|
67
69
|
});
|
|
68
70
|
});
|
|
71
|
+
|
|
72
|
+
describe("declared-vs-present capability probe (/health)", () => {
|
|
73
|
+
test("no fault when every declared model is servable", () => {
|
|
74
|
+
setServerClient({
|
|
75
|
+
assessments: { findFirst: async () => null },
|
|
76
|
+
assessment_responses: { findFirst: async () => null },
|
|
77
|
+
} as never);
|
|
78
|
+
recordServerClientCapability(["assessments", "assessment_responses"]);
|
|
79
|
+
expect(getServerClientCapabilityFault()).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("names the models the client cannot serve", () => {
|
|
83
|
+
// THE OUTAGE, AS A HEALTH ANSWER. A client was registered and carried only
|
|
84
|
+
// the platform routers, so every process-existence signal was green while
|
|
85
|
+
// every data-backed page 500d. /health must be able to say this.
|
|
86
|
+
setServerClient({ puckPages: {}, userPreferences: {} } as never);
|
|
87
|
+
recordServerClientCapability(["assessments", "assessment_responses"]);
|
|
88
|
+
const fault = getServerClientCapabilityFault();
|
|
89
|
+
expect(fault).toContain("assessments");
|
|
90
|
+
expect(fault).toContain("assessment_responses");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("a service declaring no models is never faulted", () => {
|
|
94
|
+
// Daemon/portal shells own no DB schema and are healthy with an empty client.
|
|
95
|
+
setServerClient({ puckPages: {} } as never);
|
|
96
|
+
recordServerClientCapability([]);
|
|
97
|
+
expect(getServerClientCapabilityFault()).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("an unregistered client is itself the fault", () => {
|
|
101
|
+
setServerClient(null as never);
|
|
102
|
+
recordServerClientCapability(["assessments"]);
|
|
103
|
+
expect(getServerClientCapabilityFault()).toContain("no ORPC server client registered");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("a model present without findFirst still counts as missing", () => {
|
|
107
|
+
// The generated router shape is what matters, not mere key presence.
|
|
108
|
+
setServerClient({ assessments: {} } as never);
|
|
109
|
+
recordServerClientCapability(["assessments"]);
|
|
110
|
+
expect(getServerClientCapabilityFault()).toContain("assessments");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -55,3 +55,36 @@ export function setServerClient(
|
|
|
55
55
|
): void {
|
|
56
56
|
serverClient = client;
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Declared-vs-present capability, recorded once at boot by
|
|
61
|
+
* registerPageServerClient and read by /health.
|
|
62
|
+
*
|
|
63
|
+
* The outage this exists for: a client WAS registered, so every
|
|
64
|
+
* process-existence signal was green, and it carried no CRUD models. Liveness
|
|
65
|
+
* must be a capability probe, never a registration check
|
|
66
|
+
* (`liveness-by-capability-not-process-existence`).
|
|
67
|
+
*/
|
|
68
|
+
let capabilityFault: string | null = null;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Record whether the registered client can actually serve the models the
|
|
72
|
+
* service declares. Property lookups only — no I/O — so /health stays cheap.
|
|
73
|
+
*/
|
|
74
|
+
export function recordServerClientCapability(declaredModels: readonly string[]): void {
|
|
75
|
+
if (!serverClient) {
|
|
76
|
+
capabilityFault = "no ORPC server client registered";
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const client = serverClient as unknown as Record<string, { findFirst?: unknown } | undefined>;
|
|
80
|
+
const missing = declaredModels.filter((m) => typeof client[m]?.findFirst !== "function");
|
|
81
|
+
capabilityFault =
|
|
82
|
+
missing.length === 0
|
|
83
|
+
? null
|
|
84
|
+
: `generated client exposes no findFirst for ${missing.length} declared model(s): ${missing.slice(0, 5).join(", ")}${missing.length > 5 ? ", …" : ""}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The capability fault, or null when the service can serve its declared models. */
|
|
88
|
+
export function getServerClientCapabilityFault(): string | null {
|
|
89
|
+
return capabilityFault;
|
|
90
|
+
}
|
|
@@ -148,6 +148,9 @@ export async function registerPageServerClient(
|
|
|
148
148
|
} as never,
|
|
149
149
|
);
|
|
150
150
|
mod.setServerClient(client);
|
|
151
|
+
// Record declared-vs-present capability for /health. Property lookups only.
|
|
152
|
+
(mod as unknown as { recordServerClientCapability?: (m: readonly string[]) => void })
|
|
153
|
+
.recordServerClientCapability?.(declaredModels);
|
|
151
154
|
// State the model count. "registered" alone was true in the outage and told
|
|
152
155
|
// nobody anything; a zero here is now visible in the boot log.
|
|
153
156
|
log.info(
|