From 13044879f62b3d17a87275f460becc6c368e185a Mon Sep 17 00:00:00 2001 From: dev_agent Date: Tue, 6 Oct 2026 19:05:26 -0400 Subject: [PATCH] Drop a pending Regular's link when it is renamed A Regular added from a recipe link stores recipe_url, and its grounding job grounds that link. Renaming the dinner while still pending kept the link, so the job queued for the new name still attached the old link's recipe and photo. UpdateDinner now sets recipe_url, and renaming a pending dinner clears it: the fresh job grounds the new name by search, and the household can give a link by hand to fix up the recipe if they want a specific one. Co-Authored-By: Claude Opus 5.5 --- internal/db/db_test.go | 1 + internal/db/dinners.sql.go | 3 +++ internal/dinners/dinners.go | 11 +++++++-- internal/dinners/grounding_test.go | 38 ++++++++++++++++++++++++++++++ migrations/queries/dinners.sql | 1 + 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/internal/db/db_test.go b/internal/db/db_test.go index 2f5f911..5ec14a0 100644 --- a/internal/db/db_test.go +++ b/internal/db/db_test.go @@ -190,6 +190,7 @@ func TestDinners(t *testing.T) { Difficulty: str("medium"), Cuisine: sql.NullString{}, RecipeID: sql.NullString{}, + RecipeUrl: str("https://example.com/tacos"), PhotoID: str(photo.ID), GroundingStatus: "failed", UpdatedBy: str("sam@example.com"), diff --git a/internal/db/dinners.sql.go b/internal/db/dinners.sql.go index 049f318..725dadc 100644 --- a/internal/db/dinners.sql.go +++ b/internal/db/dinners.sql.go @@ -543,6 +543,7 @@ SET name = ?, difficulty = ?, cuisine = ?, recipe_id = ?, + recipe_url = ?, photo_id = ?, grounding_status = ?, updated_by = ?, @@ -561,6 +562,7 @@ type UpdateDinnerParams struct { Difficulty sql.NullString Cuisine sql.NullString RecipeID sql.NullString + RecipeUrl sql.NullString PhotoID sql.NullString GroundingStatus string UpdatedBy sql.NullString @@ -580,6 +582,7 @@ func (q *Queries) UpdateDinner(ctx context.Context, arg UpdateDinnerParams) (Din arg.Difficulty, arg.Cuisine, arg.RecipeID, + arg.RecipeUrl, arg.PhotoID, arg.GroundingStatus, arg.UpdatedBy, diff --git a/internal/dinners/dinners.go b/internal/dinners/dinners.go index 731136a..a336bd8 100644 --- a/internal/dinners/dinners.go +++ b/internal/dinners/dinners.go @@ -294,8 +294,9 @@ func (s *Service) List(ctx context.Context, status string) ([]Dinner, error) { // Update applies p to dinner id, recording updatedBy. An invalid patch is a // *ValidationError and a new name repeating another unarchived dinner a // *DuplicateError; either changes nothing. Renaming a dinner still pending -// enqueues a fresh KindGroundDinner job for the new name: the job already -// running was for the old name, and its result no longer applies. +// clears its recipe_url and enqueues a fresh KindGroundDinner job that +// grounds the new name: the job already running was for the old name or +// link, and its result no longer applies. func (s *Service) Update(ctx context.Context, id string, p Patch, updatedBy string) (Dinner, error) { if updatedBy == "" { return Dinner{}, errors.New("dinners: update: no member to record as updated_by") @@ -327,6 +328,7 @@ func (s *Service) Update(ctx context.Context, id string, p Patch, updatedBy stri Difficulty: cur.Difficulty, Cuisine: cur.Cuisine, RecipeID: cur.RecipeID, + RecipeUrl: cur.RecipeUrl, PhotoID: cur.PhotoID, GroundingStatus: cur.GroundingStatus, UpdatedBy: sql.NullString{String: updatedBy, Valid: true}, @@ -338,6 +340,11 @@ func (s *Service) Update(ctx context.Context, id string, p Patch, updatedBy stri } params.Name = name params.NormalizedName = harness.Normalize(name) + if cur.GroundingStatus == GroundingPending && params.NormalizedName != cur.NormalizedName { + // The link was the old dinner's recipe; the new name is + // grounded by name, and a link can be given by hand. + params.RecipeUrl = sql.NullString{} + } } if p.Method != nil { params.Method = sql.NullString{String: *p.Method, Valid: true} diff --git a/internal/dinners/grounding_test.go b/internal/dinners/grounding_test.go index 325082d..928afea 100644 --- a/internal/dinners/grounding_test.go +++ b/internal/dinners/grounding_test.go @@ -349,6 +349,44 @@ func TestRenameWhilePendingGroundsNewName(t *testing.T) { } } +// TestRenameLinkWhilePendingGroundsNewName: a Regular added from a link and +// renamed while still pending drops the link and grounds its new name, not +// the old link's recipe and photo. +func TestRenameLinkWhilePendingGroundsNewName(t *testing.T) { + t.Parallel() + e := newUnstartedEnv(t) + e.search.Results["Beef stew"] = []recipes.Candidate{{URL: stew}} + e.model.QueueGenerate(recipestest.NormalizedReply("oven", 150)) + + c, err := e.svc.CreateRegular(t.Context(), dinners.NewRegular{RecipeURL: ptr(curryA)}, member) + if err != nil { + t.Fatal(err) + } + d, err := e.svc.Update(t.Context(), c.ID, dinners.Patch{Name: ptr("Beef stew")}, member) + if err != nil { + t.Fatal(err) + } + if d.GroundingStatus != dinners.GroundingPending || d.RecipeURL != nil { + t.Fatalf("renamed = %+v, want still pending with no recipe_url", d) + } + e.start(t) + + row := e.waitGrounding(t, c.ID) + if row.GroundingStatus != dinners.GroundingGrounded { + t.Fatalf("dinner = %+v, want grounded for the new name", row) + } + rec, err := db.New(e.st.Reader).GetRecipe(t.Context(), row.RecipeID.String) + if err != nil { + t.Fatal(err) + } + if rec.SourceUrl != stew || rec.PhotoID != row.PhotoID || row.Method.String != "oven" || row.MinutesTotal.Int64 != 150 { + t.Errorf("dinner = %+v recipe %s, want grounded in %s, not the old link %s", row, rec.SourceUrl, stew, curryA) + } + if got := e.search.Queries(); !slices.Equal(got, []string{"Beef stew"}) { + t.Errorf("searched %q, want only the new name", got) + } +} + // TestRenameWhilePendingEnqueueFailure: a pending Regular renamed when the // job for its new name cannot be enqueued is failed, not left pending. func TestRenameWhilePendingEnqueueFailure(t *testing.T) { diff --git a/migrations/queries/dinners.sql b/migrations/queries/dinners.sql index 118f439..a3782d8 100644 --- a/migrations/queries/dinners.sql +++ b/migrations/queries/dinners.sql @@ -42,6 +42,7 @@ SET name = ?, difficulty = ?, cuisine = ?, recipe_id = ?, + recipe_url = ?, photo_id = ?, grounding_status = ?, updated_by = ?,