find_test.mx raw
1 package find
2
3 import (
4 "bytes"
5 "os"
6 "testing"
7
8 "git.smesh.lol/nostr/pkg/event"
9 "git.smesh.lol/nostr/pkg/signer/p8k"
10 "git.smesh.lol/morly/pkg/store"
11 )
12
13 // openTmp opens a store in a fresh directory. The caller owns both: defer
14 // os.RemoveAll(dir) and defer eng.Close().
15 func openTmp(t *testing.T) (p *store.Engine, dir string) {
16 t.Helper()
17 dir, derr := os.MkdirTemp("", "moxie-test")
18 if derr != nil {
19 t.Fatal(derr)
20 }
21 eng, err := store.Open(dir)
22 if err != nil {
23 t.Fatal(err)
24 }
25 return eng, dir
26 }
27
28 func makeEvent(t *testing.T, s *p8k.Signer, content string, ts int64) (p *event.E) {
29 t.Helper()
30 ev := &event.E{
31 CreatedAt: ts,
32 Kind: 1,
33 Content: []byte(content),
34 }
35 if err := ev.Sign(s); err != nil {
36 t.Fatal(err)
37 }
38 return ev
39 }
40
41 func TestSplitWords(t *testing.T) {
42 tests := []struct {
43 input string
44 want []string
45 }{
46 {"hello world", []string{"hello", "world"}},
47 {"Hi There", []string{"there"}}, // "hi" too short
48 {"a bb ccc", []string{"ccc"}}, // min 3 chars
49 {"foo123 bar", []string{"foo123", "bar"}}, // alphanumeric
50 {"", nil},
51 }
52 for _, tt := range tests {
53 got := SplitWords([]byte(tt.input))
54 if len(got) != len(tt.want) {
55 t.Errorf("SplitWords(%q): got %d words, want %d", tt.input, len(got), len(tt.want))
56 continue
57 }
58 for i := range got {
59 if string(got[i]) != tt.want[i] {
60 t.Errorf("SplitWords(%q)[%d] = %q, want %q", tt.input, i, got[i], tt.want[i])
61 }
62 }
63 }
64 }
65
66 func TestSearchFindsEvent(t *testing.T) {
67 eng, dir := openTmp(t)
68 defer os.RemoveAll(dir)
69 defer eng.Close()
70 s := p8k.MustNew()
71 if err := s.Generate(); err != nil {
72 t.Fatal(err)
73 }
74
75 ev := makeEvent(t, s, "the quick brown fox jumps", 1700000000)
76 if err := eng.SaveEvent(ev); err != nil {
77 t.Fatal(err)
78 }
79
80 f := New(eng)
81 results := f.Search([]byte("quick brown"), 10)
82 if len(results) != 1 {
83 t.Fatalf("expected 1 result, got %d", len(results))
84 }
85 if !bytes.Equal(results[0].ID, ev.ID) {
86 t.Error("result ID mismatch")
87 }
88 }
89
90 func TestSearchNoMatch(t *testing.T) {
91 eng, dir := openTmp(t)
92 defer os.RemoveAll(dir)
93 defer eng.Close()
94 s := p8k.MustNew()
95 if err := s.Generate(); err != nil {
96 t.Fatal(err)
97 }
98
99 if err := eng.SaveEvent(makeEvent(t, s, "hello world foo", 1700000000)); err != nil {
100 t.Fatal(err)
101 }
102
103 f := New(eng)
104 results := f.Search([]byte("nonexistent"), 10)
105 if len(results) != 0 {
106 t.Fatalf("expected 0 results, got %d", len(results))
107 }
108 }
109
110 func TestSearchLimit(t *testing.T) {
111 eng, dir := openTmp(t)
112 defer os.RemoveAll(dir)
113 defer eng.Close()
114 s := p8k.MustNew()
115 if err := s.Generate(); err != nil {
116 t.Fatal(err)
117 }
118
119 for i := 0; i < 5; i++ {
120 if err := eng.SaveEvent(makeEvent(t, s, "testing search limit feature", int64(1700000000+i))); err != nil {
121 t.Fatal(err)
122 }
123 }
124
125 f := New(eng)
126 results := f.Search([]byte("testing search"), 2)
127 if len(results) != 2 {
128 t.Fatalf("expected 2 results, got %d", len(results))
129 }
130 }
131