1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: LicenseRef-MutuaL-1.2
4
5package cooked
6
7import (
8 "context"
9 "net/http"
10 "strings"
11 "testing"
12)
13
14func TestListRecipesACIDAPIClientResponses1AcceptsEmptyRecipesList(t *testing.T) {
15 client, closeServer := newTestClient(t, rawRecipeListTestHandler(
16 t,
17 "/api/user/returned-user/recipes",
18 `[]`,
19 ))
20 defer closeServer()
21
22 recipes, err := client.ListRecipes(context.Background(), 1, 10)
23 if err != nil {
24 t.Fatalf("ListRecipes() error = %v", err)
25 }
26 if len(recipes) != 0 {
27 t.Fatalf("recipes length = %d, want 0", len(recipes))
28 }
29}
30
31func TestListRecipesACIDAPIClientResponses1RejectsNullRecipesList(t *testing.T) {
32 client, closeServer := newTestClient(t, rawRecipeListTestHandler(
33 t,
34 "/api/user/returned-user/recipes",
35 `null`,
36 ))
37 defer closeServer()
38
39 _, err := client.ListRecipes(context.Background(), 1, 10)
40 if err == nil {
41 t.Fatal("ListRecipes() error = nil, want missing recipes error")
42 }
43}
44
45func TestSearchRecipesACIDAPIClientResponses1RejectsRecipeCardsWithoutIDOrTitle(t *testing.T) {
46 tests := []struct {
47 name string
48 body string
49 }{
50 {name: "missing id", body: `[{"title":"Pasta"}]`},
51 {name: "blank title", body: `[{"id":"recipe-1","title":" "}]`},
52 }
53
54 for _, tt := range tests {
55 t.Run(tt.name, func(t *testing.T) {
56 client, closeServer := newTestClient(t, rawRecipeListTestHandler(
57 t,
58 "/api/user/returned-user/recipes/search",
59 tt.body,
60 ))
61 defer closeServer()
62
63 _, err := client.SearchRecipes(context.Background(), "pasta", 1)
64 if err == nil {
65 t.Fatal("SearchRecipes() error = nil, want invalid recipe card error")
66 }
67 })
68 }
69}
70
71// api-client.RESPONSES.1 recipes.SAVE.7 recipes.SAVE.9
72func TestSaveRecipeDraftACIDAPIClientResponses1RejectsMissingRecipeID(t *testing.T) {
73 var saveCalls int
74 client, closeServer := newTestClient(t, rawSaveRecipeDraftTestHandler(t, &saveCalls, `{}`))
75 defer closeServer()
76
77 _, err := client.SaveRecipeDraft(
78 context.Background(),
79 "draft/with space",
80 "# Pasta\n\n1. Boil pasta.",
81 4,
82 )
83 if err == nil {
84 t.Fatal("SaveRecipeDraft() error = nil, want missing recipe ID error")
85 }
86 if !strings.Contains(err.Error(), "missing recipe ID") {
87 t.Fatalf("SaveRecipeDraft() error = %v, want missing recipe ID error", err)
88 }
89 if saveCalls != 1 {
90 t.Fatalf("save calls = %d, want 1", saveCalls)
91 }
92}
93
94// api-client.RESPONSES.1 recipes.SAVE.2 recipes.SAVE.9
95func TestSavePreparedRecipeACIDAPIClientResponses1RejectsMissingOrBlankRecipeID(t *testing.T) {
96 tests := []struct {
97 name string
98 body string
99 }{
100 {name: "missing recipe ID", body: `{}`},
101 {name: "blank recipe ID", body: `{"recipe-id":" "}`},
102 }
103
104 for _, tt := range tests {
105 t.Run(tt.name, func(t *testing.T) {
106 var saveCalls int
107 client, closeServer := newTestClient(t, rawSavePreparedRecipeTestHandler(t, &saveCalls, tt.body))
108 defer closeServer()
109
110 _, err := client.SavePreparedRecipe(
111 context.Background(),
112 "Pasta",
113 "# Pasta\n\n1. Boil pasta.",
114 2,
115 )
116 if err == nil {
117 t.Fatal("SavePreparedRecipe() error = nil, want missing recipe ID error")
118 }
119 if !strings.Contains(err.Error(), "missing recipe ID") {
120 t.Fatalf("SavePreparedRecipe() error = %v, want missing recipe ID error", err)
121 }
122 if saveCalls != 1 {
123 t.Fatalf("save calls = %d, want 1", saveCalls)
124 }
125 })
126 }
127}
128
129func rawRecipeListTestHandler(t *testing.T, path, body string) http.HandlerFunc {
130 t.Helper()
131
132 return func(w http.ResponseWriter, r *http.Request) {
133 switch r.URL.Path {
134 case "/api/public/login":
135 writeLoginResponse(t, w)
136 case path:
137 w.Header().Set("Content-Type", "application/json")
138 if _, err := w.Write([]byte(body)); err != nil {
139 t.Fatalf("write raw recipe list response: %v", err)
140 }
141 default:
142 t.Fatalf("unexpected path %s", r.URL.Path)
143 }
144 }
145}
146
147func rawSavePreparedRecipeTestHandler(t *testing.T, saveCalls *int, body string) http.HandlerFunc {
148 t.Helper()
149
150 return func(w http.ResponseWriter, r *http.Request) {
151 switch r.URL.Path {
152 case "/api/public/login":
153 writeLoginResponse(t, w)
154 case "/api/recipe/import/save":
155 requireMethod(t, r, http.MethodPost, "save prepared recipe")
156 requireSessionCookie(t, r)
157 requireSavePreparedRecipeRequest(t, r)
158 (*saveCalls)++
159 w.Header().Set("Content-Type", "application/json")
160 if _, err := w.Write([]byte(body)); err != nil {
161 t.Fatalf("write raw save prepared response: %v", err)
162 }
163 default:
164 t.Fatalf("unexpected path %s", r.URL.Path)
165 }
166 }
167}
168
169func rawSaveRecipeDraftTestHandler(t *testing.T, saveCalls *int, body string) http.HandlerFunc {
170 t.Helper()
171
172 return func(w http.ResponseWriter, r *http.Request) {
173 switch r.URL.EscapedPath() {
174 case "/api/public/login":
175 writeLoginResponse(t, w)
176 case "/api/extract/draft%2Fwith%20space/save":
177 requireMethod(t, r, http.MethodPost, "save recipe draft")
178 requireSessionCookie(t, r)
179 requireRecipeContentRequest(t, r, "draft save")
180 (*saveCalls)++
181 w.Header().Set("Content-Type", "application/json")
182 if _, err := w.Write([]byte(body)); err != nil {
183 t.Fatalf("write raw save draft response: %v", err)
184 }
185 default:
186 t.Fatalf("unexpected path %s", r.URL.EscapedPath())
187 }
188 }
189}