Skip to content

Commit e857c1b

Browse files
nkaradzhovclaude
andcommitted
align Search policy entries with the HLD command routing table
The static policy table had three problems for the Search module: - The `FT` module entries were upper-cased while every other module uses lower-case keys. - A second `search` module held three RediSearch cluster-admin commands (`CLUSTERSET`, `CLUSTERINFO`, `CLUSTERREFRESH`) that are not part of the public FT.* surface. - A `_FT` module held private debug subcommands (`_FT.CONFIG`, `_FT.DEBUG`, etc.) and a long list of deprecated / private FT.* entries (`_LIST`, `_CREATEIFNX`, `_DROPIFX`, `_DROPINDEXIFX`, `_ALIASADDIFNX`, `_ALIASDELIFX`, `_ALTERIFNX`, `ADD`, `DEL`, `GET`, `MGET`, `SYNADD`) which are not user-facing and shouldn't participate in policy resolution. Replace those three modules with a single lower-case `ft` module containing exactly the 25 commands listed in the HLD Command Routing Policy Table, each with the policy the client should apply per the HLD client-interpretation column. `ft.cursor` is left at `default-keyless`/`default-keyless` for now; the HLD specifies `special` (sticky cursor) but that depends on the not-yet-built special-handler registry and cursor binding map. Add `ft-policies.spec.ts`, a parameterised test that asserts every HLD command resolves to the prescribed `request`/`response` pair and that the dropped debug / admin commands no longer resolve. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent dc99602 commit e857c1b

2 files changed

Lines changed: 114 additions & 330 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { strict as assert } from 'node:assert';
2+
import {
3+
StaticPolicyResolver,
4+
REQUEST_POLICIES_WITH_DEFAULTS,
5+
RESPONSE_POLICIES_WITH_DEFAULTS
6+
} from '.';
7+
8+
/**
9+
* Snapshot of the HLD "Command Routing Policy Table" — client interpretation column.
10+
*
11+
* `default(keyless)` and `default(hashslot)` from the HLD denote "no policy
12+
* declared"; the client routes by the default rules. They are stored here as
13+
* `default-keyless` / `default-keyed` to match the resolver vocabulary.
14+
*
15+
* `ft.cursor` is mis-labeled `default-keyless` in this table on purpose.
16+
* The HLD specifies `special` request_policy (sticky cursor), which requires
17+
* the special-handler registry and cursor binding state. That work is tracked
18+
* in its own story — once those land, the entry below flips to `special` and
19+
* an extra cursor-routing test goes with it.
20+
*/
21+
const KEYLESS = {
22+
request: REQUEST_POLICIES_WITH_DEFAULTS.DEFAULT_KEYLESS,
23+
response: RESPONSE_POLICIES_WITH_DEFAULTS.DEFAULT_KEYLESS,
24+
isKeyless: true
25+
} as const;
26+
27+
const KEYED = {
28+
request: REQUEST_POLICIES_WITH_DEFAULTS.DEFAULT_KEYED,
29+
response: RESPONSE_POLICIES_WITH_DEFAULTS.DEFAULT_KEYED,
30+
isKeyless: false
31+
} as const;
32+
33+
const HLD_FT_TABLE: Record<string, typeof KEYLESS | typeof KEYED> = {
34+
'FT.CREATE': KEYLESS,
35+
'FT.SEARCH': KEYLESS,
36+
'FT.AGGREGATE': KEYLESS,
37+
'FT.DICTADD': KEYLESS,
38+
'FT.DICTDEL': KEYLESS,
39+
'FT.DICTDUMP': KEYLESS,
40+
'FT.SUGLEN': KEYED,
41+
'FT.CURSOR': KEYLESS,
42+
'FT.SUGADD': KEYED,
43+
'FT.SUGGET': KEYED,
44+
'FT.SUGDEL': KEYED,
45+
'FT.SPELLCHECK': KEYLESS,
46+
'FT.EXPLAIN': KEYLESS,
47+
'FT.EXPLAINCLI': KEYLESS,
48+
'FT.ALIASADD': KEYLESS,
49+
'FT.ALIASUPDATE': KEYLESS,
50+
'FT.ALIASDEL': KEYLESS,
51+
'FT.INFO': KEYLESS,
52+
'FT.TAGVALS': KEYLESS,
53+
'FT.SYNDUMP': KEYLESS,
54+
'FT.SYNUPDATE': KEYLESS,
55+
'FT.PROFILE': KEYLESS,
56+
'FT.ALTER': KEYLESS,
57+
'FT.DROPINDEX': KEYLESS,
58+
'FT.DROP': KEYLESS
59+
};
60+
61+
describe('FT.* policy table matches the HLD', () => {
62+
const resolver = new StaticPolicyResolver();
63+
64+
for (const [command, expected] of Object.entries(HLD_FT_TABLE)) {
65+
it(`${command} resolves to the HLD policy`, () => {
66+
const result = resolver.resolvePolicy({ command, subcommand: undefined });
67+
assert.equal(result.ok, true, `expected ${command} to resolve`);
68+
if (result.ok) {
69+
assert.equal(result.value.request, expected.request, `${command} request`);
70+
assert.equal(result.value.response, expected.response, `${command} response`);
71+
assert.equal(result.value.isKeyless, expected.isKeyless, `${command} isKeyless`);
72+
}
73+
});
74+
}
75+
76+
it('does not expose dropped debug commands (e.g. FT._LIST)', () => {
77+
const result = resolver.resolvePolicy({ command: 'FT._LIST', subcommand: undefined });
78+
assert.equal(result.ok, false);
79+
});
80+
81+
it('does not expose stray cluster-admin commands (e.g. FT.CLUSTERSET)', () => {
82+
const result = resolver.resolvePolicy({ command: 'FT.CLUSTERSET', subcommand: undefined });
83+
assert.equal(result.ok, false);
84+
});
85+
});

0 commit comments

Comments
 (0)