161 lines
4.1 KiB
Go
161 lines
4.1 KiB
Go
package seo
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestAnalyze_missingMetaAndImage(t *testing.T) {
|
||
|
|
products := []ProductInput{
|
||
|
|
{
|
||
|
|
ID: "p1",
|
||
|
|
Name: "Acme UltraWidget Pro 3000 Stainless",
|
||
|
|
Category: "Widgets",
|
||
|
|
ProcessedAttrs: map[string]any{"brand": "Acme"},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
recs := Analyze(products, nil)
|
||
|
|
types := map[string]bool{}
|
||
|
|
for _, r := range recs {
|
||
|
|
types[r.Type] = true
|
||
|
|
if r.EntityID != "p1" && r.Type != TypeDuplicateTitle {
|
||
|
|
t.Fatalf("unexpected entity %s", r.EntityID)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for _, want := range []string{TypeMissingMetaTitle, TypeMissingMetaDescription, TypeMissingImage} {
|
||
|
|
if !types[want] {
|
||
|
|
t.Fatalf("expected type %s", want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAnalyze_duplicateAndThin(t *testing.T) {
|
||
|
|
products := []ProductInput{
|
||
|
|
{ID: "a", Name: "Short", MetaTitle: "x", MetaDescription: "y"},
|
||
|
|
{ID: "b", Name: "Short", MetaTitle: "x", MetaDescription: "y"},
|
||
|
|
}
|
||
|
|
recs := Analyze(products, nil)
|
||
|
|
var thin, dup int
|
||
|
|
for _, r := range recs {
|
||
|
|
switch r.Type {
|
||
|
|
case TypeThinTitle:
|
||
|
|
thin++
|
||
|
|
case TypeDuplicateTitle:
|
||
|
|
dup++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if thin < 2 {
|
||
|
|
t.Fatalf("expected thin titles, got %d", thin)
|
||
|
|
}
|
||
|
|
if dup < 2 {
|
||
|
|
t.Fatalf("expected duplicate titles, got %d", dup)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAnalyze_duplicatePeerAndRecCaps(t *testing.T) {
|
||
|
|
n := maxDupRecsPerTitle + maxPeerIDsPerDup + 10
|
||
|
|
products := make([]ProductInput, 0, n)
|
||
|
|
for i := 0; i < n; i++ {
|
||
|
|
products = append(products, ProductInput{
|
||
|
|
ID: fmt.Sprintf("p%d", i),
|
||
|
|
Name: "Shared Title Product",
|
||
|
|
MetaTitle: "mt",
|
||
|
|
MetaDescription: "md",
|
||
|
|
})
|
||
|
|
}
|
||
|
|
recs := Analyze(products, nil)
|
||
|
|
var dup int
|
||
|
|
var peerLen int
|
||
|
|
for _, r := range recs {
|
||
|
|
if r.Type != TypeDuplicateTitle {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
dup++
|
||
|
|
peers, _ := r.Meta["peer_ids"].([]string)
|
||
|
|
if peerLen == 0 {
|
||
|
|
peerLen = len(peers)
|
||
|
|
}
|
||
|
|
if len(peers) > maxPeerIDsPerDup {
|
||
|
|
t.Fatalf("peer_ids len=%d want <=%d", len(peers), maxPeerIDsPerDup)
|
||
|
|
}
|
||
|
|
count, _ := r.Meta["duplicate_count"].(int)
|
||
|
|
if count != n {
|
||
|
|
t.Fatalf("duplicate_count=%d want %d", count, n)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if dup != maxDupRecsPerTitle {
|
||
|
|
t.Fatalf("duplicate recs=%d want %d", dup, maxDupRecsPerTitle)
|
||
|
|
}
|
||
|
|
if peerLen != maxPeerIDsPerDup {
|
||
|
|
t.Fatalf("peer_ids len=%d want %d", peerLen, maxPeerIDsPerDup)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAnalyze_categoryMeta(t *testing.T) {
|
||
|
|
cats := []CategoryInput{
|
||
|
|
{ID: "c1", UniqueID: "cat-1", Name: "AB", DescriptionTemplate: map[string]any{}},
|
||
|
|
{ID: "c2", UniqueID: "cat-2", Name: "Appliances", DescriptionTemplate: map[string]any{
|
||
|
|
"metaTitle": "t", "metaDescription": "d",
|
||
|
|
}},
|
||
|
|
}
|
||
|
|
recs := Analyze(nil, cats)
|
||
|
|
var missing, thin int
|
||
|
|
for _, r := range recs {
|
||
|
|
if r.Type == TypeCategoryMissingMeta {
|
||
|
|
missing++
|
||
|
|
if r.EntityID != "c1" {
|
||
|
|
t.Fatalf("wrong category for missing meta: %s", r.EntityID)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if r.Type == TypeThinCategoryName {
|
||
|
|
thin++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if missing != 1 {
|
||
|
|
t.Fatalf("expected 1 missing meta formula, got %d", missing)
|
||
|
|
}
|
||
|
|
if thin != 1 {
|
||
|
|
t.Fatalf("expected 1 thin category name, got %d", thin)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestFillMetaTemplate(t *testing.T) {
|
||
|
|
title, desc := FillMetaTemplate(ProductInput{
|
||
|
|
Name: "Drill 18V",
|
||
|
|
Category: "Tools",
|
||
|
|
Description: "Cordless drill with battery pack for DIY projects.",
|
||
|
|
Attributes: map[string]any{"brand": "Bosch"},
|
||
|
|
})
|
||
|
|
if title == "" || desc == "" {
|
||
|
|
t.Fatal("expected non-empty meta")
|
||
|
|
}
|
||
|
|
if runeLen(title) > MetaTitleMaxChars {
|
||
|
|
t.Fatalf("title too long: %d", runeLen(title))
|
||
|
|
}
|
||
|
|
if runeLen(desc) > MetaDescriptionMaxChars {
|
||
|
|
t.Fatalf("desc too long: %d", runeLen(desc))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestBuildChecklist_score(t *testing.T) {
|
||
|
|
products := []ProductInput{
|
||
|
|
{ID: "1", Name: "Good Product Title Here", MetaTitle: "mt", MetaDescription: "md",
|
||
|
|
MappedData: map[string]any{"image_url": "https://example.com/a.jpg"}, Category: "Good Product Title Here"},
|
||
|
|
}
|
||
|
|
recs := Analyze(products, nil)
|
||
|
|
checklist, overall := BuildChecklist(recs, 1, 0)
|
||
|
|
if overall <= 0 {
|
||
|
|
t.Fatalf("expected positive overall, got %v", overall)
|
||
|
|
}
|
||
|
|
if len(checklist) != len(AllTypes()) {
|
||
|
|
t.Fatalf("checklist len %d want %d", len(checklist), len(AllTypes()))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAllTypesStable(t *testing.T) {
|
||
|
|
types := AllTypes()
|
||
|
|
if len(types) != 8 {
|
||
|
|
t.Fatalf("want 8 types, got %d", len(types))
|
||
|
|
}
|
||
|
|
}
|