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