package feeds import ( "encoding/json" "testing" "github.com/google/uuid" ) func TestDetectMappedChangesPriceStock(t *testing.T) { t.Parallel() old := []byte(`{"price":"10","stock":"5","title":"A"}`) changes := detectMappedChanges(old, map[string]any{"price": "12", "stock": "5", "title": "A"}) if len(changes) != 1 || changes[0] != "price" { t.Fatalf("changes=%v", changes) } changes = detectMappedChanges(old, map[string]any{"price": "10", "stock": "0", "title": "A"}) if len(changes) != 1 || changes[0] != "stock" { t.Fatalf("stock changes=%v", changes) } changes = detectMappedChanges(old, map[string]any{"price": "11", "stock": "1", "title": "B", "availability": "out"}) want := map[string]bool{"price": true, "stock": true, "title": true, "availability": true} for _, c := range changes { if !want[c] { t.Fatalf("unexpected %s in %v", c, changes) } delete(want, c) } if len(want) != 0 { t.Fatalf("missing %v from %v", want, changes) } } func TestClassifyUpsertOpsStampsSyncChanges(t *testing.T) { t.Parallel() idUpdate := uuid.MustParse("22222222-2222-2222-2222-222222222222") existing := map[string]existingProduct{ "update": {ID: idUpdate, MappedData: []byte(`{"price":"10","title":"Old"}`)}, } chunk := []pendingProduct{ {GTIN: "new", RawData: map[string]string{"EAN": "new"}, MappedData: map[string]any{"title": "N"}}, {GTIN: "update", RawData: map[string]string{"EAN": "update"}, MappedData: map[string]any{"price": "12", "title": "Old"}}, } ops, skipped := classifyUpsertOps(chunk, existing) if skipped != 0 || len(ops) != 2 { t.Fatalf("ops=%d skipped=%d", len(ops), skipped) } if ops[0].kind != upsertOpInsert || len(ops[0].changes) != 1 || ops[0].changes[0] != "new" { t.Fatalf("insert op=%+v", ops[0]) } var mapped map[string]any if err := json.Unmarshal(ops[0].mappedJSON, &mapped); err != nil { t.Fatal(err) } tags, _ := mapped[syncChangesMappedKey].([]any) if len(tags) != 1 || tags[0] != "new" { t.Fatalf("insert mapped meta=%v", mapped[syncChangesMappedKey]) } if ops[1].kind != upsertOpUpdate || len(ops[1].changes) != 1 || ops[1].changes[0] != "price" { t.Fatalf("update op=%+v", ops[1]) } } func TestSyncDeltaCountsAddChanges(t *testing.T) { t.Parallel() var d syncDeltaCounts d.addChanges([]string{"price", "stock"}) d.addChanges([]string{"new"}) d.addChanges(nil) if d.PriceChanged != 1 || d.StockChanged != 1 || d.New != 1 || d.OtherChanged != 1 { t.Fatalf("%+v", d) } } func TestPreserveSyncedExtrasKeepsCategory(t *testing.T) { t.Parallel() existing := []byte(`{"price":"10","category":"46","title":"Roborock"}`) mapped := map[string]any{"price": "12", "title": "Roborock"} got := preserveSyncedExtras(existing, mapped) if got["category"] != "46" { t.Fatalf("category=%v want 46", got["category"]) } if got["price"] != "12" { t.Fatalf("price=%v want 12", got["price"]) } // Explicit new category wins. mapped2 := map[string]any{"price": "12", "category": "120"} got2 := preserveSyncedExtras(existing, mapped2) if got2["category"] != "120" { t.Fatalf("category=%v want 120", got2["category"]) } // Empty / none prior must not invent a category. got3 := preserveSyncedExtras([]byte(`{"category":"none"}`), map[string]any{"title": "X"}) if _, ok := got3["category"]; ok { t.Fatalf("unexpected category=%v", got3["category"]) } } func TestClassifyUpsertOpsPreservesCategory(t *testing.T) { t.Parallel() idUpdate := uuid.MustParse("33333333-3333-3333-3333-333333333333") existing := map[string]existingProduct{ "keep": {ID: idUpdate, MappedData: []byte(`{"price":"10","category":"46","title":"Old"}`)}, } chunk := []pendingProduct{ {GTIN: "keep", RawData: map[string]string{"EAN": "keep"}, MappedData: map[string]any{"price": "12", "title": "Old"}}, } ops, skipped := classifyUpsertOps(chunk, existing) if skipped != 0 || len(ops) != 1 { t.Fatalf("ops=%d skipped=%d", len(ops), skipped) } if ops[0].kind != upsertOpUpdate { t.Fatalf("kind=%v want update", ops[0].kind) } var mapped map[string]any if err := json.Unmarshal(ops[0].mappedJSON, &mapped); err != nil { t.Fatal(err) } if mapped["category"] != "46" { t.Fatalf("category wiped: %v", mapped["category"]) } if mapped["price"] != "12" { t.Fatalf("price=%v", mapped["price"]) } }