munin/internal/store/suchprotokoll_test.go

127 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package store
import (
"context"
"testing"
"time"
)
// TestSearchStatsZaehlt prüft die Kennzahlen: alle Suchen, ohne Treffer, mit
// Synonymen — und nur innerhalb des Zeitfensters und des Projekts.
func TestSearchStatsZaehlt(t *testing.T) {
s := testStore(t)
ctx := context.Background()
p := testProject(t, s)
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: "deployment", Results: 3})
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: "release", Synonyms: []string{"rollout"}, Results: 0})
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: "vpn", Results: 0})
// Außerhalb des Fensters: darf nicht mitzählen.
alt := time.Now().UTC().Add(-UsageWindow - time.Hour).Format(time.RFC3339Nano)
if _, err := s.schreiben.ExecContext(ctx,
`INSERT INTO search_log (ts, project_id, query, results) VALUES (?, ?, 'alt', 0)`, alt, p.ID); err != nil {
t.Fatal(err)
}
st, err := s.SearchStatsFor(ctx, p.ID, time.Now())
if err != nil {
t.Fatal(err)
}
if st.Total != 3 || st.Empty != 2 || st.WithSynonyms != 1 {
t.Errorf("SearchStats = %+v, erwartet Total 3, Empty 2, WithSynonyms 1", st)
}
if st.EmptyPercent() != 67 || st.SynonymPercent() != 33 {
t.Errorf("Anteile = %d %% / %d %%, erwartet 67 / 33", st.EmptyPercent(), st.SynonymPercent())
}
}
// TestSearchStatsLeer prüft ein Projekt ohne Suchen: alles 0, kein Fehler.
func TestSearchStatsLeer(t *testing.T) {
s := testStore(t)
p := testProject(t, s)
st, err := s.SearchStatsFor(context.Background(), p.ID, time.Now())
if err != nil {
t.Fatal(err)
}
if st != (SearchStats{}) || st.EmptyPercent() != 0 {
t.Errorf("SearchStats = %+v, erwartet nur Nullen", st)
}
}
// TestEmptySearchesFasstZusammen prüft, dass gleiche Anfragen ohne Treffer
// zusammengefasst werden, unabhängig von Groß- und Kleinschreibung, und die
// häufigste vorne steht.
func TestEmptySearchesFasstZusammen(t *testing.T) {
s := testStore(t)
ctx := context.Background()
p := testProject(t, s)
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: "vpn zugang", Results: 0})
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: "VPN Zugang", Results: 0})
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: " vpn zugang ", Synonyms: []string{"remote"}, Results: 0})
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: "drucker", Results: 0})
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: "deployment", Results: 2}) // hat Treffer
liste, err := s.EmptySearches(ctx, p.ID, time.Now(), 10)
if err != nil {
t.Fatal(err)
}
if len(liste) != 2 {
t.Fatalf("%d Anfragen, erwartet 2: %+v", len(liste), liste)
}
if liste[0].Count != 3 {
t.Errorf("häufigste Anfrage = %+v, erwartet 3× vpn zugang", liste[0])
}
if liste[0].Synonyms != "remote" {
t.Errorf("Synonyme = %q, erwartet die der jüngsten Suche (remote)", liste[0].Synonyms)
}
if liste[1].Query != "drucker" {
t.Errorf("zweite Anfrage = %q, erwartet drucker", liste[1].Query)
}
}
// TestPruneSearchLog prüft das Aufräumen nach Ablauf der Frist.
func TestPruneSearchLog(t *testing.T) {
s := testStore(t)
ctx := context.Background()
p := testProject(t, s)
alt := time.Now().UTC().Add(-SearchLogRetention - time.Hour).Format(time.RFC3339Nano)
if _, err := s.schreiben.ExecContext(ctx,
`INSERT INTO search_log (ts, project_id, query, results) VALUES (?, ?, 'alt', 0)`, alt, p.ID); err != nil {
t.Fatal(err)
}
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: "neu", Results: 0})
grenze := time.Now().UTC().Add(-SearchLogRetention).Format(time.RFC3339Nano)
n, err := s.PruneSearchLog(ctx, grenze)
if err != nil {
t.Fatal(err)
}
if n != 1 {
t.Errorf("%d Zeilen entfernt, erwartet 1", n)
}
}
// TestSuchprotokollVerschwindetMitProjekt prüft den Fremdschlüssel: Anfragen
// sind Inhalt des Projekts und gehen mit ihm.
func TestSuchprotokollVerschwindetMitProjekt(t *testing.T) {
s := testStore(t)
ctx := context.Background()
p := testProject(t, s)
s.LogSearch(ctx, SearchLogEntry{ProjectID: p.ID, Query: "geheim", Results: 0})
if err := s.DeleteProject(ctx, p.ID); err != nil {
t.Fatal(err)
}
var anzahl int
if err := s.lesen.QueryRowContext(ctx, `SELECT COUNT(*) FROM search_log`).Scan(&anzahl); err != nil {
t.Fatal(err)
}
if anzahl != 0 {
t.Errorf("%d Anfragen des gelöschten Projekts sind noch da", anzahl)
}
}