client_shopping_list_validation_test.go

  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}