Standalone per-card browser model/security/view modules for HUX-01..10 plus node+pytest suites that read the hux.v1 contract schemas directly. Reconciled drift found on integration: the activity model now accepts all 32 hux.event.v1 kinds (delegation.*, memory.suppressed, memory.retrieval_removed, budget.exhausted, side_effect.*), the autonomy model carries the external_side_effect capability, and the foundation boundary test now asserts the shipped static HUX surface exists on disk and that images never bake activated HUX_FLAGS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvMSXH8VH2tMWXanb8SJdf
414 lines
19 KiB
JavaScript
414 lines
19 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
createOnboardingClient,
|
|
OnboardingContractError,
|
|
} from "../../dockerfiles/hermes-webui-hux/onboarding/client.ts";
|
|
import {
|
|
FOUNDATION_FLAG,
|
|
FRIENDLY_MODES_FLAG,
|
|
ONBOARDING_FLAG,
|
|
PRIVACY_FLAG,
|
|
PROJECTS_FLAG,
|
|
buildActionIntent,
|
|
normalizeDecisionState,
|
|
normalizeEvaluation,
|
|
normalizeState,
|
|
normalizeSuggestion,
|
|
onboardingEnabled,
|
|
} from "../../dockerfiles/hermes-webui-hux/onboarding/model.ts";
|
|
import {
|
|
asRecord,
|
|
exactKeys,
|
|
isId,
|
|
isSafeText,
|
|
isUtc,
|
|
normalizeIdentity,
|
|
normalizeScope,
|
|
normalizeTrigger,
|
|
sameIdentity,
|
|
sameTrigger,
|
|
scopedSuggestionPath,
|
|
} from "../../dockerfiles/hermes-webui-hux/onboarding/security.ts";
|
|
|
|
const IDENTITY = Object.freeze({
|
|
tenantRef: "tnt_0123456789abcdef",
|
|
userRef: "usr_0123456789abcdef",
|
|
surface: "chat",
|
|
});
|
|
const RAW_IDENTITY = Object.freeze({
|
|
tenant_ref: IDENTITY.tenantRef,
|
|
user_ref: IDENTITY.userRef,
|
|
surface: IDENTITY.surface,
|
|
});
|
|
const SCOPE = Object.freeze({
|
|
sessionId: "ses_alpha1234",
|
|
conversationId: "conv_alpha1234",
|
|
trigger: {surface: "chat", context: "after_approval"},
|
|
});
|
|
const FLAGS = [
|
|
FOUNDATION_FLAG, PROJECTS_FLAG, FRIENDLY_MODES_FLAG, ONBOARDING_FLAG,
|
|
PRIVACY_FLAG,
|
|
];
|
|
|
|
function suggestion(extra = {}) {
|
|
return {
|
|
schema: "hux.suggestion.v1",
|
|
id: "sug_alpha1234",
|
|
kind: "workflow",
|
|
trigger: SCOPE.trigger,
|
|
title: "Run this every week?",
|
|
body: "Hermes can prepare the same report each week after you confirm it.",
|
|
action: {type: "start_workflow", payload: {intent: "schedule_weekly"}},
|
|
priority: 35,
|
|
suppression: {
|
|
dismissable: true,
|
|
max_shows: 2,
|
|
cooldown_seconds: 86400,
|
|
never_again_supported: true,
|
|
},
|
|
...extra,
|
|
};
|
|
}
|
|
|
|
function state(extra = {}) {
|
|
return {
|
|
schema: "hux.suggestion_state.v1",
|
|
owner: IDENTITY.userRef,
|
|
suggestion_id: "sug_alpha1234",
|
|
shows: 1,
|
|
last_shown_at: "2026-08-24T10:00:00Z",
|
|
never_again: false,
|
|
...extra,
|
|
};
|
|
}
|
|
|
|
function evaluation(extra = {}) {
|
|
return {
|
|
schema: "hux.suggestion_evaluation.v1",
|
|
api_version: "hux.v1",
|
|
identity: RAW_IDENTITY,
|
|
session_id: SCOPE.sessionId,
|
|
conversation_id: SCOPE.conversationId,
|
|
trigger: SCOPE.trigger,
|
|
privacy: {
|
|
schema: "hux.onboarding_privacy.v1",
|
|
no_store: false,
|
|
memory_suggestions_allowed: true,
|
|
},
|
|
claim_id: "clm_alpha1234",
|
|
issued_at: "2026-08-24T10:00:01Z",
|
|
suggestion: suggestion(),
|
|
state: state(),
|
|
...extra,
|
|
};
|
|
}
|
|
|
|
function decisionEnvelope(item, decision = "dismiss", extra = {}) {
|
|
const timestamp = "2026-08-24T10:01:00Z";
|
|
return {
|
|
schema: "hux.suggestion_decision.v1",
|
|
api_version: "hux.v1",
|
|
identity: RAW_IDENTITY,
|
|
session_id: SCOPE.sessionId,
|
|
conversation_id: SCOPE.conversationId,
|
|
claim_id: "clm_alpha1234",
|
|
suggestion_id: "sug_alpha1234",
|
|
state: {
|
|
...state(),
|
|
...(decision === "acted" ? {acted_at: timestamp} : {dismissed_at: timestamp}),
|
|
never_again: decision === "never_again",
|
|
},
|
|
...extra,
|
|
...(item ? {state: item} : {}),
|
|
};
|
|
}
|
|
|
|
function harness(responses, flags = FLAGS) {
|
|
const calls = [];
|
|
const enabled = new Set(flags);
|
|
const foundation = {
|
|
apiVersion: "hux.v1",
|
|
identity: IDENTITY,
|
|
enabled: (flag) => enabled.has(flag),
|
|
endpoint: (path) => `/hux/v1${path}`,
|
|
};
|
|
const fetcher = async (url, init) => {
|
|
calls.push({url, init});
|
|
const response = responses.shift();
|
|
if (!response) throw new Error("unexpected request");
|
|
return {
|
|
ok: response.ok ?? true,
|
|
status: response.status ?? 200,
|
|
json: async () => response.body,
|
|
};
|
|
};
|
|
return {calls, foundation,
|
|
client: createOnboardingClient({client: foundation, fetcher})};
|
|
}
|
|
|
|
test("HUX-09 is default-off and requires all four dependencies", () => {
|
|
assert.equal(onboardingEnabled(), false);
|
|
assert.equal(onboardingEnabled([]), false);
|
|
for (const flag of FLAGS.slice(0, 4)) {
|
|
assert.equal(onboardingEnabled(FLAGS.slice(0, 4).filter((item) => item !== flag)), false);
|
|
}
|
|
assert.equal(onboardingEnabled(FLAGS.slice(0, 4)), true);
|
|
});
|
|
|
|
test("security primitives bind exact identity, session and trigger", () => {
|
|
assert.deepEqual(asRecord({a: 1}), {a: 1});
|
|
assert.equal(asRecord(null), null);
|
|
assert.equal(asRecord([]), null);
|
|
assert.equal(exactKeys({a: 1, b: 2}, ["a"], ["b"]), true);
|
|
assert.equal(exactKeys({a: 1, x: 2}, ["a"]), false);
|
|
assert.equal(isId("sug_alpha1234", "sug"), true);
|
|
assert.equal(isId("bad", "sug"), false);
|
|
assert.equal(isId("clm_alpha1234", "sug"), false);
|
|
assert.equal(isUtc("2026-08-24T10:00:00Z"), true);
|
|
assert.equal(isUtc("2026-99-99T10:00:00Z"), false);
|
|
assert.equal(isSafeText("hello", 1, 10), true);
|
|
assert.equal(isSafeText("bad\u0000", 1, 10), false);
|
|
assert.deepEqual(normalizeIdentity(RAW_IDENTITY), IDENTITY);
|
|
for (const raw of [null, [], {}, {...RAW_IDENTITY, extra: true},
|
|
{...RAW_IDENTITY, tenant_ref: "bad"}, {...RAW_IDENTITY, user_ref: "bad"},
|
|
{...RAW_IDENTITY, surface: "browser"}]) assert.equal(normalizeIdentity(raw), null);
|
|
assert.equal(sameIdentity(IDENTITY, IDENTITY), true);
|
|
assert.equal(sameIdentity(IDENTITY, {...IDENTITY, surface: "worker"}), false);
|
|
assert.deepEqual(normalizeTrigger(SCOPE.trigger), SCOPE.trigger);
|
|
for (const raw of [null, {}, {...SCOPE.trigger, extra: true},
|
|
{...SCOPE.trigger, surface: "browser"}, {...SCOPE.trigger, context: "often"}])
|
|
assert.equal(normalizeTrigger(raw), null);
|
|
assert.equal(sameTrigger(SCOPE.trigger, SCOPE.trigger), true);
|
|
assert.equal(sameTrigger(SCOPE.trigger, {...SCOPE.trigger, context: "idle"}), false);
|
|
assert.deepEqual(normalizeScope(SCOPE, IDENTITY), SCOPE);
|
|
for (const raw of [{...SCOPE, extra: true}, {...SCOPE, sessionId: "bad"},
|
|
{...SCOPE, conversationId: "bad"},
|
|
{...SCOPE, trigger: {...SCOPE.trigger, surface: "worker"}}])
|
|
assert.equal(normalizeScope(raw, IDENTITY), null);
|
|
assert.equal(scopedSuggestionPath("/onboarding/suggestions/", "sug_alpha1234"),
|
|
"/onboarding/suggestions/sug_alpha1234/decisions");
|
|
for (const [base, id] of [["//evil", "sug_alpha1234"], ["/x?y", "sug_alpha1234"],
|
|
["/../x", "sug_alpha1234"], ["/safe", "bad"]])
|
|
assert.throws(() => scopedSuggestionPath(base, id), TypeError);
|
|
});
|
|
|
|
test("suggestion schema normalizes all four contextual actions", () => {
|
|
const workflow = normalizeSuggestion(suggestion(), SCOPE.trigger);
|
|
assert.equal(workflow.action.payload.intent, "schedule_weekly");
|
|
assert.equal(workflow.suppression.cooldownSeconds, 86400);
|
|
const save = normalizeSuggestion(suggestion({
|
|
action: {type: "start_workflow", payload: {intent: "save_reusable"}},
|
|
}), SCOPE.trigger);
|
|
assert.equal(save.action.payload.intent, "save_reusable");
|
|
const project = normalizeSuggestion(suggestion({kind: "project",
|
|
action: {type: "create_project", payload: {prefill_title: "Launch plan"}}}), SCOPE.trigger);
|
|
assert.equal(project.action.payload.prefill_title, "Launch plan");
|
|
const memory = normalizeSuggestion(suggestion({kind: "feature",
|
|
action: {type: "open_memory", payload: {view: "suggestions"}}}), SCOPE.trigger);
|
|
assert.equal(memory.action.type, "open_memory");
|
|
const mode = normalizeSuggestion(suggestion({kind: "feature",
|
|
action: {type: "open_mode", payload: {mode: "research"}}}), SCOPE.trigger);
|
|
assert.equal(mode.action.payload.mode, "research");
|
|
const artifacts = normalizeSuggestion(suggestion({kind: "feature",
|
|
action: {type: "open_artifacts", payload: {view: "current"}}}), SCOPE.trigger);
|
|
assert.equal(artifacts.action.type, "open_artifacts");
|
|
const tip = normalizeSuggestion(suggestion({kind: "tip", action: undefined}), SCOPE.trigger);
|
|
assert.equal(tip.action.type, "none");
|
|
assert.deepEqual(normalizeSuggestion(suggestion({kind: "project",
|
|
action: {type: "create_project"}}), SCOPE.trigger).action.payload, {});
|
|
assert.deepEqual(normalizeSuggestion(suggestion({kind: "feature",
|
|
action: {type: "open_mode"}}), SCOPE.trigger).action.payload, {});
|
|
assert.deepEqual(normalizeSuggestion(suggestion({kind: "feature",
|
|
action: {type: "open_memory"}}), SCOPE.trigger).action.payload, {});
|
|
assert.deepEqual(normalizeSuggestion(suggestion({kind: "feature",
|
|
action: {type: "open_artifacts"}}), SCOPE.trigger).action.payload, {});
|
|
});
|
|
|
|
test("malformed, mismatched, or pushy suggestions fail closed", () => {
|
|
const invalid = [
|
|
null, {...suggestion(), extra: true}, {...suggestion(), schema: "hux.suggestion.v0"},
|
|
{...suggestion(), id: "bad"}, {...suggestion(), kind: "ad"},
|
|
{...suggestion(), title: ""}, {...suggestion(), title: "x".repeat(81)},
|
|
{...suggestion(), body: "bad\u0000"}, {...suggestion(), priority: 101},
|
|
{...suggestion(), trigger: {...SCOPE.trigger, context: "idle"}},
|
|
{...suggestion(), suppression: null},
|
|
{...suggestion(), suppression: {...suggestion().suppression, dismissable: false}},
|
|
{...suggestion(), suppression: {...suggestion().suppression, max_shows: 6}},
|
|
{...suggestion(), suppression: {...suggestion().suppression, cooldown_seconds: 30}},
|
|
{...suggestion(), suppression: {...suggestion().suppression, never_again_supported: false}},
|
|
{...suggestion(), action: null},
|
|
{...suggestion(), action: {type: "unknown"}},
|
|
{...suggestion(), action: {type: "start_workflow", payload: {intent: "daily"}}},
|
|
{...suggestion(), action: {type: "start_workflow", payload: {intent: "schedule_weekly", extra: 1}}},
|
|
{...suggestion(), action: {type: "create_project", payload: {prefill_title: ""}}},
|
|
{...suggestion(), action: {type: "create_project", payload: {prefill_title: "x".repeat(121)}}},
|
|
{...suggestion(), action: {type: "create_project", payload: {extra: true}}},
|
|
{...suggestion(), action: {type: "open_mode", payload: {mode: "turbo"}}},
|
|
{...suggestion(), action: {type: "open_mode", payload: {extra: true}}},
|
|
{...suggestion(), action: {type: "open_memory", payload: {view: "all"}}},
|
|
{...suggestion(), action: {type: "open_memory", payload: {extra: true}}},
|
|
{...suggestion(), action: {type: "open_artifacts", payload: {view: "all"}}},
|
|
{...suggestion(), action: {type: "open_artifacts", payload: {extra: true}}},
|
|
{...suggestion(), action: {type: "none", payload: {surprise: true}}},
|
|
{...suggestion(), kind: "project"},
|
|
];
|
|
invalid.forEach((raw) => assert.equal(normalizeSuggestion(raw, SCOPE.trigger), null));
|
|
});
|
|
|
|
test("eligible state is owner-bound and inside the server rate limit", () => {
|
|
const item = normalizeSuggestion(suggestion(), SCOPE.trigger);
|
|
assert.equal(normalizeState(state(), item, IDENTITY.userRef).shows, 1);
|
|
const complete = normalizeState(state({dismissed_at: "2026-08-24T10:01:00Z",
|
|
acted_at: "2026-08-24T10:02:00Z"}), item, IDENTITY.userRef);
|
|
assert.equal(complete.dismissedAt, "2026-08-24T10:01:00Z");
|
|
assert.equal(complete.actedAt, "2026-08-24T10:02:00Z");
|
|
const invalid = [null, {...state(), extra: true}, {...state(), schema: "old"},
|
|
{...state(), owner: "usr_aaaaaaaaaaaaaaaa"}, {...state(), suggestion_id: "sug_other1234"},
|
|
{...state(), shows: 0}, {...state(), shows: 3}, {...state(), last_shown_at: "today"},
|
|
{...state(), never_again: true}, {...state(), dismissed_at: "today"},
|
|
{...state(), acted_at: "today"}];
|
|
invalid.forEach((raw) => assert.equal(normalizeState(raw, item, IDENTITY.userRef), null));
|
|
});
|
|
|
|
test("evaluation binds identity, session, trigger, privacy and one claim", () => {
|
|
const item = normalizeEvaluation(evaluation(), IDENTITY, SCOPE, true);
|
|
assert.equal(item.claimId, "clm_alpha1234");
|
|
assert.equal(item.state.owner, IDENTITY.userRef);
|
|
assert.equal(normalizeEvaluation(evaluation({privacy: {
|
|
schema: "hux.onboarding_privacy.v1", no_store: true,
|
|
memory_suggestions_allowed: false}, claim_id: null, suggestion: null, state: null}),
|
|
IDENTITY, SCOPE, true), null);
|
|
assert.equal(normalizeEvaluation(evaluation({suggestion: null, claim_id: null, state: null}),
|
|
IDENTITY, SCOPE, true), null);
|
|
const memoryRaw = evaluation({suggestion: suggestion({kind: "feature",
|
|
action: {type: "open_memory", payload: {view: "suggestions"}}})});
|
|
assert.equal(normalizeEvaluation(memoryRaw, IDENTITY, SCOPE, false), null);
|
|
assert.equal(normalizeEvaluation({...memoryRaw, privacy: {...memoryRaw.privacy,
|
|
memory_suggestions_allowed: false}}, IDENTITY, SCOPE, true), null);
|
|
assert.ok(normalizeEvaluation(memoryRaw, IDENTITY, SCOPE, true));
|
|
const invalid = [null, {...evaluation(), extra: true}, {...evaluation(), schema: "old"},
|
|
{...evaluation(), api_version: "hux.v0"},
|
|
{...evaluation(), identity: {...RAW_IDENTITY, surface: "worker"}},
|
|
{...evaluation(), session_id: "ses_other1234"},
|
|
{...evaluation(), conversation_id: "conv_other1234"},
|
|
{...evaluation(), trigger: {...SCOPE.trigger, context: "idle"}},
|
|
{...evaluation(), privacy: null},
|
|
{...evaluation(), privacy: {...evaluation().privacy, extra: true}},
|
|
{...evaluation(), privacy: {...evaluation().privacy, no_store: "no"}},
|
|
{...evaluation(), claim_id: "bad"}, {...evaluation(), issued_at: "today"},
|
|
{...evaluation(), state: {...state(), owner: "usr_aaaaaaaaaaaaaaaa"}}];
|
|
invalid.forEach((raw) => assert.equal(normalizeEvaluation(raw, IDENTITY, SCOPE, true), null));
|
|
});
|
|
|
|
test("actions bridge only validated HUX-03/HUX-06 interfaces", () => {
|
|
const make = (change) => normalizeEvaluation(evaluation({
|
|
suggestion: suggestion(change),
|
|
}), IDENTITY, SCOPE, true);
|
|
assert.deepEqual(buildActionIntent(make({})),
|
|
{type: "start_workflow", intent: "schedule_weekly"});
|
|
assert.deepEqual(buildActionIntent(make({kind: "project", action: {
|
|
type: "create_project", payload: {prefill_title: "Launch"}}})),
|
|
{type: "create_project", conversationId: SCOPE.conversationId, prefillTitle: "Launch"});
|
|
assert.deepEqual(buildActionIntent(make({kind: "feature", action: {
|
|
type: "open_mode", payload: {mode: "fast"}}})), {type: "open_mode", mode: "fast"});
|
|
assert.deepEqual(buildActionIntent(make({kind: "feature", action: {
|
|
type: "open_mode"}})), {type: "open_mode"});
|
|
assert.deepEqual(buildActionIntent(make({kind: "project", action: {
|
|
type: "create_project"}})),
|
|
{type: "create_project", conversationId: SCOPE.conversationId});
|
|
assert.deepEqual(buildActionIntent(make({kind: "feature", action: {
|
|
type: "open_memory"}})), {type: "open_memory", view: "suggestions"});
|
|
assert.deepEqual(buildActionIntent(make({kind: "feature", action: {
|
|
type: "open_artifacts"}})), {type: "open_artifacts", view: "current"});
|
|
assert.equal(buildActionIntent(make({kind: "tip", action: {type: "none"}})), null);
|
|
});
|
|
|
|
test("decision states preserve shows and prove the chosen outcome", () => {
|
|
const item = normalizeEvaluation(evaluation(), IDENTITY, SCOPE, true);
|
|
for (const decision of ["dismiss", "never_again", "acted"]) {
|
|
const raw = decisionEnvelope(null, decision).state;
|
|
const normalized = normalizeDecisionState(raw, item, decision);
|
|
assert.ok(normalized);
|
|
assert.equal(normalized.neverAgain, decision === "never_again");
|
|
}
|
|
for (const raw of [null, {...state(), extra: true}, {...state(), shows: 2},
|
|
{...state(), last_shown_at: "2026-08-24T11:00:00Z"},
|
|
{...state(), never_again: "yes"}, {...state(), dismissed_at: "today"},
|
|
{...state(), acted_at: "today"}, {...state(), acted_at: "2026-08-24T10:01:00Z"}])
|
|
assert.equal(normalizeDecisionState(raw, item, "dismiss"), null);
|
|
assert.ok(normalizeDecisionState({...state(), dismissed_at: "2026-08-24T10:01:00Z",
|
|
acted_at: "2026-08-24T10:02:00Z"}, item, "dismiss"));
|
|
});
|
|
|
|
test("client evaluates exact triggers with no-store same-origin requests", async () => {
|
|
const current = harness([{body: evaluation()}]);
|
|
assert.equal(current.client.enabled(), true);
|
|
const item = await current.client.evaluate(SCOPE);
|
|
assert.equal(item.suggestion.title, "Run this every week?");
|
|
assert.equal(current.calls[0].url, "/hux/v1/onboarding/evaluate");
|
|
assert.equal(current.calls[0].init.method, "POST");
|
|
assert.equal(current.calls[0].init.cache, "no-store");
|
|
assert.equal(current.calls[0].init.credentials, "same-origin");
|
|
assert.deepEqual(JSON.parse(current.calls[0].init.body), {
|
|
schema: "hux.suggestion_evaluation_request.v1", session_id: SCOPE.sessionId,
|
|
conversation_id: SCOPE.conversationId, trigger: SCOPE.trigger,
|
|
});
|
|
const replay = harness([{body: evaluation()}, {body: evaluation()}]);
|
|
assert.ok(await replay.client.evaluate(SCOPE));
|
|
assert.equal(await replay.client.evaluate(SCOPE), null);
|
|
const empty = harness([{status: 204}, {status: 404}]);
|
|
assert.equal(await empty.client.evaluate(SCOPE), null);
|
|
assert.equal(await empty.client.evaluate(SCOPE), null);
|
|
});
|
|
|
|
test("client persists explicit decisions without performing suggested work", async () => {
|
|
for (const decision of ["dismiss", "never_again", "acted"]) {
|
|
const current = harness([{body: evaluation()}, {body: decisionEnvelope(null, decision)}]);
|
|
const item = await current.client.evaluate(SCOPE);
|
|
const after = await current.client.decide(item, decision);
|
|
assert.equal(after.neverAgain, decision === "never_again");
|
|
assert.equal(current.calls[1].url,
|
|
"/hux/v1/onboarding/suggestions/sug_alpha1234/decisions");
|
|
assert.deepEqual(JSON.parse(current.calls[1].init.body), {
|
|
schema: "hux.suggestion_decision_request.v1", session_id: SCOPE.sessionId,
|
|
conversation_id: SCOPE.conversationId, claim_id: item.claimId,
|
|
suggestion_id: item.suggestion.id, expected_shows: 1, decision,
|
|
});
|
|
await assert.rejects(current.client.decide(item, decision), /not bound/);
|
|
}
|
|
});
|
|
|
|
test("client rejects disabled, malformed, cross-scope, stale, and unissued data", async () => {
|
|
const disabled = harness([], [FOUNDATION_FLAG, PROJECTS_FLAG]);
|
|
assert.equal(disabled.client.enabled(), false);
|
|
await assert.rejects(disabled.client.evaluate(SCOPE), /not enabled/);
|
|
disabled.foundation.apiVersion = "hux.v0";
|
|
await assert.rejects(disabled.client.evaluate(SCOPE), /not enabled/);
|
|
const invalidScope = harness([]);
|
|
await assert.rejects(invalidScope.client.evaluate({...SCOPE, sessionId: "bad"}), /outside/);
|
|
await assert.rejects(invalidScope.client.decide({claimId: "clm_fake1234"}, "dismiss"), /not bound/);
|
|
await assert.rejects(invalidScope.client.decide({}, "delete"), /not bound/);
|
|
for (const response of [{ok: false, status: 409}, {ok: false, status: 503}]) {
|
|
const current = harness([response]);
|
|
await assert.rejects(current.client.evaluate(SCOPE), OnboardingContractError);
|
|
}
|
|
const malformed = harness([{body: {}}]);
|
|
assert.equal(await malformed.client.evaluate(SCOPE), null);
|
|
const wrongDecision = harness([{body: evaluation()}, {body: decisionEnvelope(null, "dismiss", {
|
|
identity: {...RAW_IDENTITY, surface: "worker"}})}]);
|
|
const item = await wrongDecision.client.evaluate(SCOPE);
|
|
await assert.rejects(wrongDecision.client.decide(item, "dismiss"), /crossed/);
|
|
const missing = harness([{body: evaluation()}, {status: 204}]);
|
|
await assert.rejects(missing.client.decide(await missing.client.evaluate(SCOPE), "dismiss"), /crossed/);
|
|
assert.throws(() => createOnboardingClient({client: null, fetcher: async () => ({})}), /requires/);
|
|
assert.throws(() => createOnboardingClient({client: {...invalidScope.foundation,
|
|
identity: {...IDENTITY, tenantRef: "bad"}}, fetcher: async () => ({})}), /identity/);
|
|
const originalFetch = globalThis.fetch;
|
|
try {
|
|
globalThis.fetch = undefined;
|
|
assert.throws(() => createOnboardingClient({client: invalidScope.foundation}), /requires/);
|
|
} finally { globalThis.fetch = originalFetch; }
|
|
});
|