mirror of https://github.com/openclaw/openclaw.git
177 lines
5.7 KiB
TypeScript
177 lines
5.7 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { createLanceDbRuntimeLoader, type LanceDbRuntimeLogger } from "./lancedb-runtime.js";
|
|
|
|
const TEST_RUNTIME_MANIFEST = {
|
|
name: "openclaw-memory-lancedb-runtime",
|
|
private: true as const,
|
|
type: "module" as const,
|
|
dependencies: {
|
|
"@lancedb/lancedb": "^0.27.1",
|
|
},
|
|
};
|
|
|
|
type LanceDbModule = typeof import("@lancedb/lancedb");
|
|
type RuntimeManifest = {
|
|
name: string;
|
|
private: true;
|
|
type: "module";
|
|
dependencies: Record<string, string>;
|
|
};
|
|
|
|
function createMockModule(): LanceDbModule {
|
|
return {
|
|
connect: vi.fn(),
|
|
} as unknown as LanceDbModule;
|
|
}
|
|
|
|
function createLoader(
|
|
overrides: {
|
|
env?: NodeJS.ProcessEnv;
|
|
importBundled?: () => Promise<LanceDbModule>;
|
|
importResolved?: (resolvedPath: string) => Promise<LanceDbModule>;
|
|
resolveRuntimeEntry?: (params: {
|
|
runtimeDir: string;
|
|
manifest: RuntimeManifest;
|
|
}) => string | null;
|
|
installRuntime?: (params: {
|
|
runtimeDir: string;
|
|
manifest: RuntimeManifest;
|
|
env: NodeJS.ProcessEnv;
|
|
logger?: LanceDbRuntimeLogger;
|
|
}) => Promise<string>;
|
|
} = {},
|
|
) {
|
|
return createLanceDbRuntimeLoader({
|
|
env: overrides.env ?? ({} as NodeJS.ProcessEnv),
|
|
resolveStateDir: () => "/tmp/openclaw-state",
|
|
runtimeManifest: TEST_RUNTIME_MANIFEST,
|
|
importBundled:
|
|
overrides.importBundled ??
|
|
(async () => {
|
|
throw new Error("Cannot find package '@lancedb/lancedb'");
|
|
}),
|
|
importResolved: overrides.importResolved ?? (async () => createMockModule()),
|
|
resolveRuntimeEntry: overrides.resolveRuntimeEntry ?? (() => null),
|
|
installRuntime:
|
|
overrides.installRuntime ??
|
|
(async ({ runtimeDir }: { runtimeDir: string }) =>
|
|
`${runtimeDir}/node_modules/@lancedb/lancedb/index.js`),
|
|
});
|
|
}
|
|
|
|
describe("lancedb runtime loader", () => {
|
|
it("uses the bundled module when it is already available", async () => {
|
|
const bundledModule = createMockModule();
|
|
const importBundled = vi.fn(async () => bundledModule);
|
|
const importResolved = vi.fn(async () => createMockModule());
|
|
const resolveRuntimeEntry = vi.fn(() => null);
|
|
const installRuntime = vi.fn(async () => "/tmp/openclaw-state/plugin-runtimes/lancedb.js");
|
|
const loader = createLoader({
|
|
importBundled,
|
|
importResolved,
|
|
resolveRuntimeEntry,
|
|
installRuntime,
|
|
});
|
|
|
|
await expect(loader.load()).resolves.toBe(bundledModule);
|
|
|
|
expect(resolveRuntimeEntry).not.toHaveBeenCalled();
|
|
expect(installRuntime).not.toHaveBeenCalled();
|
|
expect(importResolved).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("reuses an existing user runtime install before attempting a reinstall", async () => {
|
|
const runtimeModule = createMockModule();
|
|
const importResolved = vi.fn(async () => runtimeModule);
|
|
const resolveRuntimeEntry = vi.fn(
|
|
() => "/tmp/openclaw-state/plugin-runtimes/memory-lancedb/runtime-entry.js",
|
|
);
|
|
const installRuntime = vi.fn(
|
|
async () => "/tmp/openclaw-state/plugin-runtimes/memory-lancedb/runtime-entry.js",
|
|
);
|
|
const loader = createLoader({
|
|
importResolved,
|
|
resolveRuntimeEntry,
|
|
installRuntime,
|
|
});
|
|
|
|
await expect(loader.load()).resolves.toBe(runtimeModule);
|
|
|
|
expect(resolveRuntimeEntry).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
runtimeDir: "/tmp/openclaw-state/plugin-runtimes/memory-lancedb/lancedb",
|
|
}),
|
|
);
|
|
expect(installRuntime).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("installs LanceDB into user state when the bundled runtime is unavailable", async () => {
|
|
const runtimeModule = createMockModule();
|
|
const logger: LanceDbRuntimeLogger = {
|
|
warn: vi.fn(),
|
|
info: vi.fn(),
|
|
};
|
|
const importResolved = vi.fn(async () => runtimeModule);
|
|
const resolveRuntimeEntry = vi.fn(() => null);
|
|
const installRuntime = vi.fn(
|
|
async ({ runtimeDir }: { runtimeDir: string }) =>
|
|
`${runtimeDir}/node_modules/@lancedb/lancedb/index.js`,
|
|
);
|
|
const loader = createLoader({
|
|
importResolved,
|
|
resolveRuntimeEntry,
|
|
installRuntime,
|
|
});
|
|
|
|
await expect(loader.load(logger)).resolves.toBe(runtimeModule);
|
|
|
|
expect(installRuntime).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
runtimeDir: "/tmp/openclaw-state/plugin-runtimes/memory-lancedb/lancedb",
|
|
manifest: TEST_RUNTIME_MANIFEST,
|
|
}),
|
|
);
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining(
|
|
"installing runtime deps under /tmp/openclaw-state/plugin-runtimes/memory-lancedb/lancedb",
|
|
),
|
|
);
|
|
});
|
|
|
|
it("fails fast in nix mode instead of attempting auto-install", async () => {
|
|
const installRuntime = vi.fn(
|
|
async ({ runtimeDir }: { runtimeDir: string }) =>
|
|
`${runtimeDir}/node_modules/@lancedb/lancedb/index.js`,
|
|
);
|
|
const loader = createLoader({
|
|
env: { OPENCLAW_NIX_MODE: "1" } as NodeJS.ProcessEnv,
|
|
installRuntime,
|
|
});
|
|
|
|
await expect(loader.load()).rejects.toThrow(
|
|
"memory-lancedb: failed to load LanceDB and Nix mode disables auto-install.",
|
|
);
|
|
expect(installRuntime).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("clears the cached failure so later calls can retry the install", async () => {
|
|
const runtimeModule = createMockModule();
|
|
const installRuntime = vi
|
|
.fn()
|
|
.mockRejectedValueOnce(new Error("network down"))
|
|
.mockResolvedValueOnce(
|
|
"/tmp/openclaw-state/plugin-runtimes/memory-lancedb/lancedb/node_modules/@lancedb/lancedb/index.js",
|
|
);
|
|
const importResolved = vi.fn(async () => runtimeModule);
|
|
const loader = createLoader({
|
|
installRuntime,
|
|
importResolved,
|
|
});
|
|
|
|
await expect(loader.load()).rejects.toThrow("network down");
|
|
await expect(loader.load()).resolves.toBe(runtimeModule);
|
|
|
|
expect(installRuntime).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|