@rubytech/create-maxy-code 0.1.474 → 0.1.476
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 +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.js +3 -2
- package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
- package/payload/platform/lib/storage-broker/src/cf-exec.ts +3 -2
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +18 -9
- package/payload/platform/plugins/cloudflare/skills/calendar-site/template/schema.sql +13 -3
- package/payload/platform/plugins/docs/references/calendar-booking.md +2 -2
- package/payload/platform/plugins/docs/references/google-guide.md +15 -6
- package/payload/platform/plugins/google/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/google/PLUGIN.md +60 -12
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-audit.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-audit.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-audit.test.js +229 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-audit.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js +9 -9
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/__tests__/consent-url-redirect.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/consent-url-redirect.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/consent-url-redirect.test.js +17 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/consent-url-redirect.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/loopback-listener.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/loopback-listener.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/loopback-listener.test.js +146 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/loopback-listener.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-redirect.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-redirect.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-redirect.test.js +19 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-redirect.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.js +41 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/account-audit.d.ts +44 -0
- package/payload/platform/plugins/google/mcp/dist/auth/account-audit.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/account-audit.js +112 -0
- package/payload/platform/plugins/google/mcp/dist/auth/account-audit.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/loopback-listener.d.ts +17 -0
- package/payload/platform/plugins/google/mcp/dist/auth/loopback-listener.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/loopback-listener.js +143 -0
- package/payload/platform/plugins/google/mcp/dist/auth/loopback-listener.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts +9 -0
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/index.js +73 -5
- package/payload/platform/plugins/google/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/scripts/account-audit.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/scripts/account-audit.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/scripts/account-audit.js +37 -0
- package/payload/platform/plugins/google/mcp/dist/scripts/account-audit.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts +20 -4
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.js +19 -9
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.js.map +1 -1
- package/payload/platform/plugins/google/references/auth.md +93 -14
- package/payload/platform/plugins/scheduling/PLUGIN.md +2 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js +158 -23
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.js +200 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js +309 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/d1-command.test.js +219 -19
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/d1-command.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +94 -11
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +106 -22
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.d.ts +109 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js +213 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.d.ts +80 -8
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.js +202 -8
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts +6 -4
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +334 -142
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts +21 -3
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js +6 -2
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +43 -5
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.d.ts +9 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.js +78 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.js.map +1 -1
- package/payload/server/{chunk-Y6V45R6M.js → chunk-LBCMFD4O.js} +173 -29
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/activity.html +6 -6
- package/payload/server/public/assets/{AdminLoginScreens-B44KbEuC.js → AdminLoginScreens-DximwPlS.js} +1 -1
- package/payload/server/public/assets/{AdminShell-BjxV2qkb.js → AdminShell-BehbH-Oa.js} +1 -1
- package/payload/server/public/assets/{Checkbox-DSeZNGNA.js → Checkbox-DM-eHqOS.js} +1 -1
- package/payload/server/public/assets/activity-hDwUeQd6.js +1 -0
- package/payload/server/public/assets/{admin-B4LpTCur.js → admin-CAkDnGdk.js} +1 -1
- package/payload/server/public/assets/{browser-CEFvaWwI.js → browser-DXl8hOoi.js} +1 -1
- package/payload/server/public/assets/{calendar-B3zo6V34.js → calendar-DP6hn4-6.js} +1 -1
- package/payload/server/public/assets/chat-DhOfVGYM.js +1 -0
- package/payload/server/public/assets/chevron-left-DYNBoLii.js +1 -0
- package/payload/server/public/assets/data-bq8fTo_s.js +1 -0
- package/payload/server/public/assets/{graph-RaOrnSf2.js → graph-CHcYoEJ5.js} +1 -1
- package/payload/server/public/assets/{graph-labels-GlTEIYJj.js → graph-labels-CZykslZM.js} +1 -1
- package/payload/server/public/assets/{maximize-2-CHDa8p5h.js → maximize-2-31AZEcMS.js} +1 -1
- package/payload/server/public/assets/{operator-DtqHY4jV.js → operator-BGGpdsO_.js} +1 -1
- package/payload/server/public/assets/{page-CkhFj8GQ.js → page-BloC6ygA.js} +1 -1
- package/payload/server/public/assets/{page-DxnWgPtd.js → page-DOeiiqbR.js} +1 -1
- package/payload/server/public/assets/{public-D5TUfQ3I.js → public-CGgO6IZv.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-BJjVAQQY.js → rotate-ccw-BlAi6iFg.js} +1 -1
- package/payload/server/public/assets/{tasks-CM9pYYG1.js → tasks-C1giFRRL.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-C5RYEYjM.js → time-entry-format-CiUUQdq7.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-DLfIsuo_.js → triangle-alert-CVqKuLoa.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-7yKeiAzV.js → useCopyFeedback-ksyUe_g1.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-CWlh6AEq.js → useSelectionMode-BPrBkdTd.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-B2n9QPVI.css → useSubAccountSwitcher-K4kYeN6i.css} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-j3ZArZ2H.js → useVoiceRecorder-CTXP8oTW.js} +1 -1
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +12 -12
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +11 -11
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +12 -12
- package/payload/server/public/tasks.html +5 -5
- package/payload/server/server.js +547 -423
- package/payload/server/public/assets/activity-CdipMTDp.js +0 -1
- package/payload/server/public/assets/chat-5tJdo9i_.js +0 -1
- package/payload/server/public/assets/chevron-left-BJllrPBj.js +0 -1
- package/payload/server/public/assets/data-Du79EgR-.js +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-CBlKgwms.js → useSubAccountSwitcher-DzgzTRRn.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"booking-schema-migration.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/booking-schema-migration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connector-sync-provision.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/connector-sync-provision.test.ts"],"names":[],"mappings":""}
|
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// Task 1809 — the connector-sync provisioning gate.
|
|
2
|
+
//
|
|
3
|
+
// The defect these tests pin had two stacked causes. `readAdminPhone` read a
|
|
4
|
+
// top-level `adminPhones` key that exists on no account (the authoritative key
|
|
5
|
+
// is `whatsapp.adminPhones`), so the gate never opened anywhere; and the gate
|
|
6
|
+
// was on a reply destination rather than on the work's own input, so an account
|
|
7
|
+
// with a calendar and no WhatsApp number could never sync even once the field
|
|
8
|
+
// path was right.
|
|
9
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
10
|
+
import { mkdtempSync, mkdirSync, writeFileSync, chmodSync, existsSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { detectCalendarConnectors, resolveDispatchDestination, shouldEmitProvisionLine, provisionSignature, emitLabel, readProvisionState, writeProvisionState, } from "../connector-sync-provision.js";
|
|
14
|
+
const HOUSE = "11111111-1111-1111-1111-111111111111";
|
|
15
|
+
/** A temp accounts dir holding one account with the given account.json. */
|
|
16
|
+
function accountsDirWith(accountId, config) {
|
|
17
|
+
const dir = mkdtempSync(join(tmpdir(), "cal-sync-prov-"));
|
|
18
|
+
const accountDir = join(dir, accountId);
|
|
19
|
+
mkdirSync(accountDir, { recursive: true });
|
|
20
|
+
writeFileSync(join(accountDir, "account.json"), JSON.stringify(config));
|
|
21
|
+
return dir;
|
|
22
|
+
}
|
|
23
|
+
function writeConnector(accountDir, rel, key) {
|
|
24
|
+
const dir = join(accountDir, "secrets", ...rel, key);
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
writeFileSync(join(dir, "tokens.enc"), "cipher");
|
|
27
|
+
}
|
|
28
|
+
describe("detectCalendarConnectors", () => {
|
|
29
|
+
it("reports google from an authorised google account token", () => {
|
|
30
|
+
const dir = accountsDirWith(HOUSE, {});
|
|
31
|
+
const accountDir = join(dir, HOUSE);
|
|
32
|
+
writeConnector(accountDir, ["google", "accounts"], "someone_gmail.com");
|
|
33
|
+
expect(detectCalendarConnectors(accountDir)).toEqual(["google"]);
|
|
34
|
+
});
|
|
35
|
+
it("reports m365 from an authorised outlook mailbox token", () => {
|
|
36
|
+
const dir = accountsDirWith(HOUSE, {});
|
|
37
|
+
const accountDir = join(dir, HOUSE);
|
|
38
|
+
writeConnector(accountDir, ["outlook", "mailboxes"], "60273abb803be05b");
|
|
39
|
+
expect(detectCalendarConnectors(accountDir)).toEqual(["m365"]);
|
|
40
|
+
});
|
|
41
|
+
it("reports both, sorted, when both are authorised", () => {
|
|
42
|
+
const dir = accountsDirWith(HOUSE, {});
|
|
43
|
+
const accountDir = join(dir, HOUSE);
|
|
44
|
+
writeConnector(accountDir, ["google", "accounts"], "someone_gmail.com");
|
|
45
|
+
writeConnector(accountDir, ["outlook", "mailboxes"], "60273abb803be05b");
|
|
46
|
+
expect(detectCalendarConnectors(accountDir)).toEqual(["google", "m365"]);
|
|
47
|
+
});
|
|
48
|
+
it("reports none when the connector directory exists but holds no token", () => {
|
|
49
|
+
const dir = accountsDirWith(HOUSE, {});
|
|
50
|
+
const accountDir = join(dir, HOUSE);
|
|
51
|
+
mkdirSync(join(accountDir, "secrets", "google", "accounts", "half-registered"), { recursive: true });
|
|
52
|
+
expect(detectCalendarConnectors(accountDir)).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
it("reports none when the account has no secrets directory at all", () => {
|
|
55
|
+
const dir = accountsDirWith(HOUSE, {});
|
|
56
|
+
expect(detectCalendarConnectors(join(dir, HOUSE))).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
// An unreadable connector directory means the gate's input is UNKNOWN, not
|
|
59
|
+
// empty. Reporting it as `not-applicable` would dress a real fault as the
|
|
60
|
+
// boring expected state — the exact reading that let 7,868 `skipped` lines
|
|
61
|
+
// pass as benign for months.
|
|
62
|
+
it("throws rather than reporting none when the directory exists but cannot be read", () => {
|
|
63
|
+
const dir = accountsDirWith(HOUSE, {});
|
|
64
|
+
const accountDir = join(dir, HOUSE);
|
|
65
|
+
const connectorDir = join(accountDir, "secrets", "google", "accounts");
|
|
66
|
+
mkdirSync(connectorDir, { recursive: true });
|
|
67
|
+
chmodSync(connectorDir, 0o000);
|
|
68
|
+
try {
|
|
69
|
+
expect(() => detectCalendarConnectors(accountDir)).toThrow(/unreadable/);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
chmodSync(connectorDir, 0o755);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("resolveDispatchDestination", () => {
|
|
77
|
+
// The house is the socket-owning authority for WhatsApp, so a house account's
|
|
78
|
+
// own `whatsapp.adminPhones` is the authoritative list and validates.
|
|
79
|
+
it("resolves the house's first registered WhatsApp admin phone", () => {
|
|
80
|
+
const dir = accountsDirWith(HOUSE, {
|
|
81
|
+
role: "house",
|
|
82
|
+
whatsapp: { adminPhones: ["+447504472444"] },
|
|
83
|
+
});
|
|
84
|
+
expect(resolveDispatchDestination(dir, HOUSE)).toEqual({
|
|
85
|
+
destination: { channel: "whatsapp", destination: "+447504472444" },
|
|
86
|
+
reason: null,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
// THE GATE-IS-GONE TEST. Before Task 1809 an account with no WhatsApp number
|
|
90
|
+
// was skipped outright, whatever else it had. A Telegram admin is a perfectly
|
|
91
|
+
// good exception destination and must now provision.
|
|
92
|
+
it("falls back to Telegram when the account has no WhatsApp admin phone", () => {
|
|
93
|
+
const dir = accountsDirWith(HOUSE, {
|
|
94
|
+
role: "house",
|
|
95
|
+
telegram: { adminUsers: [123456789] },
|
|
96
|
+
});
|
|
97
|
+
expect(resolveDispatchDestination(dir, HOUSE)).toEqual({
|
|
98
|
+
destination: { channel: "telegram", destination: "123456789" },
|
|
99
|
+
reason: null,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
// Task 1812 must not silently move existing accounts onto a new surface.
|
|
103
|
+
it("prefers WhatsApp when both channels are configured", () => {
|
|
104
|
+
const dir = accountsDirWith(HOUSE, {
|
|
105
|
+
role: "house",
|
|
106
|
+
whatsapp: { adminPhones: ["+447504472444"] },
|
|
107
|
+
telegram: { adminUsers: [123456789] },
|
|
108
|
+
});
|
|
109
|
+
expect(resolveDispatchDestination(dir, HOUSE).destination?.channel).toBe("whatsapp");
|
|
110
|
+
});
|
|
111
|
+
// THE FIELD-PATH TEST. `readAdminPhone` read this key. It exists on no
|
|
112
|
+
// account on the install, which is why every tick logged `skipped`. Reading
|
|
113
|
+
// it must not resolve a destination — otherwise the fix is cosmetic.
|
|
114
|
+
it("does not resolve from the legacy top-level adminPhones key", () => {
|
|
115
|
+
const dir = accountsDirWith(HOUSE, {
|
|
116
|
+
role: "house",
|
|
117
|
+
adminPhones: ["+447504472444"],
|
|
118
|
+
});
|
|
119
|
+
expect(resolveDispatchDestination(dir, HOUSE)).toEqual({
|
|
120
|
+
destination: null,
|
|
121
|
+
reason: "none-configured",
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
it("resolves nothing when neither channel is configured", () => {
|
|
125
|
+
const dir = accountsDirWith(HOUSE, { role: "house" });
|
|
126
|
+
expect(resolveDispatchDestination(dir, HOUSE)).toEqual({
|
|
127
|
+
destination: null,
|
|
128
|
+
reason: "none-configured",
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
// A destination the standing audit would flag as stale on the very next tick
|
|
132
|
+
// must never be armed: provisioning and the audit share one authority. The
|
|
133
|
+
// reason must survive to the caller — an operator told `unconfigured` at an
|
|
134
|
+
// account whose account.json visibly holds a phone number has no path from
|
|
135
|
+
// the log line to the cause.
|
|
136
|
+
it("refuses a WhatsApp number that is registered to no account, and says why", () => {
|
|
137
|
+
const dir = accountsDirWith(HOUSE, {
|
|
138
|
+
role: "house",
|
|
139
|
+
whatsapp: { adminPhones: ["not-a-phone"] },
|
|
140
|
+
});
|
|
141
|
+
expect(resolveDispatchDestination(dir, HOUSE)).toEqual({
|
|
142
|
+
destination: null,
|
|
143
|
+
reason: "not-registered",
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
// A number registered to a DIFFERENT account is a distinct, more actionable
|
|
147
|
+
// diagnosis than "not registered", and validateAgentDestination computes it.
|
|
148
|
+
it("reports cross-account rather than collapsing it into not-registered", () => {
|
|
149
|
+
const SUB = "22222222-2222-2222-2222-222222222222";
|
|
150
|
+
const OTHER = "33333333-3333-3333-3333-333333333333";
|
|
151
|
+
const dir = accountsDirWith(HOUSE, {
|
|
152
|
+
role: "house",
|
|
153
|
+
whatsapp: { adminPhones: [], accountManagers: { "+447700900123": OTHER } },
|
|
154
|
+
});
|
|
155
|
+
const subDir = join(dir, SUB);
|
|
156
|
+
mkdirSync(subDir, { recursive: true });
|
|
157
|
+
writeFileSync(join(subDir, "account.json"), JSON.stringify({ role: "client", whatsapp: { adminPhones: ["+447700900123"] } }));
|
|
158
|
+
expect(resolveDispatchDestination(dir, SUB)).toEqual({
|
|
159
|
+
destination: null,
|
|
160
|
+
reason: "cross-account",
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
// main() enumerates accounts by the presence of account.json, so by the time
|
|
164
|
+
// this runs "malformed" is the likely cause and "absent" the unlikely one.
|
|
165
|
+
// Reporting a corrupt config as "no destination configured" tells the
|
|
166
|
+
// operator to add a phone number when the real fact is far larger.
|
|
167
|
+
it("distinguishes an unreadable account.json from an unconfigured one", () => {
|
|
168
|
+
const dir = accountsDirWith(HOUSE, { role: "house" });
|
|
169
|
+
writeFileSync(join(dir, HOUSE, "account.json"), "{not json");
|
|
170
|
+
expect(resolveDispatchDestination(dir, HOUSE)).toEqual({
|
|
171
|
+
destination: null,
|
|
172
|
+
reason: "unreadable-config",
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
describe("shouldEmitProvisionLine", () => {
|
|
177
|
+
const HOUR = 60 * 60 * 1000;
|
|
178
|
+
const HEARTBEAT = 6 * HOUR;
|
|
179
|
+
const entry = (outcome, loggedAtMs, detail) => ({
|
|
180
|
+
outcome,
|
|
181
|
+
signature: provisionSignature(outcome, detail),
|
|
182
|
+
loggedAtMs,
|
|
183
|
+
});
|
|
184
|
+
it("emits when there is no prior state for the account", () => {
|
|
185
|
+
expect(shouldEmitProvisionLine(undefined, "not-applicable", 1_000, HEARTBEAT)).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
it("emits when the outcome changed", () => {
|
|
188
|
+
expect(shouldEmitProvisionLine(entry("unconfigured", 1_000), "provisioned", 1_000 + HOUR, HEARTBEAT)).toBe(true);
|
|
189
|
+
});
|
|
190
|
+
// The whole point: 5,688 identical lines is noise that hides its own meaning.
|
|
191
|
+
it("stays silent on an unchanged outcome inside the heartbeat window", () => {
|
|
192
|
+
expect(shouldEmitProvisionLine(entry("already-provisioned", 1_000), "already-provisioned", 1_000 + HOUR, HEARTBEAT)).toBe(false);
|
|
193
|
+
});
|
|
194
|
+
it("emits again once the heartbeat window has elapsed", () => {
|
|
195
|
+
expect(shouldEmitProvisionLine(entry("already-provisioned", 1_000), "already-provisioned", 1_000 + HEARTBEAT, HEARTBEAT)).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
// The heartbeat is the only signal separating "steady state" from "this loop
|
|
198
|
+
// stopped running". An NTP step backwards on an RTC-less Pi must not mute it.
|
|
199
|
+
it("emits rather than muting the heartbeat when the clock steps backwards", () => {
|
|
200
|
+
expect(shouldEmitProvisionLine(entry("already-provisioned", 10 * HOUR), "already-provisioned", 1_000, HEARTBEAT)).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
// A persistent Neo4j fault must not log at tick rate forever — that is the
|
|
203
|
+
// volume profile this task exists to remove — but a CHANGED error is news.
|
|
204
|
+
it("treats an unchanged error as a steady state and a changed one as news", () => {
|
|
205
|
+
const prev = entry("error", 1_000, "Neo4j unreachable");
|
|
206
|
+
expect(shouldEmitProvisionLine(prev, provisionSignature("error", "Neo4j unreachable"), 1_000 + HOUR, HEARTBEAT)).toBe(false);
|
|
207
|
+
expect(shouldEmitProvisionLine(prev, provisionSignature("error", "constraint violation"), 1_000 + HOUR, HEARTBEAT)).toBe(true);
|
|
208
|
+
});
|
|
209
|
+
// Recovery must be visible. Before errors entered the state map, an account
|
|
210
|
+
// returning to health read as "unchanged" and went silent for six hours.
|
|
211
|
+
it("reports recovery from an error immediately", () => {
|
|
212
|
+
const prev = entry("error", 1_000, "Neo4j unreachable");
|
|
213
|
+
expect(shouldEmitProvisionLine(prev, provisionSignature("already-provisioned"), 1_000 + HOUR, HEARTBEAT)).toBe(true);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
describe("emitLabel", () => {
|
|
217
|
+
// A first observation is not a change. Labelling it as one means any state
|
|
218
|
+
// loss produces a synchronised fleet-wide burst of `emit=change`, which is
|
|
219
|
+
// indistinguishable from every account genuinely flipping at once.
|
|
220
|
+
it("labels a first observation `first`, never `change`", () => {
|
|
221
|
+
expect(emitLabel(undefined, "not-applicable")).toBe("first");
|
|
222
|
+
});
|
|
223
|
+
it("labels a genuine outcome change `change`", () => {
|
|
224
|
+
expect(emitLabel({ outcome: "unconfigured", signature: "unconfigured", loggedAtMs: 1 }, "provisioned")).toBe("change");
|
|
225
|
+
});
|
|
226
|
+
it("labels an unchanged outcome `heartbeat`", () => {
|
|
227
|
+
expect(emitLabel({ outcome: "provisioned", signature: "provisioned", loggedAtMs: 1 }, "provisioned")).toBe("heartbeat");
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
describe("provision state round-trip", () => {
|
|
231
|
+
let dir;
|
|
232
|
+
beforeEach(() => {
|
|
233
|
+
dir = mkdtempSync(join(tmpdir(), "cal-sync-state-"));
|
|
234
|
+
});
|
|
235
|
+
const ENTRY = { outcome: "provisioned", signature: "provisioned", loggedAtMs: 42 };
|
|
236
|
+
it("round-trips per-account outcomes", () => {
|
|
237
|
+
const path = join(dir, "cal-sync-provision-state.json");
|
|
238
|
+
writeProvisionState(path, { [HOUSE]: ENTRY });
|
|
239
|
+
expect(readProvisionState(path)).toEqual({ [HOUSE]: ENTRY });
|
|
240
|
+
});
|
|
241
|
+
it("leaves no temp file behind, so the write is atomic", () => {
|
|
242
|
+
const path = join(dir, "cal-sync-provision-state.json");
|
|
243
|
+
writeProvisionState(path, { [HOUSE]: ENTRY });
|
|
244
|
+
expect(existsSync(`${path}.tmp`)).toBe(false);
|
|
245
|
+
});
|
|
246
|
+
it("reads an absent state file as empty and silently — that is the first run", () => {
|
|
247
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
248
|
+
expect(readProvisionState(join(dir, "missing.json"))).toEqual({});
|
|
249
|
+
expect(spy).not.toHaveBeenCalled();
|
|
250
|
+
});
|
|
251
|
+
// A corrupt state file triggers a fleet-wide `emit=first` burst. That burst
|
|
252
|
+
// must carry its own explanation, or it reads as every account flipping at once.
|
|
253
|
+
it("reads a corrupt state file as empty but says so", () => {
|
|
254
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
255
|
+
const bad = join(dir, "bad.json");
|
|
256
|
+
writeFileSync(bad, "{not json");
|
|
257
|
+
expect(readProvisionState(bad)).toEqual({});
|
|
258
|
+
expect(spy).toHaveBeenCalledWith(expect.stringContaining("result=unreadable"));
|
|
259
|
+
});
|
|
260
|
+
// Every realistic cause of a write failure is persistent, so the failure
|
|
261
|
+
// degrades emit-on-change back to per-tick logging indefinitely. Silence
|
|
262
|
+
// here would make that degradation undetectable.
|
|
263
|
+
it("reports a failed write and names the impact rather than swallowing it", () => {
|
|
264
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
265
|
+
const readOnly = join(dir, "read-only");
|
|
266
|
+
mkdirSync(readOnly);
|
|
267
|
+
chmodSync(readOnly, 0o500);
|
|
268
|
+
try {
|
|
269
|
+
writeProvisionState(join(readOnly, "state.json"), { [HOUSE]: ENTRY });
|
|
270
|
+
expect(spy).toHaveBeenCalledWith(expect.stringContaining("result=write-failed"));
|
|
271
|
+
expect(spy).toHaveBeenCalledWith(expect.stringContaining("impact=emit-on-change-degraded-to-per-tick"));
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
chmodSync(readOnly, 0o755);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
// The mutation check the task calls for. A permanently unconfigured account is
|
|
279
|
+
// the case most at risk of being silenced by the very change that quietens the
|
|
280
|
+
// per-tick noise. This asserts the outcome reaches the operator on change, and
|
|
281
|
+
// the guard below proves the assertion has teeth.
|
|
282
|
+
describe("an unconfigured account is reported, not swallowed", () => {
|
|
283
|
+
const HEARTBEAT = 6 * 60 * 60 * 1000;
|
|
284
|
+
let emitted;
|
|
285
|
+
beforeEach(() => {
|
|
286
|
+
emitted = [];
|
|
287
|
+
});
|
|
288
|
+
afterEach(() => {
|
|
289
|
+
vi.restoreAllMocks();
|
|
290
|
+
});
|
|
291
|
+
/** The emit decision exactly as reconcileAccount applies it. */
|
|
292
|
+
function tick(prevState, nowMs) {
|
|
293
|
+
const outcome = "unconfigured";
|
|
294
|
+
const signature = provisionSignature(outcome, "none-configured");
|
|
295
|
+
if (shouldEmitProvisionLine(prevState[HOUSE], signature, nowMs, HEARTBEAT)) {
|
|
296
|
+
emitted.push(outcome);
|
|
297
|
+
prevState[HOUSE] = { outcome, signature, loggedAtMs: nowMs };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
it("emits once on first observation and stays silent for the next 100 ticks", () => {
|
|
301
|
+
const state = {};
|
|
302
|
+
// 101 ticks at 120s is under 3.4h, inside the 6h heartbeat window.
|
|
303
|
+
for (let i = 0; i < 101; i++)
|
|
304
|
+
tick(state, i * 120_000);
|
|
305
|
+
// One line, not 101 — and crucially not zero.
|
|
306
|
+
expect(emitted).toEqual(["unconfigured"]);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
//# sourceMappingURL=connector-sync-provision.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connector-sync-provision.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/connector-sync-provision.test.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,kBAAkB;AAClB,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACvF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,EACvB,kBAAkB,EAClB,SAAS,EACT,kBAAkB,EAClB,mBAAmB,GAEpB,MAAM,gCAAgC,CAAC;AAExC,MAAM,KAAK,GAAG,sCAAsC,CAAC;AAErD,2EAA2E;AAC3E,SAAS,eAAe,CAAC,SAAiB,EAAE,MAA+B;IACzE,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACxC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACxE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,UAAkB,EAAE,GAAa,EAAE,GAAW;IACpE,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;IACrD,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,QAAQ,CAAC,CAAC;AACnD,CAAC;AAED,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACpC,cAAc,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACxE,MAAM,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACpC,cAAc,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,kBAAkB,CAAC,CAAC;QACzE,MAAM,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACpC,cAAc,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACxE,cAAc,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,kBAAkB,CAAC,CAAC;QACzE,MAAM,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACpC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrG,MAAM,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,6BAA6B;IAC7B,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACpC,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QACvE,SAAS,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,SAAS,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,EAAE,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC3E,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,8EAA8E;IAC9E,sEAAsE;IACtE,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE;YACjC,IAAI,EAAE,OAAO;YACb,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE;SAC7C,CAAC,CAAC;QACH,MAAM,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YACrD,WAAW,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE;YAClE,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,8EAA8E;IAC9E,qDAAqD;IACrD,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE;YACjC,IAAI,EAAE,OAAO;YACb,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE;SACtC,CAAC,CAAC;QACH,MAAM,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YACrD,WAAW,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE;YAC9D,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,yEAAyE;IACzE,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE;YACjC,IAAI,EAAE,OAAO;YACb,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE;YAC5C,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,SAAS,CAAC,EAAE;SACtC,CAAC,CAAC;QACH,MAAM,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,uEAAuE;IACvE,4EAA4E;IAC5E,qEAAqE;IACrE,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE;YACjC,IAAI,EAAE,OAAO;YACb,WAAW,EAAE,CAAC,eAAe,CAAC;SAC/B,CAAC,CAAC;QACH,MAAM,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YACrD,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,iBAAiB;SAC1B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACtD,MAAM,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YACrD,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,iBAAiB;SAC1B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,2EAA2E;IAC3E,4EAA4E;IAC5E,2EAA2E;IAC3E,6BAA6B;IAC7B,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE;YACjC,IAAI,EAAE,OAAO;YACb,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,aAAa,CAAC,EAAE;SAC3C,CAAC,CAAC;QACH,MAAM,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YACrD,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,gBAAgB;SACzB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,6EAA6E;IAC7E,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,GAAG,GAAG,sCAAsC,CAAC;QACnD,MAAM,KAAK,GAAG,sCAAsC,CAAC;QACrD,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE;YACjC,IAAI,EAAE,OAAO;YACb,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE;SAC3E,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC9B,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,aAAa,CACX,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,EAC5B,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,CAAC,CACjF,CAAC;QACF,MAAM,CAAC,0BAA0B,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;YACnD,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,eAAe;SACxB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,2EAA2E;IAC3E,sEAAsE;IACtE,mEAAmE;IACnE,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACtD,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,cAAc,CAAC,EAAE,WAAW,CAAC,CAAC;QAC7D,MAAM,CAAC,0BAA0B,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YACrD,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,mBAAmB;SAC5B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,MAAM,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5B,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC;IAC3B,MAAM,KAAK,GAAG,CAAC,OAAyB,EAAE,UAAkB,EAAE,MAAe,EAAE,EAAE,CAAC,CAAC;QACjF,OAAO;QACP,SAAS,EAAE,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC;QAC9C,UAAU;KACX,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,uBAAuB,CAAC,SAAS,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CACJ,uBAAuB,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,EAAE,aAAa,EAAE,KAAK,GAAG,IAAI,EAAE,SAAS,CAAC,CAC9F,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,8EAA8E;IAC9E,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CACJ,uBAAuB,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,EAAE,qBAAqB,EAAE,KAAK,GAAG,IAAI,EAAE,SAAS,CAAC,CAC7G,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CACJ,uBAAuB,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,EAAE,qBAAqB,EAAE,KAAK,GAAG,SAAS,EAAE,SAAS,CAAC,CAClH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,8EAA8E;IAC9E,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,CACJ,uBAAuB,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE,qBAAqB,EAAE,KAAK,EAAE,SAAS,CAAC,CAC1G,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,2EAA2E;IAC3E,2EAA2E;IAC3E,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAAC;QACxD,MAAM,CACJ,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,SAAS,CAAC,CACzG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,MAAM,CACJ,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,CAAC,OAAO,EAAE,sBAAsB,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,SAAS,CAAC,CAC5G,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,yEAAyE;IACzE,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAAC;QACxD,MAAM,CACJ,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,CAAC,qBAAqB,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,SAAS,CAAC,CAClG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,2EAA2E;IAC3E,2EAA2E;IAC3E,mEAAmE;IACnE,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC1H,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,IAAI,GAAW,CAAC;IAChB,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,EAAE,OAAO,EAAE,aAAiC,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAEvG,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,+BAA+B,CAAC,CAAC;QACxD,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,+BAA+B,CAAC,CAAC;QACxD,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACpE,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAClE,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,iFAAiF;IACjF,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAClC,aAAa,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAChC,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,yEAAyE;IACzE,yEAAyE;IACzE,iDAAiD;IACjD,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QACxC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACpB,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC;YACH,mBAAmB,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YACtE,MAAM,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAC,CAAC;YACjF,MAAM,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,gBAAgB,CAAC,4CAA4C,CAAC,CAAC,CAAC;QAC1G,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,kDAAkD;AAClD,QAAQ,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAClE,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACrC,IAAI,OAA2B,CAAC;IAEhC,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,eAAe,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,gEAAgE;IAChE,SAAS,IAAI,CACX,SAA+F,EAC/F,KAAa;QAEb,MAAM,OAAO,GAAqB,cAAc,CAAC;QACjD,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;QACjE,IAAI,uBAAuB,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;YAC3E,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACtB,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,KAAK,GAAyF,EAAE,CAAC;QACvG,mEAAmE;QACnE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE;YAAE,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;QACvD,8CAA8C;QAC9C,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,23 +1,223 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
// Task 1805 — the transport is HTTP, not a process per statement. These pin the
|
|
2
|
+
// requests it composes, the rows it parses, the one-lookup-per-run cache, and the
|
|
3
|
+
// two bounds (60 s request, 16 MB streamed response) inherited from the spawnSync
|
|
4
|
+
// call this replaced. Task 1350's npx-head guard is retired with the spawn it guarded.
|
|
5
|
+
import { describe, it, expect, vi } from "vitest";
|
|
6
|
+
import { buildD1LookupRequest, buildD1QueryRequest, createD1Client, D1_RESPONSE_CAP_BYTES, D1_TIMEOUT_MS, } from "../d1-command.js";
|
|
7
|
+
const ACC = "acc123";
|
|
8
|
+
const DB_ID = "11111111-2222-3333-4444-555555555555";
|
|
9
|
+
function bodyOf(text) {
|
|
10
|
+
return (async function* () {
|
|
11
|
+
yield new TextEncoder().encode(text);
|
|
12
|
+
})();
|
|
13
|
+
}
|
|
14
|
+
/** A fetch seam whose responses are scripted per call, recording what it received. */
|
|
15
|
+
function fakeFetch(responses) {
|
|
16
|
+
const calls = [];
|
|
17
|
+
let i = 0;
|
|
18
|
+
const fn = async (url, init) => {
|
|
19
|
+
calls.push({ url, init });
|
|
20
|
+
const r = responses[i++];
|
|
21
|
+
if (!r)
|
|
22
|
+
throw new Error(`unexpected extra fetch to ${url}`);
|
|
23
|
+
return { ok: r.ok ?? true, status: r.status ?? 200, chunks: bodyOf(r.text) };
|
|
24
|
+
};
|
|
25
|
+
return { fn, calls };
|
|
26
|
+
}
|
|
27
|
+
const lookupOk = (name) => JSON.stringify({ success: true, result: [{ uuid: DB_ID, name }] });
|
|
28
|
+
const queryOk = (rows) => JSON.stringify({ success: true, result: [{ results: rows, success: true }] });
|
|
29
|
+
describe("buildD1LookupRequest", () => {
|
|
30
|
+
it("addresses the account's D1 list endpoint filtered by the database name", () => {
|
|
31
|
+
const { url, init } = buildD1LookupRequest(ACC, "book db", "tok");
|
|
32
|
+
expect(url).toBe(`https://api.cloudflare.com/client/v4/accounts/${ACC}/d1/database?name=book+db`);
|
|
33
|
+
expect(init.method).toBe("GET");
|
|
34
|
+
});
|
|
35
|
+
it("carries a bearer Authorization header", () => {
|
|
36
|
+
const { init } = buildD1LookupRequest(ACC, "mydb", "tok");
|
|
37
|
+
expect(Object.keys(init.headers)).toContain("Authorization");
|
|
38
|
+
expect(init.headers.Authorization.startsWith("Bearer ")).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe("buildD1QueryRequest", () => {
|
|
42
|
+
it("posts the SQL to the database's query endpoint", () => {
|
|
43
|
+
const { url, init } = buildD1QueryRequest(ACC, DB_ID, "SELECT 1", "tok");
|
|
44
|
+
expect(url).toBe(`https://api.cloudflare.com/client/v4/accounts/${ACC}/d1/database/${DB_ID}/query`);
|
|
45
|
+
expect(init.method).toBe("POST");
|
|
46
|
+
expect(init.headers["Content-Type"]).toBe("application/json");
|
|
47
|
+
expect(JSON.parse(init.body)).toEqual({ sql: "SELECT 1", params: [] });
|
|
48
|
+
});
|
|
49
|
+
it("sends values as bound parameters rather than folding them into the SQL", () => {
|
|
50
|
+
const { init } = buildD1QueryRequest(ACC, DB_ID, "UPDATE bookings SET swept=1 WHERE bookingId=?1", "tok", ["b'1"]);
|
|
51
|
+
const sent = JSON.parse(init.body);
|
|
52
|
+
expect(sent.params).toEqual(["b'1"]);
|
|
53
|
+
expect(sent.sql).not.toContain("b'1");
|
|
54
|
+
});
|
|
55
|
+
it("carries a bearer Authorization header", () => {
|
|
56
|
+
const { init } = buildD1QueryRequest(ACC, DB_ID, "SELECT 1", "tok");
|
|
57
|
+
expect(Object.keys(init.headers)).toContain("Authorization");
|
|
58
|
+
expect(init.headers.Authorization.startsWith("Bearer ")).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it("starts no process — the module exports no argv builder", async () => {
|
|
61
|
+
const mod = await import("../d1-command.js");
|
|
62
|
+
expect("buildD1Command" in mod).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe("createD1Client", () => {
|
|
66
|
+
it("resolves the database by name then returns the query's rows", async () => {
|
|
67
|
+
const { fn, calls } = fakeFetch([
|
|
68
|
+
{ text: lookupOk("mydb") },
|
|
69
|
+
{ text: queryOk([{ bookingId: "b1" }]) },
|
|
20
70
|
]);
|
|
71
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
72
|
+
await expect(client.query("mydb", "SELECT 1")).resolves.toEqual([{ bookingId: "b1" }]);
|
|
73
|
+
expect(calls[0].url).toContain("/d1/database?name=mydb");
|
|
74
|
+
expect(calls[1].url).toContain(`/d1/database/${DB_ID}/query`);
|
|
75
|
+
});
|
|
76
|
+
it("returns an empty row set when the statement produced no results", async () => {
|
|
77
|
+
const { fn } = fakeFetch([{ text: lookupOk("mydb") }, { text: queryOk([]) }]);
|
|
78
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
79
|
+
await expect(client.query("mydb", "UPDATE t SET x=1")).resolves.toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
it("passes a caller's parameters through to the request body", async () => {
|
|
82
|
+
const { fn, calls } = fakeFetch([{ text: lookupOk("mydb") }, { text: queryOk([]) }]);
|
|
83
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
84
|
+
await client.query("mydb", "UPDATE t SET x=1 WHERE id=?1", ["abc"]);
|
|
85
|
+
expect(JSON.parse(calls[1].init.body).params).toEqual(["abc"]);
|
|
86
|
+
});
|
|
87
|
+
it("looks the database up once per run and reuses the id for later statements", async () => {
|
|
88
|
+
const { fn, calls } = fakeFetch([
|
|
89
|
+
{ text: lookupOk("mydb") },
|
|
90
|
+
{ text: queryOk([]) },
|
|
91
|
+
{ text: queryOk([]) },
|
|
92
|
+
]);
|
|
93
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
94
|
+
await client.query("mydb", "SELECT 1");
|
|
95
|
+
await client.query("mydb", "SELECT 2");
|
|
96
|
+
expect(calls.filter((c) => c.url.includes("?name=")).length).toBe(1);
|
|
97
|
+
expect(calls.length).toBe(3);
|
|
98
|
+
});
|
|
99
|
+
it("counts the statements it issued and the time it spent", async () => {
|
|
100
|
+
const { fn } = fakeFetch([
|
|
101
|
+
{ text: lookupOk("mydb") },
|
|
102
|
+
{ text: queryOk([]) },
|
|
103
|
+
{ text: queryOk([]) },
|
|
104
|
+
]);
|
|
105
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
106
|
+
await client.query("mydb", "SELECT 1");
|
|
107
|
+
await client.query("mydb", "SELECT 2");
|
|
108
|
+
expect(client.statements).toBe(2);
|
|
109
|
+
expect(client.elapsedMs).toBeGreaterThanOrEqual(0);
|
|
110
|
+
});
|
|
111
|
+
it("refuses a name the account does not carry rather than querying a guessed id", async () => {
|
|
112
|
+
const { fn } = fakeFetch([
|
|
113
|
+
{ text: JSON.stringify({ success: true, result: [{ uuid: DB_ID, name: "otherdb" }] }) },
|
|
114
|
+
]);
|
|
115
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
116
|
+
await expect(client.query("mydb", "SELECT 1")).rejects.toThrow(/mydb/);
|
|
117
|
+
});
|
|
118
|
+
it("surfaces a non-2xx with its status and body", async () => {
|
|
119
|
+
const { fn } = fakeFetch([{ ok: false, status: 403, text: '{"errors":[{"code":10000}]}' }]);
|
|
120
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
121
|
+
await expect(client.query("mydb", "SELECT 1")).rejects.toThrow(/403[\s\S]*10000/);
|
|
122
|
+
});
|
|
123
|
+
it("surfaces a 2xx that is not a success envelope with its body", async () => {
|
|
124
|
+
const { fn } = fakeFetch([
|
|
125
|
+
{ text: lookupOk("mydb") },
|
|
126
|
+
{ text: JSON.stringify({ success: false, errors: [{ message: "no such table" }] }) },
|
|
127
|
+
]);
|
|
128
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
129
|
+
await expect(client.query("mydb", "SELECT 1")).rejects.toThrow(/no such table/);
|
|
130
|
+
});
|
|
131
|
+
it("surfaces a statement that reports its own failure inside a success envelope", async () => {
|
|
132
|
+
// The REST API's per-statement `success` semantics are undocumented, so this
|
|
133
|
+
// refuses to let an unmeasured fact decide correctness. If a failed sweep can
|
|
134
|
+
// arrive this way, treating it as success would leave the row unswept while
|
|
135
|
+
// the caller believed it had swept — a silent no-op on the write path.
|
|
136
|
+
const { fn } = fakeFetch([
|
|
137
|
+
{ text: lookupOk("mydb") },
|
|
138
|
+
{
|
|
139
|
+
text: JSON.stringify({
|
|
140
|
+
success: true,
|
|
141
|
+
result: [{ success: false, results: [], error: "no such table: bookings" }],
|
|
142
|
+
}),
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
146
|
+
await expect(client.query("mydb", "UPDATE bookings SET swept=1")).rejects.toThrow(/no such table/);
|
|
147
|
+
});
|
|
148
|
+
it("accepts a statement that omits the success flag rather than requiring it", async () => {
|
|
149
|
+
// Absent is not false. Requiring an undocumented field to be present would
|
|
150
|
+
// reject healthy responses that simply do not carry it.
|
|
151
|
+
const { fn } = fakeFetch([
|
|
152
|
+
{ text: lookupOk("mydb") },
|
|
153
|
+
{ text: JSON.stringify({ success: true, result: [{ results: [{ n: 1 }] }] }) },
|
|
154
|
+
]);
|
|
155
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
156
|
+
await expect(client.query("mydb", "SELECT 1")).resolves.toEqual([{ n: 1 }]);
|
|
157
|
+
});
|
|
158
|
+
it("counts a statement that was issued and then failed, not only ones that succeeded", async () => {
|
|
159
|
+
// `statements` is what reached D1. Counting successes only would report 0 for
|
|
160
|
+
// both "the name lookup failed so nothing was sent" and "one statement was
|
|
161
|
+
// sent and errored", which the op=d1 line needs to tell apart.
|
|
162
|
+
const { fn } = fakeFetch([
|
|
163
|
+
{ text: lookupOk("mydb") },
|
|
164
|
+
{ ok: false, status: 500, text: "boom" },
|
|
165
|
+
]);
|
|
166
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
167
|
+
await expect(client.query("mydb", "SELECT 1")).rejects.toThrow(/500/);
|
|
168
|
+
expect(client.statements).toBe(1);
|
|
169
|
+
});
|
|
170
|
+
it("counts no statement when the name lookup itself failed", async () => {
|
|
171
|
+
const { fn } = fakeFetch([{ ok: false, status: 403, text: "denied" }]);
|
|
172
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
173
|
+
await expect(client.query("mydb", "SELECT 1")).rejects.toThrow(/403/);
|
|
174
|
+
expect(client.statements).toBe(0);
|
|
175
|
+
});
|
|
176
|
+
it("surfaces a 2xx that is not JSON with its body", async () => {
|
|
177
|
+
const { fn } = fakeFetch([{ text: "<html>gateway</html>" }]);
|
|
178
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
179
|
+
await expect(client.query("mydb", "SELECT 1")).rejects.toThrow(/gateway/);
|
|
180
|
+
});
|
|
181
|
+
it("aborts a response past the cap while streaming, not after buffering it", async () => {
|
|
182
|
+
// Three chunks of 8 MB. The cap is 16 MB, so the third crosses it. Counting
|
|
183
|
+
// the chunks consumed proves the read stopped rather than materialising the
|
|
184
|
+
// whole body and checking its length afterwards.
|
|
185
|
+
let consumed = 0;
|
|
186
|
+
const chunk = new Uint8Array(8 * 1024 * 1024);
|
|
187
|
+
const fn = async () => ({
|
|
188
|
+
ok: true,
|
|
189
|
+
status: 200,
|
|
190
|
+
chunks: (async function* () {
|
|
191
|
+
for (let i = 0; i < 5; i++) {
|
|
192
|
+
consumed++;
|
|
193
|
+
yield chunk;
|
|
194
|
+
}
|
|
195
|
+
})(),
|
|
196
|
+
});
|
|
197
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
198
|
+
await expect(client.query("mydb", "SELECT 1")).rejects.toThrow(/cap/);
|
|
199
|
+
expect(consumed).toBe(3);
|
|
200
|
+
expect(D1_RESPONSE_CAP_BYTES).toBe(16 * 1024 * 1024);
|
|
201
|
+
});
|
|
202
|
+
it("rejects at the 60 s bound rather than hanging when the endpoint stalls", async () => {
|
|
203
|
+
vi.useFakeTimers();
|
|
204
|
+
try {
|
|
205
|
+
// Honours the signal the client attached; resolves never otherwise. This is
|
|
206
|
+
// the property the PID lock depends on: a stalled Cloudflare must not wedge
|
|
207
|
+
// the run and hold the lock.
|
|
208
|
+
const fn = (_url, init) => new Promise((_resolve, reject) => {
|
|
209
|
+
init.signal.addEventListener("abort", () => reject(init.signal.reason));
|
|
210
|
+
});
|
|
211
|
+
const client = createD1Client({ accountId: ACC, token: "tok", fetchFn: fn });
|
|
212
|
+
const pending = client.query("mydb", "SELECT 1");
|
|
213
|
+
const assertion = expect(pending).rejects.toThrow(/timed out after 60000ms/);
|
|
214
|
+
await vi.advanceTimersByTimeAsync(D1_TIMEOUT_MS + 1);
|
|
215
|
+
await assertion;
|
|
216
|
+
expect(D1_TIMEOUT_MS).toBe(60_000);
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
vi.useRealTimers();
|
|
220
|
+
}
|
|
21
221
|
});
|
|
22
222
|
});
|
|
23
223
|
//# sourceMappingURL=d1-command.test.js.map
|