148 lines
5.9 KiB
TypeScript
Vendored
148 lines
5.9 KiB
TypeScript
Vendored
import assert from "node:assert/strict";
|
|
import { test } from "vitest";
|
|
import {
|
|
buildRedisCompletionItems,
|
|
getRedisCompletionContext,
|
|
getRedisCompletionResultValidFor,
|
|
shouldAutoOpenRedisCompletion,
|
|
takesKeyArgument,
|
|
} from "../../apps/desktop/src/lib/redis/redisCompletion.ts";
|
|
|
|
function labels(items: { label: string }[]): string[] {
|
|
return items.map((item) => item.label);
|
|
}
|
|
|
|
test("command mode: completes command names by prefix (case-insensitive)", () => {
|
|
const items = buildRedisCompletionItems("GE", 2);
|
|
const names = labels(items);
|
|
assert.ok(names.includes("GET"));
|
|
assert.ok(names.includes("GETSET"));
|
|
assert.ok(names.includes("GETRANGE"));
|
|
// no subcommand forms leak in
|
|
assert.ok(!names.some((n) => n.includes(" ")));
|
|
// lowercase prefix also works
|
|
assert.ok(labels(buildRedisCompletionItems("ge", 2)).includes("GET"));
|
|
});
|
|
|
|
test("command mode: does not surface bare subcommand labels", () => {
|
|
const items = buildRedisCompletionItems("XGROUP", 6);
|
|
const names = labels(items);
|
|
assert.ok(names.includes("XGROUP"));
|
|
// "CREATE" alone is not a top-level command
|
|
assert.ok(!names.includes("CREATE"));
|
|
});
|
|
|
|
test("command mode: detail/info carry group + arity + safety", () => {
|
|
const items = buildRedisCompletionItems("FLUSHALL", 9);
|
|
const flush = items.find((item) => item.label === "FLUSHALL");
|
|
assert.ok(flush);
|
|
assert.match(flush!.detail ?? "", /server.*blocked/i);
|
|
assert.match(flush!.info ?? "", /Group:\s*server/i);
|
|
assert.match(flush!.info ?? "", /Arity:/i);
|
|
assert.match(flush!.info ?? "", /Safety:\s*blocked/i);
|
|
});
|
|
|
|
test("subcommand mode: after 'XGROUP ' suggests its subcommands", () => {
|
|
const items = buildRedisCompletionItems("XGROUP ", 7);
|
|
const names = labels(items);
|
|
assert.ok(names.includes("CREATE"));
|
|
assert.ok(names.includes("DESTROY"));
|
|
assert.ok(names.includes("SETID"));
|
|
// unrelated commands absent
|
|
assert.ok(!names.includes("GET"));
|
|
});
|
|
|
|
test("subcommand mode: filters subcommands by prefix", () => {
|
|
const items = buildRedisCompletionItems("XGROUP C", 8);
|
|
const names = labels(items);
|
|
assert.ok(names.includes("CREATE"));
|
|
assert.ok(names.includes("CREATECONSUMER"));
|
|
assert.ok(!names.includes("DESTROY"));
|
|
});
|
|
|
|
test("argument mode: offers key names for key-taking commands", () => {
|
|
const items = buildRedisCompletionItems("GET ", 4, { keys: ["user:1", "user:2", "config:db"] });
|
|
const names = labels(items);
|
|
assert.ok(names.includes("user:1"));
|
|
assert.ok(names.includes("user:2"));
|
|
});
|
|
|
|
test("argument mode: filters keys by substring prefix", () => {
|
|
const items = buildRedisCompletionItems("GET user", 8, { keys: ["user:1", "user:2", "config:db"] });
|
|
const names = labels(items);
|
|
assert.ok(names.includes("user:1"));
|
|
assert.ok(names.includes("user:2"));
|
|
assert.ok(!names.includes("config:db"));
|
|
});
|
|
|
|
test("argument mode: empty keys yields no key items", () => {
|
|
const items = buildRedisCompletionItems("GET ", 4, { keys: [] });
|
|
assert.equal(items.length, 0);
|
|
});
|
|
|
|
test("argument mode: does not suggest keys once the key argument is filled in", () => {
|
|
// GET takes exactly one key argument; after "GET key " the next slot is out of range.
|
|
assert.equal(buildRedisCompletionItems("GET key ", 8, { keys: ["user:1"] }).length, 0);
|
|
assert.equal(buildRedisCompletionItems("GET key extra ", 14, { keys: ["user:1"] }).length, 0);
|
|
// HSET takes key then field/value; after the key only non-key args remain.
|
|
assert.equal(buildRedisCompletionItems("HSET k ", 7, { keys: ["user:1"] }).length, 0);
|
|
});
|
|
|
|
test("argument mode: variadic key-list commands keep suggesting keys", () => {
|
|
// DEL accepts multiple keys — keep suggesting after the first one.
|
|
assert.ok(labels(buildRedisCompletionItems("DEL k1 ", 7, { keys: ["user:1"] })).includes("user:1"));
|
|
assert.ok(labels(buildRedisCompletionItems("EXISTS k1 k2 ", 13, { keys: ["user:1"] })).includes("user:1"));
|
|
});
|
|
|
|
test("argument mode: still suggests keys at the first argument slot", () => {
|
|
// GET <here> — first argument position
|
|
const items = buildRedisCompletionItems("GET ", 4, { keys: ["user:1", "user:2"] });
|
|
assert.ok(labels(items).includes("user:1"));
|
|
});
|
|
|
|
test("argument mode: non-key commands (FLUSHDB) do not suggest keys", () => {
|
|
const items = buildRedisCompletionItems("FLUSHDB ", 8, { keys: ["user:1", "user:2"] });
|
|
assert.equal(items.length, 0);
|
|
});
|
|
|
|
test("takesKeyArgument reflects command group", () => {
|
|
assert.equal(takesKeyArgument("GET"), true); // string
|
|
assert.equal(takesKeyArgument("HSET"), true); // hash
|
|
assert.equal(takesKeyArgument("DEL"), true); // generic
|
|
assert.equal(takesKeyArgument("FLUSHDB"), false); // server
|
|
assert.equal(takesKeyArgument("SELECT"), false); // connection
|
|
assert.equal(takesKeyArgument(undefined), false);
|
|
});
|
|
|
|
test("context parsing: empty line → command mode", () => {
|
|
const ctx = getRedisCompletionContext("", 0);
|
|
assert.equal(ctx.mode, "command");
|
|
assert.equal(ctx.prefix, "");
|
|
});
|
|
|
|
test("context parsing: command + space → subcommand mode when the command has subcommands", () => {
|
|
const ctx = getRedisCompletionContext("XGROUP ", 7);
|
|
assert.equal(ctx.mode, "subcommand");
|
|
assert.equal(ctx.mainCommand, "XGROUP");
|
|
});
|
|
|
|
test("context parsing: GET + space → argument mode", () => {
|
|
const ctx = getRedisCompletionContext("GET ", 4);
|
|
assert.equal(ctx.mode, "argument");
|
|
assert.equal(ctx.mainCommand, "GET");
|
|
});
|
|
|
|
test("shouldAutoOpenRedisCompletion opens on word/space chars, not newline", () => {
|
|
assert.equal(shouldAutoOpenRedisCompletion("GET", 3), true);
|
|
assert.equal(shouldAutoOpenRedisCompletion("GET ", 4), true); // space → offer keys/subcommands
|
|
assert.equal(shouldAutoOpenRedisCompletion("GET\n", 4), false);
|
|
assert.equal(shouldAutoOpenRedisCompletion("user:", 5), true);
|
|
});
|
|
|
|
test("result validFor covers command-name and key-name characters", () => {
|
|
const re = getRedisCompletionResultValidFor();
|
|
assert.equal(re.test("GET"), true);
|
|
assert.equal(re.test("user:1"), true);
|
|
assert.equal(re.test("a.b-c"), true);
|
|
});
|