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
14// api-client.RESPONSES.1 shopping-list.READ.1
15func TestReadShoppingListACIDAPIClientResponses1AcceptsEmptyShoppingList(t *testing.T) {
16 var readCalls int
17 client, closeServer := newTestClient(t, rawShoppingListTestHandler(
18 t,
19 &readCalls,
20 `{"shopping-list":{"aisles":[]},"recipes":[]}`,
21 ))
22 defer closeServer()
23
24 shoppingList, err := client.ReadShoppingList(context.Background())
25 if err != nil {
26 t.Fatalf("ReadShoppingList() error = %v", err)
27 }
28 if readCalls != 1 {
29 t.Fatalf("read calls = %d, want 1", readCalls)
30 }
31 if len(shoppingList.Aisles) != 0 {
32 t.Fatalf("aisles length = %d, want 0", len(shoppingList.Aisles))
33 }
34}
35
36// api-client.RESPONSES.1 api-client.RESPONSES.3 shopping-list.READ.1 shopping-list.READ.2 shopping-list.READ.3 shopping-list.READ.4 shopping-list.READ.5 shopping-list.READ.8
37func TestReadShoppingListACIDAPIClientResponses1RejectsInvalidShoppingListShape(t *testing.T) {
38 tests := []struct {
39 name string
40 body string
41 wantErr string
42 }{
43 {name: "missing shopping-list", body: `{"recipes":[]}`, wantErr: "missing shopping-list"},
44 {name: "missing aisles", body: `{"shopping-list":{},"recipes":[]}`, wantErr: "missing aisles"},
45 {
46 name: "blank aisle id",
47 body: `{"shopping-list":{"aisles":[{"aisle-id":" ","aisle-name":"Pantry","product-groups":[]}]},"recipes":[]}`,
48 wantErr: "aisle 0 missing id",
49 },
50 {
51 name: "blank aisle name",
52 body: `{"shopping-list":{"aisles":[{"aisle-id":"pantry","aisle-name":" ","product-groups":[]}]},"recipes":[]}`,
53 wantErr: "aisle 0 missing name",
54 },
55 {
56 name: "missing product groups",
57 body: `{"shopping-list":{"aisles":[{"aisle-id":"pantry","aisle-name":"Pantry"}]},"recipes":[]}`,
58 wantErr: "aisle 0 missing product groups",
59 },
60 {
61 name: "blank product group id",
62 body: `{"shopping-list":{"aisles":[{"aisle-id":"pantry","aisle-name":"Pantry","product-groups":[{"id":" ","name":"Pasta","quantity":"200g","selected":false}]}]},"recipes":[]}`,
63 wantErr: "aisle 0 product group 0 missing id",
64 },
65 {
66 name: "blank product group name",
67 body: `{"shopping-list":{"aisles":[{"aisle-id":"pantry","aisle-name":"Pantry","product-groups":[{"id":"pasta","name":" ","quantity":"200g","selected":false}]}]},"recipes":[]}`,
68 wantErr: "aisle 0 product group 0 missing name",
69 },
70 }
71
72 for _, tt := range tests {
73 t.Run(tt.name, func(t *testing.T) {
74 var readCalls int
75 client, closeServer := newTestClient(t, rawShoppingListTestHandler(t, &readCalls, tt.body))
76 defer closeServer()
77
78 _, err := client.ReadShoppingList(context.Background())
79 if err == nil {
80 t.Fatal("ReadShoppingList() error = nil, want invalid shopping-list shape error")
81 }
82 if !strings.Contains(err.Error(), tt.wantErr) {
83 t.Fatalf("ReadShoppingList() error = %v, want %q", err, tt.wantErr)
84 }
85 if readCalls != 1 {
86 t.Fatalf("read calls = %d, want 1", readCalls)
87 }
88 })
89 }
90}
91
92func rawShoppingListTestHandler(t *testing.T, readCalls *int, body string) http.HandlerFunc {
93 t.Helper()
94
95 return func(w http.ResponseWriter, r *http.Request) {
96 switch r.URL.Path {
97 case "/api/public/login":
98 writeLoginResponse(t, w)
99 case "/api/user/returned-user/shopping-list":
100 requireMethod(t, r, http.MethodGet, "read shopping list")
101 requireSessionCookie(t, r)
102 (*readCalls)++
103 w.Header().Set("Content-Type", "application/json")
104 if _, err := w.Write([]byte(body)); err != nil {
105 t.Fatalf("write raw shopping-list response: %v", err)
106 }
107 default:
108 t.Fatalf("unexpected path %s", r.URL.Path)
109 }
110 }
111}