client_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	"encoding/json"
 10	"net/http"
 11	"net/http/httptest"
 12	"net/url"
 13	"testing"
 14)
 15
 16func TestReadShoppingListACIDAuthenticationLogin3StoresCookies(t *testing.T) {
 17	var sawSessionCookie bool
 18
 19	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 20		switch r.URL.Path {
 21		case "/api/public/login":
 22			if r.Method != http.MethodPost {
 23				t.Fatalf("login method = %s, want POST", r.Method)
 24			}
 25
 26			var request loginRequest
 27			if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
 28				t.Fatalf("decode login request: %v", err)
 29			}
 30			if request.Username != "configured-user" || request.Password != "configured-password" {
 31				t.Fatalf("login request = %#v, want configured credentials", request)
 32			}
 33
 34			http.SetCookie(w, &http.Cookie{Name: "cooked_session", Value: "session-value", Path: "/"})
 35			writeJSON(t, w, loginResponse{Username: "returned-user"})
 36		case "/api/user/returned-user/shopping-list":
 37			cookie, err := r.Cookie("cooked_session")
 38			if err != nil {
 39				t.Fatalf("missing session cookie: %v", err)
 40			}
 41			if cookie.Value != "session-value" {
 42				t.Fatalf("session cookie = %q, want session-value", cookie.Value)
 43			}
 44			sawSessionCookie = true
 45			writeJSON(t, w, shoppingListResponse{
 46				ShoppingList: ShoppingList{
 47					Aisles: []Aisle{{
 48						ID:   "pantry",
 49						Name: "Pantry",
 50						ProductGroups: []ProductGroup{{
 51							ID:       "pasta",
 52							Name:     "Pasta",
 53							Quantity: "200g",
 54						}},
 55					}},
 56				},
 57				Recipes: []string{"recipe-id"},
 58			})
 59		default:
 60			t.Fatalf("unexpected path %s", r.URL.Path)
 61		}
 62	}))
 63	defer server.Close()
 64
 65	baseURL, err := url.Parse(server.URL)
 66	if err != nil {
 67		t.Fatalf("parse server URL: %v", err)
 68	}
 69	client, err := NewClient(baseURL, "configured-user", "configured-password")
 70	if err != nil {
 71		t.Fatalf("NewClient() error = %v", err)
 72	}
 73
 74	shoppingList, err := client.ReadShoppingList(context.Background())
 75	if err != nil {
 76		t.Fatalf("ReadShoppingList() error = %v", err)
 77	}
 78
 79	if !sawSessionCookie {
 80		t.Fatal("shopping-list request did not receive session cookie")
 81	}
 82	if len(shoppingList.Aisles) != 1 || shoppingList.Aisles[0].ProductGroups[0].Name != "Pasta" {
 83		t.Fatalf("shopping list = %#v, want one Pasta item", shoppingList)
 84	}
 85}
 86
 87func TestUserPathEscapesAuthenticatedUsername(t *testing.T) {
 88	client := &Client{authenticatedUsername: "user/name"}
 89
 90	got := client.userPath("/api/user/{username}/shopping-list")
 91	if got != "/api/user/user%2Fname/shopping-list" {
 92		t.Fatalf("userPath() = %q, want escaped username", got)
 93	}
 94}
 95
 96func writeJSON[T loginResponse | shoppingListResponse](t *testing.T, w http.ResponseWriter, value T) {
 97	t.Helper()
 98
 99	w.Header().Set("Content-Type", "application/json")
100	if err := json.NewEncoder(w).Encode(value); err != nil {
101		t.Fatalf("write JSON: %v", err)
102	}
103}