fix(test): prevent config file from leaking kagi token into search test

The search token validation test only cleared KAGI_TOKEN from the
environment but still loaded the user config file, which could supply
the token and cause the test to pass incorrectly. Pass
--config=/nonexistent/path so loadConfig returns an empty object.

Also includes search command improvements: markdown/json format output
and --format flag.
This commit is contained in:
2026-05-02 19:29:53 -04:00
parent 8db4ed1370
commit eb1de23f4e
2 changed files with 29 additions and 3 deletions

View File

@@ -2,7 +2,7 @@
import { loadConfig, type GlimpseConfig } from "./config.js"; import { loadConfig, type GlimpseConfig } from "./config.js";
import { createDriver, type WebDriver } from "./driver.js"; import { createDriver, type WebDriver } from "./driver.js";
import { searchKagi } from "./providers/kagi.js"; import { searchKagi, type SearchResult } from "./providers/kagi.js";
import { readFileSync, writeFileSync } from "node:fs"; import { readFileSync, writeFileSync } from "node:fs";
import TurndownService from "turndown"; import TurndownService from "turndown";
@@ -118,6 +118,7 @@ Reader Options:
Search Options: Search Options:
--provider=<provider> Search provider: kagi (default: config or kagi) --provider=<provider> Search provider: kagi (default: config or kagi)
--token=<token> Kagi token (default: KAGI_TOKEN or config) --token=<token> Kagi token (default: KAGI_TOKEN or config)
--format=<format> Output format: markdown, json (default: markdown)
Examples: Examples:
glimpse snapshot https://example.com glimpse snapshot https://example.com
@@ -446,17 +447,33 @@ function renderReaderOutput(article: ReaderArticle, format: string) {
} }
} }
function searchResultsToMarkdown(results: SearchResult[]): string {
return results
.map((r) => `## [${r.title}](${r.url})\n> ${r.description}`)
.join("\n\n")
.trim();
}
async function searchCommand() { async function searchCommand() {
const provider = const provider =
getOption("--provider") ?? appConfig.search?.provider ?? "kagi"; getOption("--provider") ?? appConfig.search?.provider ?? "kagi";
const query = getPositionalArgs().join(" "); const query = getPositionalArgs().join(" ");
const format = getOption("--format") ?? "markdown";
if (!query) usage(); if (!query) usage();
if (!["markdown", "json"].includes(format)) {
cliError(
"INVALID_OPTION",
`Unsupported search format: ${format}. Expected markdown, json.`,
);
}
// Run Provider Search // Run Provider Search
let results: SearchResult[];
switch (provider) { switch (provider) {
case "kagi": case "kagi":
return searchKagi({ results = await searchKagi({
query, query,
token: getOption("--token"), token: getOption("--token"),
config: appConfig, config: appConfig,
@@ -464,12 +481,21 @@ async function searchCommand() {
existingUrl, existingUrl,
timeoutMs, timeoutMs,
}); });
break;
default: default:
cliError( cliError(
"UNSUPPORTED_SEARCH_PROVIDER", "UNSUPPORTED_SEARCH_PROVIDER",
`Unsupported search provider: ${provider}. Expected kagi.`, `Unsupported search provider: ${provider}. Expected kagi.`,
); );
} }
// Render Output
switch (format) {
case "markdown":
return searchResultsToMarkdown(results);
case "json":
return results;
}
} }
async function readerCommand() { async function readerCommand() {

View File

@@ -163,7 +163,7 @@ test(
test("search validates kagi token in provider", ["search", "errors"], () => { test("search validates kagi token in provider", ["search", "errors"], () => {
const env = { ...process.env }; const env = { ...process.env };
delete env.KAGI_TOKEN; delete env.KAGI_TOKEN;
const result = runCli(["search", "example query"], { env }); const result = runCli(["search", "--config=/nonexistent/path", "example query"], { env });
const output = parseJson(result.stderr); const output = parseJson(result.stderr);
assert.notEqual(result.status, 0, result.stdout || result.stderr); assert.notEqual(result.status, 0, result.stdout || result.stderr);