package yookassa import ( "strings" "testing" ) func testShops() Shops { return Shops{ ChannelWeb: {ShopID: "100500", SecretKey: "web-secret"}, ChannelAndroid: {ShopID: "100700", SecretKey: "android-secret"}, } } func TestShopsShopRouting(t *testing.T) { s := testShops() for channel, wantShopID := range map[string]string{ ChannelWeb: "100500", ChannelAndroid: "100700", "ios": "100500", // an unconfigured channel falls back to web "": "100500", // so does an unknown platform subtype } { got, ok := s.Shop(channel) if !ok || got.ShopID != wantShopID { t.Errorf("Shop(%q) = %q ok=%v, want %q", channel, got.ShopID, ok, wantShopID) } } } func TestShopsShopWithoutWebFallback(t *testing.T) { // Only android configured: an unknown channel has nowhere safe to fall back to. s := Shops{ChannelAndroid: {ShopID: "100700", SecretKey: "android-secret"}} if got, ok := s.Shop(ChannelAndroid); !ok || got.ShopID != "100700" { t.Errorf("Shop(android) = %q ok=%v, want the android shop", got.ShopID, ok) } if _, ok := s.Shop("ios"); ok { t.Error("Shop(ios) resolved with no web shop configured") } if _, ok := (Shops{}).Shop(ChannelWeb); ok { t.Error("an empty set resolved a shop") } } func TestShopsByShopIDDoesNotGuess(t *testing.T) { s := testShops() channel, cfg, ok := s.ByShopID("100700") if !ok || channel != ChannelAndroid || cfg.SecretKey != "android-secret" { t.Errorf("ByShopID(100700) = %q/%q ok=%v, want the android shop", channel, cfg.SecretKey, ok) } // An unrecognised shop id means the notification was not meant for us; guessing at credentials // would verify a foreign payment against our own shop. for _, id := range []string{"999999", ""} { if _, _, ok := s.ByShopID(id); ok { t.Errorf("ByShopID(%q) resolved a shop", id) } } } func TestShopsIgnoreHalfConfiguredEntries(t *testing.T) { s := Shops{ ChannelWeb: {ShopID: "100500"}, // no secret key ChannelAndroid: {SecretKey: "android-secret"}, } if s.Configured() { t.Error("Configured() = true with no complete shop") } if _, ok := s.Shop(ChannelWeb); ok { t.Error("Shop(web) resolved a shop with no secret key") } if _, _, ok := s.ByShopID("100500"); ok { t.Error("ByShopID matched a shop with no secret key") } } func TestShopsConfigured(t *testing.T) { if !testShops().Configured() { t.Error("Configured() = false with two complete shops") } if (Shops{}).Configured() { t.Error("Configured() = true for an empty set") } } func TestSingleItemReceiptTruncatesLongTitles(t *testing.T) { long := strings.Repeat("ф", 200) // Cyrillic: truncation must count runes, not bytes r := SingleItemReceipt("buyer@example.test", long, Amount{Value: "1.00", Currency: "RUB"}, VatCodeNone) got := []rune(r.Items[0].Description) if len(got) != maxDescriptionRunes { t.Errorf("description = %d runes, want %d", len(got), maxDescriptionRunes) } for _, c := range got { if c != 'ф' { t.Fatalf("truncation cut a multi-byte rune: %q", string(got)) } } if r.Items[0].Quantity != 1 { t.Errorf("quantity = %v, want 1 (a chip pack is bought once)", r.Items[0].Quantity) } } func TestValidVatCode(t *testing.T) { for _, code := range []int{1, 4, 11, 12} { if !ValidVatCode(code) { t.Errorf("vat code %d rejected, want accepted", code) } } for _, code := range []int{0, -1, 13} { if ValidVatCode(code) { t.Errorf("vat code %d accepted, want rejected", code) } } }