package coingecko import ( "context" "testing" "gitea.stevedudenhoeffer.com/steve/go-extractor" "gitea.stevedudenhoeffer.com/steve/go-extractor/extractortest" ) func makeBitcoinDoc() *extractortest.MockDocument { return &extractortest.MockDocument{ URLValue: "https://www.coingecko.com/en/coins/bitcoin", MockNode: extractortest.MockNode{ Children: map[string]extractor.Nodes{ "div[data-coin-show-target='coin'] h1": { &extractortest.MockNode{TextValue: "Bitcoin"}, }, "span[data-coin-symbol]": { &extractortest.MockNode{TextValue: "btc"}, }, "span[data-converter-target='price']": { &extractortest.MockNode{TextValue: "$97,234.56"}, }, "span[data-price-change='24h']": { &extractortest.MockNode{TextValue: "-2.34%"}, }, "span[data-price-change='7d']": { &extractortest.MockNode{TextValue: "5.67%"}, }, "span[data-coin-stat='market-cap']": { &extractortest.MockNode{TextValue: "$1.92T"}, }, "span[data-coin-stat='volume']": { &extractortest.MockNode{TextValue: "$28.5B"}, }, "span[data-coin-stat='high-24h']": { &extractortest.MockNode{TextValue: "$98,100.00"}, }, "span[data-coin-stat='low-24h']": { &extractortest.MockNode{TextValue: "$95,500.00"}, }, }, }, } } func TestExtractCoinPrice(t *testing.T) { doc := makeBitcoinDoc() data, err := extractCoinPrice(doc) if err != nil { t.Fatalf("extractCoinPrice() error: %v", err) } if data.Name != "Bitcoin" { t.Errorf("Name = %q, want %q", data.Name, "Bitcoin") } if data.Symbol != "BTC" { t.Errorf("Symbol = %q, want %q", data.Symbol, "BTC") } if data.Price != 97234.56 { t.Errorf("Price = %v, want 97234.56", data.Price) } if data.Change24h != -2.34 { t.Errorf("Change24h = %v, want -2.34", data.Change24h) } if data.Change7d != 5.67 { t.Errorf("Change7d = %v, want 5.67", data.Change7d) } if data.MarketCap != 1_920_000_000_000 { t.Errorf("MarketCap = %v, want 1920000000000", data.MarketCap) } if data.Volume24h != 28_500_000_000 { t.Errorf("Volume24h = %v, want 28500000000", data.Volume24h) } if data.High24h != 98100.00 { t.Errorf("High24h = %v, want 98100.00", data.High24h) } if data.Low24h != 95500.00 { t.Errorf("Low24h = %v, want 95500.00", data.Low24h) } } func TestExtractCoinPrice_NameFallback(t *testing.T) { doc := &extractortest.MockDocument{ MockNode: extractortest.MockNode{ Children: map[string]extractor.Nodes{ "h1": { &extractortest.MockNode{TextValue: "Ethereum"}, }, "span[data-converter-target='price']": { &extractortest.MockNode{TextValue: "$3,456.78"}, }, }, }, } data, err := extractCoinPrice(doc) if err != nil { t.Fatalf("extractCoinPrice() error: %v", err) } if data.Name != "Ethereum" { t.Errorf("Name = %q, want %q", data.Name, "Ethereum") } if data.Price != 3456.78 { t.Errorf("Price = %v, want 3456.78", data.Price) } } func TestExtractCoinPrice_PriceFallback(t *testing.T) { doc := &extractortest.MockDocument{ MockNode: extractortest.MockNode{ Children: map[string]extractor.Nodes{ "span[data-price-target='price']": { &extractortest.MockNode{TextValue: "$0.5432"}, }, }, }, } data, err := extractCoinPrice(doc) if err != nil { t.Fatalf("extractCoinPrice() error: %v", err) } if data.Price != 0.5432 { t.Errorf("Price = %v, want 0.5432", data.Price) } } func TestExtractCoinPrice_Empty(t *testing.T) { doc := &extractortest.MockDocument{ MockNode: extractortest.MockNode{ Children: map[string]extractor.Nodes{}, }, } data, err := extractCoinPrice(doc) if err != nil { t.Fatalf("extractCoinPrice() error: %v", err) } if data.Name != "" || data.Price != 0 { t.Error("expected zero values for empty doc") } } func TestGetPrice_MockBrowser(t *testing.T) { doc := makeBitcoinDoc() browser := &extractortest.MockBrowser{ Documents: map[string]*extractortest.MockDocument{ "https://www.coingecko.com/en/coins/bitcoin": doc, }, } data, err := DefaultConfig.GetPrice(context.Background(), browser, "bitcoin") if err != nil { t.Fatalf("GetPrice() error: %v", err) } if data.Name != "Bitcoin" { t.Errorf("Name = %q, want %q", data.Name, "Bitcoin") } if data.Price != 97234.56 { t.Errorf("Price = %v, want 97234.56", data.Price) } } func TestParseLargeNumber(t *testing.T) { tests := []struct { input string want float64 }{ {"$1.92T", 1_920_000_000_000}, {"$28.5B", 28_500_000_000}, {"$250M", 250_000_000}, {"$1,234,567", 1234567}, {"$0.50", 0.50}, } for _, tt := range tests { got := parseLargeNumber(tt.input) if got != tt.want { t.Errorf("parseLargeNumber(%q) = %v, want %v", tt.input, got, tt.want) } } } func TestCoinURL(t *testing.T) { got := coinURL("Bitcoin") want := "https://www.coingecko.com/en/coins/bitcoin" if got != want { t.Errorf("coinURL(\"Bitcoin\") = %q, want %q", got, want) } }