Files
openserp/core/serp_features_test.go

175 lines
6.2 KiB
Go

package core
import (
"encoding/json"
"strings"
"testing"
"time"
)
func TestEnvelopeAlwaysIncludesSerpFeatures(t *testing.T) {
env := NewEnvelope(Query{Text: "golang"}, "req-1", time.Unix(0, 0), []string{"google"})
data, err := json.Marshal(env)
if err != nil {
t.Fatalf("marshal envelope: %v", err)
}
if !strings.Contains(string(data), `"serp_features":[]`) {
t.Fatalf("expected empty serp_features array in JSON, got %s", data)
}
}
func TestAppendEnrichedSearchResultAddsFeatureSurface(t *testing.T) {
env := NewEnvelope(Query{Text: "weather"}, "req-1", time.Unix(0, 0), []string{"google"})
raw := SearchResult{
Rank: 1,
URL: "https://example.com/weather",
Title: "Weather result",
Description: "Organic snippet",
Features: []SerpFeature{{
Type: ResultTypeAnswerBox,
Text: "72 F and sunny",
Links: []FeatureLink{{
Title: "Weather source",
URL: "https://example.com/weather",
}},
Position: &Position{Absolute: 1},
Confidence: 0.9,
}},
}
AppendEnrichedSearchResult(env, raw, EnrichContext{Engine: "google", Query: Query{}}, time.Unix(0, 0))
if len(env.Results) != 1 {
t.Fatalf("expected 1 result, got %d", len(env.Results))
}
if len(env.SerpFeatures) != 1 {
t.Fatalf("expected 1 feature, got %d", len(env.SerpFeatures))
}
feature := env.SerpFeatures[0]
if feature.ID == "" || !strings.HasPrefix(feature.ID, "f_") {
t.Fatalf("expected stable feature ID, got %q", feature.ID)
}
if feature.Engine != "google" {
t.Fatalf("feature engine = %q, want google", feature.Engine)
}
if feature.ExtractedAt != "1970-01-01T00:00:00Z" {
t.Fatalf("feature extracted_at = %q", feature.ExtractedAt)
}
if len(feature.SourceResultIDs) != 1 || feature.SourceResultIDs[0] != env.Results[0].ID {
t.Fatalf("expected feature to reference result ID %q, got %#v", env.Results[0].ID, feature.SourceResultIDs)
}
}
func TestAppendEnrichedSearchResultMirrorsExistingAnswerResultAsFeature(t *testing.T) {
env := NewEnvelope(Query{Text: "weather"}, "req-1", time.Unix(0, 0), []string{"google"})
raw := SearchResult{
Rank: -1,
Type: ResultTypeAnswerBox,
URL: "https://example.com/weather",
Title: "Weather",
Description: "72 F and sunny",
}
AppendEnrichedSearchResult(env, raw, EnrichContext{Engine: "google", Query: Query{}}, time.Unix(0, 0))
if len(env.Results) != 1 {
t.Fatalf("expected existing result to be preserved, got %d results", len(env.Results))
}
if len(env.SerpFeatures) != 1 {
t.Fatalf("expected mirrored feature, got %d", len(env.SerpFeatures))
}
if env.SerpFeatures[0].Type != ResultTypeAnswerBox {
t.Fatalf("mirrored feature type = %q", env.SerpFeatures[0].Type)
}
if env.SerpFeatures[0].Text != "72 F and sunny" {
t.Fatalf("mirrored feature text = %q", env.SerpFeatures[0].Text)
}
if len(env.SerpFeatures[0].SourceResultIDs) != 1 || env.SerpFeatures[0].SourceResultIDs[0] != env.Results[0].ID {
t.Fatalf("mirrored feature did not reference result: %#v", env.SerpFeatures[0].SourceResultIDs)
}
}
func TestRenderersIncludeSerpFeatures(t *testing.T) {
env := NewEnvelope(Query{Text: "openserp"}, "req-1", time.Unix(0, 0), []string{"google"})
AppendEnrichedSearchResult(env, SearchResult{
Rank: 1,
URL: "https://example.com/result",
Title: "Organic result",
Description: "Snippet",
Features: []SerpFeature{
{
Type: ResultTypeAISummary,
Text: "OpenSERP is a search API.",
Links: []FeatureLink{{
Title: "Example",
URL: "https://example.com/source",
}},
Position: &Position{Absolute: 1},
Confidence: 0.95,
},
{
Type: ResultTypeRelatedSearches,
Items: []FeatureItem{
{Text: "openserp cloud"},
{Text: "serp api"},
},
},
},
}, EnrichContext{Engine: "google", Query: Query{}}, time.Unix(0, 0))
env.Finalize(time.Unix(0, 0), Query{Text: "openserp", Limit: 10})
markdown := string(RenderMarkdown(env))
if !strings.Contains(markdown, "## AI summary") || !strings.Contains(markdown, "Sources:") {
t.Fatalf("markdown missing AI summary feature section:\n%s", markdown)
}
if !strings.Contains(markdown, "## Results") {
t.Fatalf("markdown missing Results section:\n%s", markdown)
}
if !strings.Contains(markdown, "## Related searches") {
t.Fatalf("markdown missing related searches section:\n%s", markdown)
}
// Spec order: AI summary -> ... -> Results -> Related searches.
if aiIdx, resIdx, relIdx := strings.Index(markdown, "## AI summary"), strings.Index(markdown, "## Results"), strings.Index(markdown, "## Related searches"); !(aiIdx < resIdx && resIdx < relIdx) {
t.Fatalf("markdown section order wrong: ai=%d results=%d related=%d\n%s", aiIdx, resIdx, relIdx, markdown)
}
text := string(RenderText(env))
if !strings.Contains(text, "AI summary\n") || !strings.Contains(text, "Related searches\n") {
t.Fatalf("text missing feature sections:\n%s", text)
}
lines := strings.Split(strings.TrimSpace(string(RenderNDJSON(env))), "\n")
if len(lines) != 3 {
t.Fatalf("expected 3 ndjson lines, got %d: %v", len(lines), lines)
}
if !strings.Contains(lines[0], `"kind":"result"`) {
t.Fatalf("first ndjson line should be a result, got %s", lines[0])
}
if !strings.Contains(lines[1], `"kind":"feature"`) || !strings.Contains(lines[2], `"kind":"feature"`) {
t.Fatalf("feature ndjson lines missing kind tag: %v", lines)
}
}
// TestRenderFeaturesUnorderedTypeStillRenders guards against silently dropping a
// feature whose Type is in neither render-order section (e.g. a newly added
// enum value not yet placed). It must still appear in text and markdown output.
func TestRenderFeaturesUnorderedTypeStillRenders(t *testing.T) {
const unplaced ResultType = "experimental_module"
env := NewEnvelope(Query{Text: "openserp"}, "req-1", time.Unix(0, 0), []string{"google"})
env.SerpFeatures = append(env.SerpFeatures, SerpFeature{
Type: unplaced,
Text: "experimental feature body",
})
text := string(RenderText(env))
if !strings.Contains(text, "experimental feature body") {
t.Fatalf("text output dropped an unordered feature type:\n%s", text)
}
markdown := string(RenderMarkdown(env))
if !strings.Contains(markdown, "experimental feature body") {
t.Fatalf("markdown output dropped an unordered feature type:\n%s", markdown)
}
}