Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

517 lines
20 KiB
TypeScript

/**
* Shape tests for Anthropic prompt-cache injection in the OpenRouter text handler.
* Covers the runtime-fallback cacheControl (Fix 2), internal-field stripping from wire
* options (Fix 3a), segmented-user-content breakpoints with validated shapes and capping
* (Fix 3b/3c), the cacheSystem:false opt-out, verbatim survival of caller-supplied
* providerOptions (openrouter + arbitrary keys) alongside injected cacheControl and
* multi-breakpoint stamping (#15825), and the explicit boundary failure when a
* caller-supplied cacheControl is malformed (#15825, no silent drop). AI SDK and provider
* are mocked — no network calls, deterministic string fixtures.
*/
import type { IAgentRuntime } from "@elizaos/core";
import { afterEach, describe, expect, it, vi } from "vitest";
function createRuntime(settings: Record<string, string> = {}) {
return {
character: { system: "system prompt" },
emitEvent: vi.fn(async () => undefined),
getSetting: vi.fn((key: string) => {
return (
(
{
OPENROUTER_API_KEY: "test-key",
OPENROUTER_LARGE_MODEL: "anthropic/claude-opus-4-8",
...settings,
} as Record<string, string>
)[key] ?? null
);
}),
} as IAgentRuntime;
}
function mockModules() {
const generateText = vi.fn(async () => ({
text: "ok",
finishReason: "stop",
usage: { inputTokens: 5, outputTokens: 2, totalTokens: 7 },
}));
vi.doMock("ai", () => ({ generateText, streamText: vi.fn() }));
vi.doMock("../providers", () => ({
createOpenRouterProvider: () => ({
chat: (m: string) => ({ modelName: m }),
}),
}));
return { generateText };
}
afterEach(() => {
vi.doUnmock("ai");
vi.doUnmock("../providers");
vi.clearAllMocks();
vi.resetModules();
});
describe("Anthropic cache injection — runtime cacheControl fallback", () => {
it("injects ephemeral cacheControl on system message for Anthropic models even without explicit providerOptions", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), { prompt: "hello" } as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const messages = call.messages as Array<Record<string, unknown>>;
const systemMsg = messages?.[0];
expect(systemMsg?.role).toBe("system");
const provOpts = systemMsg?.providerOptions as Record<string, unknown> | undefined;
const anthropicOpts = provOpts?.anthropic as Record<string, unknown> | undefined;
expect(anthropicOpts?.cacheControl).toEqual(expect.objectContaining({ type: "ephemeral" }));
});
it("respects ANTHROPIC_PROMPT_CACHE_TTL=1h when producing the fallback cacheControl", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime({ ANTHROPIC_PROMPT_CACHE_TTL: "1h" }), {
prompt: "hello",
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const messages = call.messages as Array<Record<string, unknown>>;
const anthropicOpts = (messages?.[0]?.providerOptions as Record<string, unknown>)?.anthropic as
| Record<string, unknown>
| undefined;
expect(anthropicOpts?.cacheControl).toEqual({
type: "ephemeral",
ttl: "1h",
});
});
it("does NOT inject cacheControl for non-Anthropic models even when ANTHROPIC_PROMPT_CACHE_TTL is set", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(
createRuntime({
OPENROUTER_LARGE_MODEL: "google/gemini-2.5-flash",
ANTHROPIC_PROMPT_CACHE_TTL: "1h",
}),
{ prompt: "hello" } as never
);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
// Non-Anthropic path routes through the plain prompt/system path — no message array with
// cacheControl injected.
if (Array.isArray(call.messages)) {
for (const msg of call.messages as Array<Record<string, unknown>>) {
expect(msg?.providerOptions).toBeUndefined();
}
}
const wireAnthropicOpts = (call.providerOptions as Record<string, unknown> | undefined)
?.anthropic as Record<string, unknown> | undefined;
expect(wireAnthropicOpts?.cacheControl).toBeUndefined();
});
});
describe("Anthropic cache injection — internal field stripping", () => {
it("strips cacheBreakpoints and maxBreakpoints from wire providerOptions", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), {
prompt: "hello",
providerOptions: {
anthropic: {
cacheControl: { type: "ephemeral" },
cacheBreakpoints: [{ segmentIndex: 0, cacheControl: { type: "ephemeral" } }],
maxBreakpoints: 2,
},
gateway: { caching: "auto" },
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const providerOpts = call.providerOptions as Record<string, unknown> | undefined;
const wireAnthropic = providerOpts?.anthropic as Record<string, unknown> | undefined;
expect(wireAnthropic?.cacheBreakpoints).toBeUndefined();
expect(wireAnthropic?.maxBreakpoints).toBeUndefined();
// Non-Anthropic provider options pass through untouched
expect(providerOpts?.gateway).toEqual({ caching: "auto" });
});
it("strips cacheSystem from wire options while keeping remaining anthropic fields", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime({ OPENROUTER_LARGE_MODEL: "anthropic/claude-opus-4-8" }), {
messages: [
{ role: "system", content: "system prompt" },
{ role: "user", content: "hello" },
],
providerOptions: {
anthropic: { cacheSystem: true, thinking: { type: "enabled" } },
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const wireAnthropic = (call.providerOptions as Record<string, unknown> | undefined)
?.anthropic as Record<string, unknown> | undefined;
expect(wireAnthropic?.cacheSystem).toBeUndefined();
expect(wireAnthropic?.thinking).toEqual({ type: "enabled" });
});
});
describe("Anthropic cache injection — segmented user content", () => {
it("builds N content blocks for N promptSegments, applying cacheControl only at breakpoint indices", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), {
prompt: "seg1seg2",
promptSegments: [
{ content: "seg1", stable: true },
{ content: "seg2", stable: false },
],
providerOptions: {
anthropic: {
cacheBreakpoints: [{ segmentIndex: 0, cacheControl: { type: "ephemeral" } }],
},
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const messages = call.messages as Array<Record<string, unknown>>;
const userMsg = messages?.[1];
const content = userMsg?.content as Array<Record<string, unknown>>;
expect(content).toHaveLength(2);
expect(content[0]?.text).toBe("seg1");
const seg0Opts = (content[0]?.providerOptions as Record<string, unknown>)?.anthropic as
| Record<string, unknown>
| undefined;
expect(seg0Opts?.cacheControl).toEqual({ type: "ephemeral" });
expect(content[1]?.text).toBe("seg2");
expect(content[1]?.providerOptions).toBeUndefined();
});
it("filters out cacheBreakpoints with invalid shapes without crashing", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), {
prompt: "text",
promptSegments: [{ content: "text", stable: true }],
providerOptions: {
anthropic: {
cacheBreakpoints: [
{
segmentIndex: "not-a-number",
cacheControl: { type: "ephemeral" },
},
{ segmentIndex: 0, cacheControl: { type: "invalid" } },
null,
42,
],
},
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const messages = call.messages as Array<Record<string, unknown>>;
// No valid breakpoints after filtering → user content blocks carry no cacheControl
const userMsg = messages?.[1];
if (userMsg) {
const content = userMsg.content as Array<Record<string, unknown>>;
for (const block of content) {
expect(block.providerOptions).toBeUndefined();
}
}
});
it("caps applied breakpoints at maxBreakpoints", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), {
prompt: "s0s1s2s3",
promptSegments: [
{ content: "s0", stable: true },
{ content: "s1", stable: true },
{ content: "s2", stable: true },
{ content: "s3", stable: false },
],
providerOptions: {
anthropic: {
maxBreakpoints: 1,
cacheBreakpoints: [
{ segmentIndex: 0, cacheControl: { type: "ephemeral" } },
{ segmentIndex: 1, cacheControl: { type: "ephemeral" } },
{ segmentIndex: 2, cacheControl: { type: "ephemeral" } },
],
},
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const messages = call.messages as Array<Record<string, unknown>>;
const userMsg = messages?.[1];
const content = userMsg?.content as Array<Record<string, unknown>>;
const cachedBlocks = content?.filter(
(b) => (b.providerOptions as Record<string, unknown> | undefined)?.anthropic !== undefined
);
// Only segmentIndex 0 survives the cap of 1
expect(cachedBlocks).toHaveLength(1);
expect(cachedBlocks?.[0]?.text).toBe("s0");
});
});
describe("Anthropic cache injection — caller providerOptions survive verbatim", () => {
it("preserves openrouter.promptCacheKey and arbitrary provider keys alongside injected cacheControl", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), {
prompt: "hello",
providerOptions: {
openrouter: { promptCacheKey: "caller-key-123" },
gateway: { caching: "auto" },
customProvider: { nested: { flag: true }, count: 7 },
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const providerOpts = call.providerOptions as Record<string, unknown>;
// Caller keys survive unchanged into the serialized request.
expect(providerOpts.openrouter).toEqual({
promptCacheKey: "caller-key-123",
});
expect(providerOpts.gateway).toEqual({ caching: "auto" });
expect(providerOpts.customProvider).toEqual({
nested: { flag: true },
count: 7,
});
// And the injected message-level cacheControl is still applied on the system message.
const messages = call.messages as Array<Record<string, unknown>>;
const anthropicOpts = (messages?.[0]?.providerOptions as Record<string, unknown>)?.anthropic as
| Record<string, unknown>
| undefined;
expect(anthropicOpts?.cacheControl).toEqual(expect.objectContaining({ type: "ephemeral" }));
});
it("stamps cacheControl on multiple segment breakpoints while caller providerOptions survive", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), {
prompt: "s0s1s2",
promptSegments: [
{ content: "s0", stable: true },
{ content: "s1", stable: true },
{ content: "s2", stable: false },
],
providerOptions: {
openrouter: { promptCacheKey: "multi-bp" },
anthropic: {
cacheBreakpoints: [
{ segmentIndex: 0, cacheControl: { type: "ephemeral" } },
{ segmentIndex: 1, cacheControl: { type: "ephemeral", ttl: "1h" } },
],
},
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const messages = call.messages as Array<Record<string, unknown>>;
const content = messages?.[1]?.content as Array<Record<string, unknown>>;
expect(content).toHaveLength(3);
const cc0 = (content[0]?.providerOptions as Record<string, unknown>)?.anthropic as
| Record<string, unknown>
| undefined;
const cc1 = (content[1]?.providerOptions as Record<string, unknown>)?.anthropic as
| Record<string, unknown>
| undefined;
expect(cc0?.cacheControl).toEqual({ type: "ephemeral" });
expect(cc1?.cacheControl).toEqual({ type: "ephemeral", ttl: "1h" });
expect(content[2]?.providerOptions).toBeUndefined();
// Caller-supplied openrouter option survives.
expect((call.providerOptions as Record<string, unknown>).openrouter).toEqual({
promptCacheKey: "multi-bp",
});
});
});
describe("Anthropic cache injection — malformed cacheControl fails loudly", () => {
it("throws when caller-supplied cacheControl has an unsupported type", async () => {
mockModules();
const { handleTextLarge } = await import("../models/text");
await expect(
handleTextLarge(createRuntime(), {
prompt: "hello",
providerOptions: {
anthropic: { cacheControl: { type: "persistent" } },
},
} as never)
).rejects.toThrow(/cacheControl/);
});
it("throws when cacheControl is present but not an object", async () => {
mockModules();
const { handleTextLarge } = await import("../models/text");
await expect(
handleTextLarge(createRuntime(), {
prompt: "hello",
providerOptions: { anthropic: { cacheControl: "ephemeral" } },
} as never)
).rejects.toThrow(/cacheControl/);
});
it("throws when cacheControl.ttl is an unsupported value", async () => {
mockModules();
const { handleTextLarge } = await import("../models/text");
await expect(
handleTextLarge(createRuntime(), {
prompt: "hello",
providerOptions: {
anthropic: { cacheControl: { type: "ephemeral", ttl: "2h" } },
},
} as never)
).rejects.toThrow(/ttl/);
});
});
describe("Anthropic cache injection — cacheSystem:false opt-out", () => {
it("passes system as plain string and does not inject cacheControl on system message", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), {
messages: [
{ role: "system", content: "system prompt" },
{ role: "user", content: "hello" },
],
providerOptions: {
anthropic: { cacheSystem: false },
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
// With cacheSystem:false the system is forwarded as call.system, not as a message
// with providerOptions, so there is no Anthropic cacheControl on any message.
expect(call.system).toBe("system prompt");
expect(call.messages).toEqual([{ role: "user", content: "hello" }]);
if (Array.isArray(call.messages)) {
for (const msg of call.messages as Array<Record<string, unknown>>) {
expect(msg?.providerOptions).toBeUndefined();
}
}
});
});
describe("Anthropic cache injection — tools and trajectory breakpoints", () => {
const trajectory = [
{ role: "user", content: [{ type: "text", text: "question" }] },
{
role: "assistant",
content: [{ type: "tool-call", toolCallId: "1", toolName: "READ", input: {} }],
},
{
role: "tool",
content: [
{
type: "tool-result",
toolCallId: "1",
toolName: "READ",
output: { type: "text", value: "result" },
},
],
},
];
it("stamps only the last tool and the kept-trajectory tail", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), {
prompt: "ignored",
messages: trajectory,
tools: {
READ: { description: "Read", inputSchema: { type: "object" } },
WRITE: { description: "Write", inputSchema: { type: "object" } },
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const tools = call.tools as Record<string, Record<string, unknown>>;
expect(tools.READ.providerOptions).toBeUndefined();
expect(tools.WRITE.providerOptions).toEqual({
anthropic: { cacheControl: { type: "ephemeral" } },
});
const messages = call.messages as Array<Record<string, unknown>>;
const tail = (messages.at(-1)?.content as Array<Record<string, unknown>>).at(-1);
expect(tail?.providerOptions).toEqual({
anthropic: { cacheControl: { type: "ephemeral" } },
});
const leadingUserPart = (messages[1]?.content as Array<Record<string, unknown>>)[0];
expect(leadingUserPart.providerOptions).toBeUndefined();
});
it("honors opt-outs, strips local flags, and preserves the runtime TTL", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime({ ANTHROPIC_PROMPT_CACHE_TTL: "1h" }), {
prompt: "ignored",
messages: trajectory,
tools: { READ: { description: "Read", inputSchema: { type: "object" } } },
providerOptions: {
anthropic: { cacheTools: false, cacheTrajectory: false },
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const tools = call.tools as Record<string, Record<string, unknown>>;
expect(tools.READ.providerOptions).toBeUndefined();
const messages = call.messages as Array<Record<string, unknown>>;
const tail = (messages.at(-1)?.content as Array<Record<string, unknown>>).at(-1);
expect(tail?.providerOptions).toBeUndefined();
expect(JSON.stringify(call.providerOptions ?? {})).not.toContain("cacheTools");
expect(JSON.stringify(call.providerOptions ?? {})).not.toContain("cacheTrajectory");
const systemAnthropic = (messages[0]?.providerOptions as Record<string, unknown>)?.anthropic;
expect(systemAnthropic).toEqual({
cacheControl: { type: "ephemeral", ttl: "1h" },
});
});
});
describe("Anthropic cache injection — breakpoint budget", () => {
it("reserves one of the four cache slots for the tools breakpoint", async () => {
const { generateText } = mockModules();
const { handleTextLarge } = await import("../models/text");
await handleTextLarge(createRuntime(), {
prompt: "s0s1s2",
promptSegments: [
{ content: "s0", stable: true },
{ content: "s1", stable: true },
{ content: "s2", stable: true },
],
tools: { READ: { description: "Read", inputSchema: { type: "object" } } },
providerOptions: {
anthropic: {
maxBreakpoints: 3,
cacheBreakpoints: [
{ segmentIndex: 0, cacheControl: { type: "ephemeral" } },
{ segmentIndex: 1, cacheControl: { type: "ephemeral" } },
{ segmentIndex: 2, cacheControl: { type: "ephemeral" } },
],
},
},
} as never);
const call = generateText.mock.calls[0][0] as Record<string, unknown>;
const messages = call.messages as Array<Record<string, unknown>>;
const segmentParts = messages[1]?.content as Array<Record<string, unknown>>;
expect(segmentParts.filter((part) => part.providerOptions)).toHaveLength(2);
expect(
(call.tools as Record<string, Record<string, unknown>>).READ.providerOptions
).toBeDefined();
});
});