Files
insomnia/packages/insomnia-data/node-src/services/helpers/command-search.test.ts
Bingbing 8a1dc1ef62 perf: rewrite command palette search to eliminate UI freezes and stale results (#10257)
* perf: rewrite command palette search to eliminate UI freezes and stale results

Problems fixed:
- clientLoader blocked navigation on every palette open, freezing the UI during
  full sequential DB scan
- no AbortController: stale searches could overwrite newer results mid-type
- no debounce: every keystroke triggered a full DB traversal immediately
- React Aria's default contains filter re-filtered server-side fuzzy results,
  silently dropping valid matches and making fuzzy work redundant
- no warm baseline: palette started blank until the entire load completed

Changes:
- move fuzzyMatch/fuzzyMatchAll → insomnia-data/common-src/search.ts (shared,
  testable in node)
- move search logic → insomnia-data/node-src/services/helpers/command-search.ts
  - per-request AbortController cancellation via abort(requestId)
  - recursion depth guard (max 20) on request group traversal
  - safeParent() replaces non-null assertions, safe against orphaned documents
- add CommandSearchResult type in insomnia-data/src/command-search-types.ts
- add useCommandSearch hook: 250ms debounce + abort on filter change + warm
  baseline on mount
- add defaultFilter={() => true} to ComboBox, delegating all filtering to the
  service
- delete routes/commands.tsx

* fix

* fix
2026-07-17 01:07:38 +00:00

447 lines
15 KiB
TypeScript

import { services } from 'insomnia-data';
import { describe, expect, it } from 'vitest';
import { abortCommandSearch, commandSearch } from './command-search';
let seq = 0;
const uid = (prefix: string) => `${prefix}_cs_${++seq}`;
function makeOrg() {
const id = uid('org');
return { organizationId: id, allOrganizations: [{ id, name: `Org ${id}` }] };
}
describe('search()', () => {
it('returns empty results when the org has no projects', async () => {
const { organizationId, allOrganizations } = makeOrg();
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: uid('proj'),
workspaceId: null,
filter: null,
requestId: uid('rid'),
});
expect(result).toEqual({
requestId: expect.any(String),
current: { requests: [], files: [], environments: [] },
other: { requests: [], files: [] },
});
});
it('partitions requests: current workspace vs other workspaces', async () => {
const { organizationId, allOrganizations } = makeOrg();
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'P', parentId: organizationId });
const ws1Id = uid('wrk');
const ws2Id = uid('wrk');
await services.workspace.create({ _id: ws1Id, name: 'WS1', parentId: projId, scope: 'collection' });
await services.workspace.create({ _id: ws2Id, name: 'WS2', parentId: projId, scope: 'collection' });
const r1 = await services.request.create({ _id: uid('r'), name: 'Req1', parentId: ws1Id, url: 'http://a.com' });
const r2 = await services.request.create({ _id: uid('r'), name: 'Req2', parentId: ws2Id, url: 'http://b.com' });
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: projId,
workspaceId: ws1Id,
filter: null,
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: { requests: [{ id: r1._id }] },
other: { requests: [{ id: r2._id }] },
});
});
it('partitions files: current project vs other projects', async () => {
const { organizationId, allOrganizations } = makeOrg();
const proj1Id = uid('proj');
const proj2Id = uid('proj');
await services.project.create({ _id: proj1Id, name: 'P1', parentId: organizationId });
await services.project.create({ _id: proj2Id, name: 'P2', parentId: organizationId });
const ws1 = await services.workspace.create({
_id: uid('wrk'),
name: 'File1',
parentId: proj1Id,
scope: 'collection',
});
const ws2 = await services.workspace.create({
_id: uid('wrk'),
name: 'File2',
parentId: proj2Id,
scope: 'collection',
});
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: proj1Id,
workspaceId: null,
filter: null,
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: { files: [{ id: ws1._id }] },
other: { files: [{ id: ws2._id }] },
});
});
it('filters requests by fuzzy match on name/url', async () => {
const { organizationId, allOrganizations } = makeOrg();
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'P', parentId: organizationId });
const wsId = uid('wrk');
await services.workspace.create({ _id: wsId, name: 'WS', parentId: projId, scope: 'collection' });
const match = await services.request.create({
_id: uid('r'),
name: 'Get Users',
parentId: wsId,
url: 'http://api.com/users',
});
await services.request.create({ _id: uid('r'), name: 'Totally Different', parentId: wsId, url: 'http://zzz.com' });
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: projId,
workspaceId: wsId,
filter: 'users',
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: { requests: [{ id: match._id, name: 'Get Users' }] },
});
});
it('filters files (workspaces) by fuzzy match on name', async () => {
const { organizationId, allOrganizations } = makeOrg();
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'P', parentId: organizationId });
const matchWs = await services.workspace.create({
_id: uid('wrk'),
name: 'Payments API',
parentId: projId,
scope: 'collection',
});
await services.workspace.create({
_id: uid('wrk'),
name: 'Completely Unrelated',
parentId: projId,
scope: 'collection',
});
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: projId,
workspaceId: null,
filter: 'payments',
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: { files: [{ id: matchWs._id, name: 'Payments API' }] },
});
});
it('includes base env + sub-environments when workspaceId is set', async () => {
const { organizationId, allOrganizations } = makeOrg();
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'P', parentId: organizationId });
const wsId = uid('wrk');
await services.workspace.create({ _id: wsId, name: 'WS', parentId: projId, scope: 'collection' });
const baseEnv = await services.environment.create({ _id: uid('env'), name: 'Base Environment', parentId: wsId });
const prodEnv = await services.environment.create({ _id: uid('env'), name: 'Production', parentId: baseEnv._id });
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: projId,
workspaceId: wsId,
filter: null,
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: {
environments: expect.arrayContaining([
expect.objectContaining({ _id: baseEnv._id }),
expect.objectContaining({ _id: prodEnv._id }),
]),
},
});
});
it('returns no environments when workspaceId is null', async () => {
const { organizationId, allOrganizations } = makeOrg();
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'P', parentId: organizationId });
const wsId = uid('wrk');
await services.workspace.create({ _id: wsId, name: 'WS', parentId: projId, scope: 'collection' });
await services.environment.create({ _id: uid('env'), name: 'Base Environment', parentId: wsId });
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: projId,
workspaceId: null,
filter: null,
requestId: uid('rid'),
});
expect(result).toMatchObject({ current: { environments: [] } });
});
it('filters environments by fuzzy match on name', async () => {
const { organizationId, allOrganizations } = makeOrg();
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'P', parentId: organizationId });
const wsId = uid('wrk');
await services.workspace.create({ _id: wsId, name: 'WS', parentId: projId, scope: 'collection' });
const baseEnv = await services.environment.create({ _id: uid('env'), name: 'Base Environment', parentId: wsId });
const matchEnv = await services.environment.create({ _id: uid('env'), name: 'Production', parentId: baseEnv._id });
await services.environment.create({ _id: uid('env'), name: 'Development', parentId: baseEnv._id });
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: projId,
workspaceId: wsId,
filter: 'production',
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: { environments: [expect.objectContaining({ _id: matchEnv._id, name: 'Production' })] },
});
});
it('finds requests nested inside request groups (2 levels deep)', async () => {
const { organizationId, allOrganizations } = makeOrg();
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'P', parentId: organizationId });
const wsId = uid('wrk');
await services.workspace.create({ _id: wsId, name: 'WS', parentId: projId, scope: 'collection' });
const folder = await services.requestGroup.create({ _id: uid('rg'), name: 'Auth', parentId: wsId });
const nested = await services.requestGroup.create({ _id: uid('rg'), name: 'OAuth', parentId: folder._id });
const req = await services.request.create({
_id: uid('r'),
name: 'Token',
parentId: nested._id,
url: 'http://auth.com',
});
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: projId,
workspaceId: wsId,
filter: null,
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: { requests: [{ id: req._id, name: 'Token' }] },
});
});
it('includes grpcRequests and webSocketRequests', async () => {
const { organizationId, allOrganizations } = makeOrg();
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'P', parentId: organizationId });
const wsId = uid('wrk');
await services.workspace.create({ _id: wsId, name: 'WS', parentId: projId, scope: 'collection' });
const grpc = await services.grpcRequest.create({
_id: uid('gr'),
name: 'GrpcCall',
parentId: wsId,
url: 'grpc://svc',
});
const wsr = await services.webSocketRequest.create({
_id: uid('ws'),
name: 'WsEvent',
parentId: wsId,
url: 'ws://svc',
});
const result = await commandSearch({
allOrganizations,
organizationId,
projectId: projId,
workspaceId: wsId,
filter: null,
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: {
requests: expect.arrayContaining([
expect.objectContaining({ id: grpc._id, name: 'GrpcCall' }),
expect.objectContaining({ id: wsr._id, name: 'WsEvent' }),
]),
},
});
});
it('builds the correct request URL and metadata fields', async () => {
const orgId = uid('org');
const allOrganizations = [{ id: orgId, name: 'My Org' }];
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'My Project', parentId: orgId });
const wsId = uid('wrk');
await services.workspace.create({ _id: wsId, name: 'My WS', parentId: projId, scope: 'collection' });
const req = await services.request.create({
_id: uid('r'),
name: 'Get Items',
parentId: wsId,
url: 'http://example.com',
});
const result = await commandSearch({
allOrganizations,
organizationId: orgId,
projectId: projId,
workspaceId: wsId,
filter: null,
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: {
requests: [
{
id: req._id,
name: 'Get Items',
url: `/organization/${orgId}/project/${projId}/workspace/${wsId}/debug/request/${req._id}`,
organizationId: orgId,
projectId: projId,
workspaceId: wsId,
organizationName: 'My Org',
projectName: 'My Project',
workspaceName: 'My WS',
},
],
},
});
});
it('aggregates requests across multiple organizations', async () => {
const orgAId = uid('org');
const orgBId = uid('org');
const allOrganizations = [
{ id: orgAId, name: 'Org A' },
{ id: orgBId, name: 'Org B' },
];
const projA = await services.project.create({ _id: uid('proj'), name: 'ProjA', parentId: orgAId });
const projB = await services.project.create({ _id: uid('proj'), name: 'ProjB', parentId: orgBId });
const wsA = await services.workspace.create({
_id: uid('wrk'),
name: 'WsA',
parentId: projA._id,
scope: 'collection',
});
const wsB = await services.workspace.create({
_id: uid('wrk'),
name: 'WsB',
parentId: projB._id,
scope: 'collection',
});
const reqA = await services.request.create({ _id: uid('r'), name: 'ReqA', parentId: wsA._id, url: 'http://a.com' });
const reqB = await services.request.create({ _id: uid('r'), name: 'ReqB', parentId: wsB._id, url: 'http://b.com' });
const result = await commandSearch({
allOrganizations,
organizationId: orgAId,
projectId: projA._id,
workspaceId: wsA._id,
filter: null,
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: { requests: [{ id: reqA._id, organizationId: orgAId }] },
other: { requests: [{ id: reqB._id, organizationId: orgBId }] },
});
});
it('scratchpad org scopes only to its own projects, ignoring allOrganizations', async () => {
const scratchpadOrgId = 'org_scratchpad';
const otherOrgId = uid('org');
const allOrganizations = [
{ id: scratchpadOrgId, name: 'Scratchpad' },
{ id: otherOrgId, name: 'Other Org' },
];
const scratchProj = await services.project.create({
_id: uid('proj'),
name: 'ScratchProj',
parentId: scratchpadOrgId,
});
const otherProj = await services.project.create({ _id: uid('proj'), name: 'OtherProj', parentId: otherOrgId });
const scratchWs = await services.workspace.create({
_id: uid('wrk'),
name: 'ScratchWs',
parentId: scratchProj._id,
scope: 'collection',
});
const otherWs = await services.workspace.create({
_id: uid('wrk'),
name: 'OtherWs',
parentId: otherProj._id,
scope: 'collection',
});
const scratchReq = await services.request.create({
_id: uid('r'),
name: 'ScratchReq',
parentId: scratchWs._id,
url: 'http://scratch.com',
});
await services.request.create({ _id: uid('r'), name: 'OtherReq', parentId: otherWs._id, url: 'http://other.com' });
const result = await commandSearch({
allOrganizations,
organizationId: scratchpadOrgId,
projectId: scratchProj._id,
workspaceId: scratchWs._id,
filter: null,
requestId: uid('rid'),
});
expect(result).toMatchObject({
current: { requests: [{ id: scratchReq._id }] },
other: { requests: [] },
});
});
});
describe('abort()', () => {
it('does not throw when aborting an unknown requestId', async () => {
await expect(abortCommandSearch('nonexistent-request-id')).resolves.toBeUndefined();
});
it('rejects with AbortError when aborted synchronously before first DB await settles', async () => {
const { organizationId, allOrganizations } = makeOrg();
const projId = uid('proj');
await services.project.create({ _id: projId, name: 'P', parentId: organizationId });
const requestId = uid('rid');
const searchPromise = commandSearch({
allOrganizations,
organizationId,
projectId: projId,
workspaceId: null,
filter: null,
requestId,
});
// controller.abort() inside abort() is synchronous — fires the abort event
// before any NeDB microtask can resolve, guaranteeing AbortError.
void abortCommandSearch(requestId);
await expect(searchPromise).rejects.toMatchObject({ name: 'AbortError' });
});
});