//go:build integration package inttest import ( "context" "net/http" "strings" "testing" "github.com/google/uuid" "scrabble/backend/internal/payments" ) // benefitFor returns the account's benefit on the given origin from its statement. func benefitFor(t *testing.T, pay *payments.Service, id uuid.UUID, origin payments.Source) payments.OriginBenefit { t.Helper() stmt, err := pay.AccountStatement(context.Background(), id) if err != nil { t.Fatalf("statement: %v", err) } for _, b := range stmt.Benefits { if b.Origin == origin { return b } } return payments.OriginBenefit{} } // TestConsoleAdminGrant drives the admin grant: a raw benefit grant, a by-product grant of a reward // bundle, and a refusal to grant a chips pack; the create is CSRF-guarded. func TestConsoleAdminGrant(t *testing.T) { ctx := context.Background() srv, _, pay := bannerServer(t) h := srv.Handler() id := provisionAccount(t) const origin = "http://admin.test" base := "http://admin.test/_gm/users/" + id.String() // CSRF: a grant without the origin header is refused. if code, _ := consoleDo(h, http.MethodPost, base+"/grant", "origin=direct&hints=1", ""); code != http.StatusForbidden { t.Fatalf("grant without origin = %d, want 403", code) } // Raw grant: 5 hints + 30 no-ads days to the direct origin. if code, body := consoleDo(h, http.MethodPost, base+"/grant", "origin=direct&hints=5&noads=30", origin); code != http.StatusOK || !strings.Contains(body, "Granted") { t.Fatalf("raw grant = %d, has 'Granted' = %v", code, strings.Contains(body, "Granted")) } if b := benefitFor(t, pay, id, payments.SourceDirect); b.Hints != 5 || b.AdsPaidUntil.IsZero() { t.Fatalf("after raw grant: hints=%d adsUntil-zero=%v, want 5 hints + a no-ads term", b.Hints, b.AdsPaidUntil.IsZero()) } // By-product grant: an archived reward bundle (3 hints) to vk. reward, err := pay.CreateProduct(ctx, payments.ProductInput{ Title: "reward-3-hints", Atoms: []payments.AtomLine{{Atom: "hints", Quantity: 3}}, }, false) if err != nil { t.Fatalf("create reward: %v", err) } if code, body := consoleDo(h, http.MethodPost, base+"/grant-product", "origin=vk&product_id="+reward.String(), origin); code != http.StatusOK || !strings.Contains(body, "Granted") { t.Fatalf("product grant = %d, has 'Granted' = %v", code, strings.Contains(body, "Granted")) } if b := benefitFor(t, pay, id, payments.SourceVK); b.Hints != 3 { t.Fatalf("after product grant: vk hints=%d, want 3", b.Hints) } // A chips pack cannot be granted. pack, err := pay.CreateProduct(ctx, payments.ProductInput{ Title: "grant-pack", Atoms: []payments.AtomLine{{Atom: "chips", Quantity: 100}}, Prices: []payments.PriceLine{{Method: "direct", Currency: payments.CurrencyRUB, Amount: 14900}}, }, true) if err != nil { t.Fatalf("create pack: %v", err) } if code, body := consoleDo(h, http.MethodPost, base+"/grant-product", "origin=direct&product_id="+pack.String(), origin); code != http.StatusOK || !strings.Contains(body, "cannot grant chips") { t.Fatalf("chips grant = %d, has 'cannot grant chips' = %v", code, strings.Contains(body, "cannot grant chips")) } }