package dinners_test import ( "database/sql" "encoding/json" "errors" "strings" "testing" "code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/dinners" ) // TestCreateRegularNeverNamesFromLink: a Regular added with a recipe link // is named only by the household, so no part of the link — host, path, // query or fragment — is stored as its name or shown in a dinner or a // duplicate error, even a token of plain letters in the path. func TestCreateRegularNeverNamesFromLink(t *testing.T) { t.Parallel() e := newUnstartedEnv(t) const link = "https://c.example/stew-key-abcdefghijklmno?sig=zyxwvut#lookup" // Each leak holds a character no ULID has (Crockford base32 has no I, L, // O, U, "-", "." or "/"), so a dinner's id never matches one. leaks := []string{link, "c.example", "stew-key", "abcdefghijklmno", "sig", "zyxwvut", "lookup"} check := func(what, text string) { t.Helper() lower := strings.ToLower(text) for _, leak := range leaks { if strings.Contains(lower, leak) { t.Errorf("%s contains %q: %s", what, leak, text) } } } _, err := e.svc.CreateRegular(t.Context(), dinners.NewRegular{RecipeURL: ptr(link)}, member) var verr *dinners.ValidationError if !errors.As(err, &verr) { t.Fatalf("link without a name: err = %v, want a *ValidationError", err) } check("validation error", verr.Error()) c, err := e.svc.CreateRegular(t.Context(), dinners.NewRegular{Name: ptr(" Sunday stew "), RecipeURL: ptr(link)}, member) if err != nil { t.Fatal(err) } if c.Name != "Sunday stew" { t.Errorf("name = %q, want the household's %q", c.Name, "Sunday stew") } created, err := json.Marshal(c) if err != nil { t.Fatal(err) } check("create response", string(created)) row := e.dinner(t, c.ID) check("stored name", row.Name+" "+row.NormalizedName) if row.RecipeUrl.String != link { t.Errorf("stored recipe_url = %q, want %q kept for grounding", row.RecipeUrl.String, link) } list, err := e.svc.List(t.Context(), dinners.StatusRegular) if err != nil { t.Fatal(err) } listed, err := json.Marshal(list) if err != nil { t.Fatal(err) } check("list response", string(listed)) // The same link under another name is still a duplicate, and its error // names the existing dinner by the household's name. _, err = e.svc.CreateRegular(t.Context(), dinners.NewRegular{Name: ptr("Weeknight chili"), RecipeURL: ptr(link)}, member) var dup *dinners.DuplicateError if !errors.As(err, &dup) || dup.Existing.ID != c.ID { t.Fatalf("same link again: err = %v, want a duplicate of %s", err, c.ID) } check("duplicate error", dup.Error()) } // TestPageSiteNameWithholdsHosts: a stored site name that could be a host — // the link's own, in any case and without "www.", an IP address or a dotted // host name — is withheld; a name the page gave, dotless or with spaces, is // kept. func TestPageSiteNameWithholdsHosts(t *testing.T) { t.Parallel() const link = "https://www.TokenHost/recipes/stew" for _, tc := range []struct { stored string valid bool want *string }{ {"", false, nil}, {"tokenhost", true, nil}, {"TOKENHOST", true, nil}, {"private-token.example.com", true, nil}, {"Food.com", true, nil}, {"203.0.113.7", true, nil}, {"2001:db8::1", true, nil}, {"Delish", true, ptr("Delish")}, {"Bon Appétit", true, ptr("Bon Appétit")}, {"Mr. Food Test Kitchen", true, ptr("Mr. Food Test Kitchen")}, } { got, err := dinners.PageSiteName(sql.NullString{String: tc.stored, Valid: tc.valid}, link) if err != nil { t.Fatalf("PageSiteName(%q): %v", tc.stored, err) } if (got == nil) != (tc.want == nil) || (got != nil && *got != *tc.want) { t.Errorf("PageSiteName(%q) = %v, want %v", tc.stored, deref(got), deref(tc.want)) } } if _, err := dinners.PageSiteName(sql.NullString{String: "Delish", Valid: true}, "https://secret-token\x7f.example/%zz"); err == nil || strings.Contains(err.Error(), "secret-token") { t.Errorf("PageSiteName of an unparseable link: err = %v, want one not quoting the link", err) } } func deref(s *string) any { if s == nil { return nil } return *s }