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 = ?,