Files
buzz/desktop/src/testing/e2eBridgeSearch.test.mjs
Jatinder Mahajan cb2a265b53 feat(search): parse from:/in:/after:/before: and pass them in the filter (#2871)
## Summary
- Adds a pure-TS operator parser (`from:` / `in:` / `after:` /
`before:`) with unit tests. Invalid date tokens stay in the FTS text.
- Extends `search_messages` so the desktop can send `authors` / `since`
/ `until` (and existing `#h`) on the filter the relay already
understands.
- Wires topbar search to strip operators from the prefix query, resolve
`in:` against local channels and `from:` against hex pubkeys / known
agents, then pass the structured fields through.

This is part 1 of #2853 (parser + command plumbing). Autocomplete chips
/ richer `from:@name` resolution can follow in a second PR.

## Test plan
- [x] `node --import ./test-loader.mjs --experimental-strip-types --test
src/features/search/lib/parseSearchOperators.test.mjs`
- [x] Added `search_messages_filter_emits_operator_fields` unit test
(full `buzz-desktop` crate build needs local sidecar binaries in this
environment)
- [ ] Manual: topbar `deploy from:<hex> after:2024-01-01` emits
authors/since on the bridge filter and returns narrowed hits

Made with [Cursor](https://cursor.com)

---------

Signed-off-by: Jatinder Mahajan <jatinder.mahajan@certifyos.com>
Co-authored-by: Jatinder Mahajan <jatinder.mahajan@certifyos.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-27 21:52:56 +00:00

82 lines
2.3 KiB
JavaScript

/**
* Unit tests for the e2e bridge's mock `search_messages` predicate.
*
* The mock stands in for the relay in identity-less E2E runs, so its `since` /
* `until` comparators must match the relay's inclusive NIP-01 bounds
* (`crates/buzz-core/src/filter.rs` keeps `since <= created_at <= until`).
* `before:YYYY-MM-DD` gets its exclusivity from `parseSearchOperators`, which
* emits `localMidnight - 1`; a mock that also excluded its own bound would
* drop the last second of the range that production returns.
*/
import assert from "node:assert/strict";
import test from "node:test";
import { mockSearchHitMatches } from "./e2eBridge.ts";
const NO_FILTERS = { query: "", authorSet: null };
function hitAt(created_at) {
return {
event_id: "hit",
content: "deploy notes",
kind: 9,
pubkey: "a".repeat(64),
channel_id: "9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50",
channel_name: "general",
created_at,
score: 1,
};
}
test("until is inclusive, so a before: bound keeps its own second", () => {
const localMidnight = Math.floor(new Date(2024, 1, 1).getTime() / 1000);
// What parseSearchOperators emits for `before:2024-02-01`.
const until = localMidnight - 1;
assert.equal(
mockSearchHitMatches(hitAt(until), { ...NO_FILTERS, until }),
true,
);
assert.equal(
mockSearchHitMatches(hitAt(localMidnight), { ...NO_FILTERS, until }),
false,
);
});
test("since is inclusive and excludes only earlier timestamps", () => {
const since = Math.floor(new Date(2024, 0, 15).getTime() / 1000);
assert.equal(
mockSearchHitMatches(hitAt(since), { ...NO_FILTERS, since }),
true,
);
assert.equal(
mockSearchHitMatches(hitAt(since - 1), { ...NO_FILTERS, since }),
false,
);
});
test("channel, author, and text filters still narrow results", () => {
const hit = hitAt(1_700_000_000);
assert.equal(
mockSearchHitMatches(hit, { ...NO_FILTERS, channelId: "other-channel" }),
false,
);
assert.equal(
mockSearchHitMatches(hit, {
...NO_FILTERS,
authorSet: new Set(["b".repeat(64)]),
}),
false,
);
assert.equal(
mockSearchHitMatches(hit, { query: "zzz", authorSet: null }),
false,
);
assert.equal(
mockSearchHitMatches(hit, { query: "deploy", authorSet: null }),
true,
);
});