const assert = require("assert"); const fs = require("fs"); const os = require("os"); const path = require("path"); const { SearchProvider } = require("../backend/provider_adapter"); const { formatResults } = require("../backend/result_formatter"); const { WebSearchTool } = require("../backend/search_tool"); const { readSettings, writeSettings } = require("../backend/settings"); const { evaluateUrl, matchesRule } = require("../backend/url_policy"); const { ToolRegistry } = require("../../lumi_ai/backend/tool_router"); const { ToolInstaller } = require("../../lumi_ai/backend/tool_installer"); const { ToolLoader } = require("../../lumi_ai/backend/tool_loader"); const plugin = require("../index"); const PUBLIC_DNS = async () => ["93.184.216.34"]; async function run() { await verifyPolicy(); await verifyRedirectPolicy(); verifyFormatting(); await verifySearchFlow(); await verifyLoaderLifecycle(); verifyRegistrationAvailability(); verifyStaticFiles(); console.log("Lumi AI Web Search verification passed."); } async function verifyLoaderLifecycle() { const root = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-web-loader-")); const pluginsDir = path.join(root, "plugins"); const toolDir = path.join(pluginsDir, "lumi_ai_web_search"); copyDirectory(path.resolve(__dirname, ".."), toolDir, new Set(["data"])); fs.mkdirSync(path.join(toolDir, "data"), { recursive: true }); const installer = new ToolInstaller({ pluginsDir, stagingRoot: path.join(root, "staging"), repoClient: {} }); const registry = new ToolRegistry(() => {}); const loader = new ToolLoader({ registry, installer, settings: { getSetting: (_key, fallback) => fallback }, stateFile: path.join(root, "enabled.json"), lumiAiVersion: "0.7.1", lumiVersion: "0.1.0" }); const unavailable = await loader.enable("lumi_ai_web_search"); assert.equal(unavailable.unavailable, true); assert.equal(registry.tools.has("lumi_ai_web_search.search"), false); writeSettings(path.join(toolDir, "data"), providerSettings()); const enabled = await loader.enable("lumi_ai_web_search"); assert.equal(enabled.loaded, true); assert.equal(registry.tools.has("lumi_ai_web_search.search"), true); await loader.disable("lumi_ai_web_search"); assert.equal(registry.tools.has("lumi_ai_web_search.search"), false); fs.rmSync(root, { recursive: true, force: true }); } async function verifyPolicy() { let result = await evaluateUrl("https://docs.example.com/guide", { mode: "whitelist", rules: ["*.example.com/*"], resolveHost: PUBLIC_DNS }); assert.equal(result.allowed, true); result = await evaluateUrl("https://unrelated.test/guide", { mode: "whitelist", rules: ["*.example.com/*"], resolveHost: PUBLIC_DNS }); assert.equal(result.allowed, false); assert.equal(result.reason, "not_whitelisted"); result = await evaluateUrl("https://ads.example.com/tracker", { mode: "blacklist", rules: ["*.example.com/tracker*"], resolveHost: PUBLIC_DNS }); assert.equal(result.allowed, false); result = await evaluateUrl("https://docs.example.org/", { mode: "blacklist", rules: ["*.example.com/tracker*"], resolveHost: PUBLIC_DNS }); assert.equal(result.allowed, true); for (const target of [ "http://127.0.0.1/", "http://10.1.2.3/", "http://169.254.169.254/latest/meta-data/", "http://localhost/", "file:///etc/passwd" ]) { result = await evaluateUrl(target, { mode: "blacklist", rules: [], resolveHost: PUBLIC_DNS }); assert.equal(result.allowed, false, target); } result = await evaluateUrl("https://dns-rebind.example/", { mode: "blacklist", rules: [], resolveHost: async () => ["10.0.0.8"] }); assert.equal(result.allowed, false); assert.equal(matchesRule(new URL("https://docs.example.com/guide/start"), "example.com/guide"), true); assert.equal(matchesRule(new URL("https://example.com/"), "http://example.com/"), false); } async function verifyRedirectPolicy() { const provider = new SearchProvider({ resolveHost: PUBLIC_DNS, fetch: async () => response({ status: 302, headers: { location: "http://127.0.0.1/private" } }) }); await assert.rejects( () => provider.search("test", providerSettings()), /blocked by policy/i ); let calls = 0; const crossOrigin = new SearchProvider({ resolveHost: PUBLIC_DNS, fetch: async () => { calls += 1; return response({ status: 302, headers: { location: "https://other-provider.example/search" } }); } }); await assert.rejects( () => crossOrigin.search("test", { ...providerSettings(), provider_api_key: "secret" }), /cross_origin_provider_redirect/i ); assert.equal(calls, 1); } function verifyFormatting() { const rows = [ result("Official docs", "https://docs.example.com/guide", "A detailed official answer for the requested subject.", "documentation"), result("Community post", "https://community.example.com/post", "A secondary explanation with useful context.", "web"), result("Recent update", "https://news.example.com/update", "A recently published update.", "news", "2026-06-12") ]; const settings = { max_results: 5, show_source_links: true, twitch_output_chars: 180, discord_output_chars: 700, webui_output_chars: 3000, other_output_chars: 500 }; const fact = formatResults(rows, { reason: "fact_lookup", origin: "twitch", settings }); const resource = formatResults(rows, { reason: "resource_lookup", origin: "discord", settings }); const webui = formatResults(rows, { reason: "documentation_lookup", origin: "webui", settings }); assert(fact.condensed_text.length <= 180); assert(fact.results.length <= 2); assert(resource.condensed_text.length <= 700); assert(webui.condensed_text.length > fact.condensed_text.length); assert.equal(webui.results[0].source_type, "documentation"); } async function verifySearchFlow() { const root = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-web-search-")); const settings = { ...providerSettings(), enabled: true, policy_mode: "whitelist", url_rules: ["*.example.com/*"], allowed_origins: ["webui", "discord", "twitch"], cache_ttl_seconds: 60 }; writeSettings(root, settings); let calls = 0; const provider = { resolveHost: PUBLIC_DNS, async search() { calls += 1; return [ result("Verified fact", "https://docs.example.com/fact", "The answer is current.", "documentation"), result("Blocked local", "http://127.0.0.1/private", "Must never be returned.", "web") ]; }, async fetchPage() { return { url: "https://docs.example.com/fact", text: "Expanded public page text." }; } }; const tool = new WebSearchTool({ dataDir: root, provider }); const first = await tool.run({ query: "current fact", reason: "fact_lookup", user: { id: "user-1" }, ctx: { origin: "webui", server_id: "server-1" } }); assert.equal(first.status, "ok"); assert.equal(first.result_count, 1); assert.equal(first.results[0].title, "Verified fact"); assert.equal(first.results.some((entry) => entry.url?.includes("127.0.0.1")), false); const cached = await tool.run({ query: "current fact", reason: "fact_lookup", user: { id: "user-1" }, ctx: { origin: "webui", server_id: "server-1" } }); assert.equal(cached.cache_hit, true); assert.equal(calls, 1); const twitch = await tool.run({ query: "current fact twitch", reason: "resource_lookup", user: { id: "user-1" }, ctx: { origin: "twitch", channel_id: "channel-1" } }); assert(twitch.condensed_text.length <= readSettings(root).twitch_output_chars); assert.equal(calls, 2); writeSettings(root, { ...readSettings(root), allowed_origins: ["webui"] }); const blockedOrigin = await tool.run({ query: "current fact", reason: "fact_lookup", origin: "webui", user: { id: "user-1" }, ctx: { origin: "discord" } }); assert.equal(blockedOrigin.status, "blocked"); assert.equal(blockedOrigin.blocked_reason, "origin_not_allowed"); const failing = new WebSearchTool({ dataDir: root, provider: { resolveHost: PUBLIC_DNS, async search() { throw new Error("provider secret https://provider.example/api?token=secret"); } } }); const failed = await failing.run({ query: "failure", reason: "general_lookup", user: { id: "user-2" }, ctx: { origin: "webui" } }); assert.equal(failed.status, "unavailable"); assert.equal(failed.error.includes("provider.example"), false); const audit = fs.readFileSync(path.join(root, "audit.jsonl"), "utf8") .trim().split(/\r?\n/).map(JSON.parse); assert(audit.some((entry) => entry.query === "current fact" && entry.actor === "user-1" && entry.origin === "webui" && typeof entry.timing_ms === "number" )); fs.rmSync(root, { recursive: true, force: true }); } function verifyRegistrationAvailability() { const root = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-web-register-")); assert.equal(plugin.checkAvailability({ paths: { data: root } }).available, false); writeSettings(root, { ...providerSettings(), enabled: true }); assert.equal(plugin.checkAvailability({ paths: { data: root } }).available, true); const definitions = []; plugin.register({ paths: { data: root }, registerTool: (definition) => definitions.push(definition) }); assert.equal(definitions.length, 1); assert.equal(definitions[0].tool_id, "lumi_ai_web_search.search"); assert.equal(definitions[0].permission_check({ user: { id: "user" }, context: { origin: "webui" } }), true); fs.rmSync(root, { recursive: true, force: true }); } function verifyStaticFiles() { const root = path.resolve(__dirname, ".."); const metadata = JSON.parse(fs.readFileSync(path.join(root, "tool_info.json"), "utf8")); assert.equal(metadata.tool_id, "lumi_ai_web_search"); assert.equal(metadata.settings_schema.policy_mode.default, "whitelist"); assert(fs.existsSync(path.join(root, "readme.md"))); assert(fs.readFileSync(path.join(root, "views", "settings-modal.ejs"), "utf8").includes("settings_schema")); } function providerSettings() { return { ...Object.fromEntries( Object.entries(require("../tool_info.json").settings_schema).map(([key, field]) => [key, structuredClone(field.default)]) ), provider_endpoint: "https://search.example.net/search", enabled: true, allowed_origins: ["webui", "discord", "twitch"], url_rules: ["*.example.com/*"] }; } function result(title, url, snippet, sourceType, date = null) { return { title, url, snippet, source_type: sourceType, date, relevance_score: 0.9 }; } function response({ status = 200, headers = {}, body = "" }) { const normalized = Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value])); return { ok: status >= 200 && status < 300, status, headers: { get(name) { return normalized[String(name).toLowerCase()] || null; } }, async arrayBuffer() { return Buffer.from(body); } }; } function copyDirectory(source, destination, ignored = new Set()) { fs.mkdirSync(destination, { recursive: true }); for (const entry of fs.readdirSync(source, { withFileTypes: true })) { if (ignored.has(entry.name)) continue; const from = path.join(source, entry.name); const to = path.join(destination, entry.name); if (entry.isDirectory()) copyDirectory(from, to); else if (entry.isFile()) fs.copyFileSync(from, to); } } run().catch((error) => { console.error(error); process.exitCode = 1; });