204 lines
5.2 KiB
Go
204 lines
5.2 KiB
Go
package websearch
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
func loadFixture(t *testing.T, name string) Payload {
|
|
t.Helper()
|
|
_, file, _, ok := runtime.Caller(0)
|
|
if !ok {
|
|
t.Fatal("runtime.Caller")
|
|
}
|
|
path := filepath.Join(filepath.Dir(file), "..", "..", "bin", "tools", "web-search", "fixtures", name)
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var p Payload
|
|
if err := json.Unmarshal(raw, &p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return p
|
|
}
|
|
|
|
func TestClassifyHealthyIsOK(t *testing.T) {
|
|
if got := Classify(loadFixture(t, "healthy.json")); got != StatusOK {
|
|
t.Fatalf("classify healthy = %q, want ok", got)
|
|
}
|
|
}
|
|
|
|
func TestClassifyEmptyIsThrottledNotEmpty(t *testing.T) {
|
|
got := Classify(loadFixture(t, "throttled.json"))
|
|
if got != StatusThrottled {
|
|
t.Fatalf("classify empty = %q, want throttled", got)
|
|
}
|
|
if got == "empty" || got == "no_results" {
|
|
t.Fatal("status must never sound like absence")
|
|
}
|
|
}
|
|
|
|
func TestProjectKeepsContextFields(t *testing.T) {
|
|
out := Project(loadFixture(t, "healthy.json"), 3, DefaultSnippetChars)
|
|
if out.Status != StatusOK {
|
|
t.Fatalf("status = %q", out.Status)
|
|
}
|
|
if len(out.Results) != 3 {
|
|
t.Fatalf("len = %d, want 3", len(out.Results))
|
|
}
|
|
r := out.Results[0]
|
|
if r.Rank != 1 || r.Title == "" || r.URL == "" {
|
|
t.Fatalf("hit = %+v", r)
|
|
}
|
|
}
|
|
|
|
func TestProjectTrimsSnippet(t *testing.T) {
|
|
out := Project(loadFixture(t, "healthy.json"), 5, 40)
|
|
for _, r := range out.Results {
|
|
n := utf8.RuneCountInString(r.Snippet)
|
|
if n > 43 {
|
|
t.Fatalf("snippet len %d > 43: %q", n, r.Snippet)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestProjectIsCheaperThanRaw(t *testing.T) {
|
|
raw, err := os.ReadFile(filepath.Join(fixtureDir(t), "healthy.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out, err := json.Marshal(Project(loadFixture(t, "healthy.json"), 5, DefaultSnippetChars))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(out)*3 >= len(raw) {
|
|
t.Fatalf("projected %d not cheaper than raw %d", len(out), len(raw))
|
|
}
|
|
}
|
|
|
|
func TestThrottledProjectionCarriesEngineReasons(t *testing.T) {
|
|
out := Project(loadFixture(t, "throttled.json"), 5, DefaultSnippetChars)
|
|
if out.Status != StatusThrottled {
|
|
t.Fatalf("status = %q", out.Status)
|
|
}
|
|
if len(out.Results) != 0 {
|
|
t.Fatalf("results = %v", out.Results)
|
|
}
|
|
if len(out.Unresponsive) == 0 {
|
|
t.Fatal("unresponsive empty")
|
|
}
|
|
if !strings.Contains(out.Note, "not evidence that nothing exists") {
|
|
t.Fatalf("note = %q", out.Note)
|
|
}
|
|
}
|
|
|
|
func TestCacheKeyStable(t *testing.T) {
|
|
if CacheKey("Pflegegrad", nil) != CacheKey("Pflegegrad", map[string]string{}) {
|
|
t.Fatal("nil vs empty params")
|
|
}
|
|
if CacheKey(" Pflegegrad ", nil) != CacheKey("pflegegrad", nil) {
|
|
t.Fatal("case/padding")
|
|
}
|
|
if CacheKey("x", map[string]string{"lang": "de"}) == CacheKey("x", nil) {
|
|
t.Fatal("params must change key")
|
|
}
|
|
a := CacheKey("x", map[string]string{"a": "1", "b": "2"})
|
|
b := CacheKey("x", map[string]string{"b": "2", "a": "1"})
|
|
if a != b {
|
|
t.Fatal("param order must not change key")
|
|
}
|
|
}
|
|
|
|
func TestPHIGuard(t *testing.T) {
|
|
if PHIReason("Pflegegrad SGB XI Einstufung") != "" {
|
|
t.Fatal("technical query refused")
|
|
}
|
|
if PHIReason("site:example.com technical query") != "" {
|
|
t.Fatal("site query refused")
|
|
}
|
|
if PHIReason("SGB XI Paragraph 45b") != "" {
|
|
t.Fatal("short numbers refused")
|
|
}
|
|
if PHIReason("Kunde 4711220385 Adresse") == "" {
|
|
t.Fatal("long digit run allowed")
|
|
}
|
|
if PHIReason("KV-Nr A123456789") == "" {
|
|
t.Fatal("KV-Nr allowed")
|
|
}
|
|
if PHIReason("Hauptstraße 14 Berlin") == "" {
|
|
t.Fatal("street allowed")
|
|
}
|
|
if PHIReason("Lindenstr. 7") == "" {
|
|
t.Fatal("str. allowed")
|
|
}
|
|
if PHIReason("Personalnummer 12") == "" {
|
|
t.Fatal("Personalnummer allowed")
|
|
}
|
|
}
|
|
|
|
func TestWaitFor(t *testing.T) {
|
|
last := 100.0
|
|
if got := WaitFor(&last, 104.0, 10); got != 6 {
|
|
t.Fatalf("wait = %v, want 6", got)
|
|
}
|
|
if got := WaitFor(&last, 130.0, 10); got != 0 {
|
|
t.Fatalf("wait = %v, want 0", got)
|
|
}
|
|
if got := WaitFor(nil, 130.0, 10); got != 0 {
|
|
t.Fatalf("first call wait = %v", got)
|
|
}
|
|
}
|
|
|
|
func TestSQLiteCacheRoundTrip(t *testing.T) {
|
|
dir := t.TempDir()
|
|
c, err := OpenCache(filepath.Join(dir, "web-search.sqlite"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
p := loadFixture(t, "healthy.json")
|
|
key := CacheKey("pflegegrad", nil)
|
|
if got, err := c.Get(key, CacheTTL, 1_000); err != nil || got != nil {
|
|
t.Fatalf("empty get = %v %v", got, err)
|
|
}
|
|
if err := c.Put(key, p, 1_000); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := c.Get(key, CacheTTL, 1_001)
|
|
if err != nil || got == nil {
|
|
t.Fatalf("get = %v %v", got, err)
|
|
}
|
|
if Classify(*got) != StatusOK {
|
|
t.Fatalf("cached classify = %s", Classify(*got))
|
|
}
|
|
expired, err := c.Get(key, 10, 2_000)
|
|
if err != nil || expired != nil {
|
|
t.Fatalf("expired = %v %v", expired, err)
|
|
}
|
|
if v, err := c.LastCall(); err != nil || v != nil {
|
|
t.Fatalf("last = %v %v", v, err)
|
|
}
|
|
if err := c.MarkCall(50); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v, err := c.LastCall()
|
|
if err != nil || v == nil || *v != 50 {
|
|
t.Fatalf("last after mark = %v %v", v, err)
|
|
}
|
|
}
|
|
|
|
func fixtureDir(t *testing.T) string {
|
|
t.Helper()
|
|
_, file, _, ok := runtime.Caller(0)
|
|
if !ok {
|
|
t.Fatal("runtime.Caller")
|
|
}
|
|
return filepath.Join(filepath.Dir(file), "..", "..", "bin", "tools", "web-search", "fixtures")
|
|
}
|