diff --git a/internal/cooked/client.go b/internal/cooked/client.go index f0f3cd3fdc23ddbd29fab156a7690bbf8b6c27bd..6b8cdc6f395f5baee108791d9d5f4c0dd6d7e38c 100644 --- a/internal/cooked/client.go +++ b/internal/cooked/client.go @@ -339,6 +339,9 @@ func (c *Client) SaveRecipeDraft(ctx context.Context, draftID, markdown string, if err := c.doAuthenticated(ctx, http.MethodPost, path, body, decodeSave); err != nil { return "", err } + if strings.TrimSpace(response.RecipeID) == "" { + return "", fmt.Errorf("save recipe draft response missing recipe ID") + } return response.RecipeID, nil } diff --git a/internal/cooked/client_recipe_validation_test.go b/internal/cooked/client_recipe_validation_test.go index 99025c6d7097c1b7afbcc215562fb0253bdcb7e1..444d06fafc816a57a8d51bbc7d2bc0a5b5edafa3 100644 --- a/internal/cooked/client_recipe_validation_test.go +++ b/internal/cooked/client_recipe_validation_test.go @@ -7,6 +7,7 @@ package cooked import ( "context" "net/http" + "strings" "testing" ) @@ -67,6 +68,29 @@ func TestSearchRecipesACIDAPIClientResponses1RejectsRecipeCardsWithoutIDOrTitle( } } +// api-client.RESPONSES.1 recipes.SAVE.7 recipes.SAVE.9 +func TestSaveRecipeDraftACIDAPIClientResponses1RejectsMissingRecipeID(t *testing.T) { + var saveCalls int + client, closeServer := newTestClient(t, rawSaveRecipeDraftTestHandler(t, &saveCalls, `{}`)) + defer closeServer() + + _, err := client.SaveRecipeDraft( + context.Background(), + "draft/with space", + "# Pasta\n\n1. Boil pasta.", + 4, + ) + if err == nil { + t.Fatal("SaveRecipeDraft() error = nil, want missing recipe ID error") + } + if !strings.Contains(err.Error(), "missing recipe ID") { + t.Fatalf("SaveRecipeDraft() error = %v, want missing recipe ID error", err) + } + if saveCalls != 1 { + t.Fatalf("save calls = %d, want 1", saveCalls) + } +} + func rawRecipeListTestHandler(t *testing.T, path, body string) http.HandlerFunc { t.Helper() @@ -84,3 +108,25 @@ func rawRecipeListTestHandler(t *testing.T, path, body string) http.HandlerFunc } } } + +func rawSaveRecipeDraftTestHandler(t *testing.T, saveCalls *int, body string) http.HandlerFunc { + t.Helper() + + return func(w http.ResponseWriter, r *http.Request) { + switch r.URL.EscapedPath() { + case "/api/public/login": + writeLoginResponse(t, w) + case "/api/extract/draft%2Fwith%20space/save": + requireMethod(t, r, http.MethodPost, "save recipe draft") + requireSessionCookie(t, r) + requireRecipeContentRequest(t, r, "draft save") + (*saveCalls)++ + w.Header().Set("Content-Type", "application/json") + if _, err := w.Write([]byte(body)); err != nil { + t.Fatalf("write raw save draft response: %v", err) + } + default: + t.Fatalf("unexpected path %s", r.URL.EscapedPath()) + } + } +}