ai cooking
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

check recieved (#238)

authored by

Paul Miller and committed by
GitHub
4be2a201 e93fa697

+162
+9
cmd/careme/mail.go
··· 16 16 "errors" 17 17 "fmt" 18 18 "log/slog" 19 + "net/http" 19 20 "os" 20 21 "time" 21 22 ··· 189 190 response, err := m.client.Send(message) 190 191 if err != nil { 191 192 slog.ErrorContext(ctx, "mail error", "error", err.Error(), "user", user.Email[0]) 193 + return 194 + } 195 + if response == nil { 196 + slog.ErrorContext(ctx, "mail error", "error", "nil sendgrid response", "user", user.Email[0]) 197 + return 198 + } 199 + if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { 200 + slog.ErrorContext(ctx, "mail rejected by sendgrid", "status", response.StatusCode, "body", response.Body, "headers", response.Headers, "user", user.Email[0]) 192 201 return 193 202 } 194 203 slog.InfoContext(ctx, "status", slog.Int("status", response.StatusCode), "body", response.Body, "headers", response.Headers)
+153
cmd/careme/web_e2e_test.go
··· 1 1 package main 2 2 3 3 import ( 4 + "context" 5 + "encoding/json" 4 6 "io" 5 7 "net/http" 6 8 "net/http/httptest" ··· 11 13 "testing" 12 14 "time" 13 15 16 + "careme/internal/ai" 14 17 "careme/internal/auth" 15 18 "careme/internal/cache" 16 19 "careme/internal/config" ··· 18 21 "careme/internal/recipes" 19 22 "careme/internal/templates" 20 23 "careme/internal/users" 24 + utypes "careme/internal/users/types" 21 25 26 + "github.com/sendgrid/rest" 27 + sgmail "github.com/sendgrid/sendgrid-go/helpers/mail" 22 28 "golang.org/x/net/html" 23 29 ) 24 30 ··· 111 117 112 118 //TODO step 6 make sure recipes are saved to user page? 113 119 120 + } 121 + 122 + type fakeMailCache struct { 123 + shoppingListJSON string 124 + data map[string]string 125 + } 126 + 127 + func newFakeMailCache(t *testing.T) *fakeMailCache { 128 + t.Helper() 129 + listJSON, err := json.Marshal(ai.ShoppingList{ 130 + Recipes: []ai.Recipe{ 131 + {Title: "Test Recipe"}, 132 + }, 133 + }) 134 + if err != nil { 135 + t.Fatalf("failed to marshal shopping list: %v", err) 136 + } 137 + return &fakeMailCache{ 138 + shoppingListJSON: string(listJSON), 139 + data: map[string]string{}, 140 + } 141 + } 142 + 143 + func (c *fakeMailCache) Get(_ context.Context, key string) (io.ReadCloser, error) { 144 + if strings.HasPrefix(key, "shoppinglist/") { 145 + return io.NopCloser(strings.NewReader(c.shoppingListJSON)), nil 146 + } 147 + value, ok := c.data[key] 148 + if !ok { 149 + return nil, cache.ErrNotFound 150 + } 151 + return io.NopCloser(strings.NewReader(value)), nil 152 + } 153 + 154 + func (c *fakeMailCache) Exists(_ context.Context, key string) (bool, error) { 155 + _, ok := c.data[key] 156 + return ok, nil 157 + } 158 + 159 + func (c *fakeMailCache) Put(_ context.Context, key, value string, opts cache.PutOptions) error { 160 + if opts.Condition == cache.PutIfNoneMatch { 161 + if _, exists := c.data[key]; exists { 162 + return cache.ErrAlreadyExists 163 + } 164 + } 165 + c.data[key] = value 166 + return nil 167 + } 168 + 169 + type fakeMailLocServer struct { 170 + location *locations.Location 171 + } 172 + 173 + func (f *fakeMailLocServer) GetLocationByID(_ context.Context, _ string) (*locations.Location, error) { 174 + return f.location, nil 175 + } 176 + 177 + type fakeMailClient struct { 178 + response *rest.Response 179 + err error 180 + } 181 + 182 + func (f *fakeMailClient) Send(_ *sgmail.SGMailV3) (*rest.Response, error) { 183 + return f.response, f.err 184 + } 185 + 186 + func TestSendEmail_DoesNotRecordSentClaimOnNonSuccessSendGridStatus(t *testing.T) { 187 + if err := templates.Init(&config.Config{}, "/assets/tailwind.css"); err != nil { 188 + t.Fatalf("failed to initialize templates: %v", err) 189 + } 190 + 191 + fc := newFakeMailCache(t) 192 + m := &mailer{ 193 + cache: fc, 194 + locServer: &fakeMailLocServer{ 195 + location: &locations.Location{ID: "123", Name: "Test Store", Address: "123 Test St"}, 196 + }, 197 + client: &fakeMailClient{ 198 + response: &rest.Response{StatusCode: 500, Body: "sendgrid internal error"}, 199 + }, 200 + } 201 + 202 + m.sendEmail(context.Background(), utypes.User{ 203 + ID: "user-1", 204 + MailOptIn: true, 205 + Email: []string{"u1@example.com"}, 206 + FavoriteStore: "123", 207 + }) 208 + 209 + for key := range fc.data { 210 + if strings.HasPrefix(key, mailSentPrefix) { 211 + t.Fatalf("did not expect sent claim to be recorded for non-success status; got key %q", key) 212 + } 213 + } 214 + } 215 + 216 + func TestSendEmail_RecordsSentClaimOnSuccessSendGridStatus(t *testing.T) { 217 + if err := templates.Init(&config.Config{}, "/assets/tailwind.css"); err != nil { 218 + t.Fatalf("failed to initialize templates: %v", err) 219 + } 220 + 221 + fc := newFakeMailCache(t) 222 + m := &mailer{ 223 + cache: fc, 224 + locServer: &fakeMailLocServer{ 225 + location: &locations.Location{ID: "123", Name: "Test Store", Address: "123 Test St"}, 226 + }, 227 + client: &fakeMailClient{ 228 + response: &rest.Response{StatusCode: 202, Body: "accepted"}, 229 + }, 230 + } 231 + 232 + m.sendEmail(context.Background(), utypes.User{ 233 + ID: "user-1", 234 + MailOptIn: true, 235 + Email: []string{"u1@example.com"}, 236 + FavoriteStore: "123", 237 + }) 238 + 239 + var ( 240 + foundKey string 241 + claimValue string 242 + ) 243 + for key, value := range fc.data { 244 + if strings.HasPrefix(key, mailSentPrefix) { 245 + foundKey = key 246 + claimValue = value 247 + break 248 + } 249 + } 250 + if foundKey == "" { 251 + t.Fatalf("expected sent claim to be recorded for successful status") 252 + } 253 + if !strings.HasSuffix(foundKey, "/user-1") { 254 + t.Fatalf("expected sent claim key to end with /user-1, got %q", foundKey) 255 + } 256 + 257 + var claim mailSentClaim 258 + if err := json.Unmarshal([]byte(claimValue), &claim); err != nil { 259 + t.Fatalf("failed to decode sent claim: %v", err) 260 + } 261 + if claim.UserID != "user-1" { 262 + t.Fatalf("expected claim user id user-1, got %q", claim.UserID) 263 + } 264 + if claim.ParamsHash == "" { 265 + t.Fatalf("expected claim params hash to be set") 266 + } 114 267 } 115 268 116 269 func newTestServer(t *testing.T) *httptest.Server {