@@ -363,10 +363,30 @@ func (c *Client) getRecipes(ctx context.Context, path string) ([]RecipeCard, err
if err := c.doAuthenticated(ctx, http.MethodGet, path, nil, decodeRecipes); err != nil {
return nil, err
}
+ if err := validateRecipeCards(response.Recipes); err != nil {
+ return nil, err
+ }
return response.Recipes, nil
}
+func validateRecipeCards(recipes []RecipeCard) error {
+ if recipes == nil {
+ return fmt.Errorf("cooked recipe list response missing recipes")
+ }
+
+ for index, recipe := range recipes {
+ if strings.TrimSpace(recipe.ID) == "" {
+ return fmt.Errorf("cooked recipe list response recipe %d missing id", index)
+ }
+ if strings.TrimSpace(recipe.Title) == "" {
+ return fmt.Errorf("cooked recipe list response recipe %d missing title", index)
+ }
+ }
+
+ return nil
+}
+
func (c *Client) doAuthenticated(
ctx context.Context,
method, path string,
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: LicenseRef-MutuaL-1.2
+
+package cooked
+
+import (
+ "context"
+ "net/http"
+ "testing"
+)
+
+func TestListRecipesACIDAPIClientResponses1AcceptsEmptyRecipesList(t *testing.T) {
+ client, closeServer := newTestClient(t, rawRecipeListTestHandler(
+ t,
+ "/api/user/returned-user/recipes",
+ "{\"recipes\":[]}",
+ ))
+ defer closeServer()
+
+ recipes, err := client.ListRecipes(context.Background(), 1, 10)
+ if err != nil {
+ t.Fatalf("ListRecipes() error = %v", err)
+ }
+ if len(recipes) != 0 {
+ t.Fatalf("recipes length = %d, want 0", len(recipes))
+ }
+}
+
+func TestListRecipesACIDAPIClientResponses1RejectsMissingRecipesField(t *testing.T) {
+ client, closeServer := newTestClient(t, rawRecipeListTestHandler(
+ t,
+ "/api/user/returned-user/recipes",
+ `{}`,
+ ))
+ defer closeServer()
+
+ _, err := client.ListRecipes(context.Background(), 1, 10)
+ if err == nil {
+ t.Fatal("ListRecipes() error = nil, want missing recipes error")
+ }
+}
+
+func TestSearchRecipesACIDAPIClientResponses1RejectsRecipeCardsWithoutIDOrTitle(t *testing.T) {
+ tests := []struct {
+ name string
+ body string
+ }{
+ {name: "missing id", body: "{\"recipes\":[{\"title\":\"Pasta\"}]}"},
+ {name: "blank title", body: "{\"recipes\":[{\"id\":\"recipe-1\",\"title\":\" \"}]}"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ client, closeServer := newTestClient(t, rawRecipeListTestHandler(
+ t,
+ "/api/user/returned-user/recipes/search",
+ tt.body,
+ ))
+ defer closeServer()
+
+ _, err := client.SearchRecipes(context.Background(), "pasta", 1)
+ if err == nil {
+ t.Fatal("SearchRecipes() error = nil, want invalid recipe card error")
+ }
+ })
+ }
+}
+
+func rawRecipeListTestHandler(t *testing.T, path, body string) http.HandlerFunc {
+ t.Helper()
+
+ return func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.Path {
+ case "/api/public/login":
+ writeLoginResponse(t, w)
+ case path:
+ w.Header().Set("Content-Type", "application/json")
+ if _, err := w.Write([]byte(body)); err != nil {
+ t.Fatalf("write raw recipe list response: %v", err)
+ }
+ default:
+ t.Fatalf("unexpected path %s", r.URL.Path)
+ }
+ }
+}