diff options
| author | historia <[not public]> | 2026-10-06 12:57:23 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-10-06 12:57:23 -0400 |
| commit | 9666676766bf21364f4fc4d0d1d336e72f2a91b4 (patch) | |
| tree | 0cade34091d7602790dc38cd44053f0b74bbadf2 /internal/cryptomarket | |
| parent | 4bc1daaf259e0c19c5fe4a12ec8bc7f6e5a735c1 (diff) | |
| download | thehouseoficarus-9666676766bf21364f4fc4d0d1d336e72f2a91b4.tar.gz | |
refactor: split casino.go, dedup helpers, remove audit docs
Diffstat (limited to 'internal/cryptomarket')
| -rw-r--r-- | internal/cryptomarket/chart.go | 5 | ||||
| -rw-r--r-- | internal/cryptomarket/client.go | 44 | ||||
| -rw-r--r-- | internal/cryptomarket/client_test.go | 21 |
3 files changed, 47 insertions, 23 deletions
diff --git a/internal/cryptomarket/chart.go b/internal/cryptomarket/chart.go index c1fef29..5ea477c 100644 --- a/internal/cryptomarket/chart.go +++ b/internal/cryptomarket/chart.go @@ -13,7 +13,10 @@ func Chart(candles []Candle, width int, unicode bool, paint func(string, bool) s width = max(12, min(80, width)) label := 12 count := width - label - 2 - if count < 2 || len(candles) < 2 { + if count < 2 { + return []string{"Chart too narrow; widen wrap_width."} + } + if len(candles) < 2 { return []string{"Not enough completed daily candles."} } if len(candles) > count { diff --git a/internal/cryptomarket/client.go b/internal/cryptomarket/client.go index 4d6d0a8..8445f4a 100644 --- a/internal/cryptomarket/client.go +++ b/internal/cryptomarket/client.go @@ -95,16 +95,16 @@ func (s Snapshot) Expiry(symbol string, expiry int64) (string, bool) { } type Client struct { - HTTP *http.Client - Base string - once sync.Once - gate chan struct{} - cache Snapshot - fetchedAt map[string]time.Time - historyDay int64 - history map[string][]Candle - retryAfter time.Time - lastError error + HTTP *http.Client + Base string + once sync.Once + gate chan struct{} + cache Snapshot + attemptedAt map[string]time.Time + historyDay int64 + history map[string][]Candle + retryAfter time.Time + lastError error } func NewClient() *Client { @@ -179,27 +179,27 @@ func (c *Client) Fetch(ctx context.Context, symbols []string) (Snapshot, error) c.retryAfter = time.Now().Add(15 * time.Second) return result, err } - if c.fetchedAt == nil { - c.fetchedAt = map[string]time.Time{} + if c.attemptedAt == nil { + c.attemptedAt = map[string]time.Time{} } c.cache = c.cache.merge(result) now := time.Now() - for sym := range result.Quotes { - c.fetchedAt[sym] = now + for _, sym := range symbols { + c.attemptedAt[sym] = now } return result, nil } -// fresh reports whether every requested symbol has a recent quote. Each -// symbol ages independently, so another currency's fetch cannot make a stale -// quote look usable. No background work runs: only an explicit interaction -// refreshes anything. +// fresh reports whether every requested symbol has a recent fetch attempt. A +// symbol that was attempted but produced no quote (halted/delisted, or a +// missing/empty ticker) is still considered fresh: without this, every crypto +// command would re-run the full request chain for a permanently-unavailable +// pair. No background work runs: only an explicit interaction refreshes +// anything. func (c *Client) fresh(symbols []string) bool { for _, sym := range symbols { - if _, ok := c.cache.Quotes[sym]; !ok { - return false - } - if at, ok := c.fetchedAt[sym]; !ok || time.Since(at) >= 15*time.Second { + at, ok := c.attemptedAt[sym] + if !ok || time.Since(at) >= 15*time.Second { return false } } diff --git a/internal/cryptomarket/client_test.go b/internal/cryptomarket/client_test.go index 39367fc..ab77b24 100644 --- a/internal/cryptomarket/client_test.go +++ b/internal/cryptomarket/client_test.go @@ -214,3 +214,24 @@ func TestClientCancellationDoesNotPoisonSharedBackoff(t *testing.T) { t.Fatalf("cancelled fetch poisoned the shared client: %v", e) } } + +func TestHaltedSymbolIsFreshAfterAttempt(t *testing.T) { + c, calls := mockKraken(t, "halted") + if s, e := c.Fetch(context.Background(), []string{"BTC"}); e != nil { + t.Fatal(e) + } else if _, ok := s.Quotes["BTC"]; ok { + t.Fatal("halted symbol unexpectedly quoted") + } + first := calls.Load() + if first != 4 { + t.Fatalf("first fetch calls = %d, want 4", first) + } + // A halted/delisted pair has no quote, but the attempt must be cached so + // the next command does not re-run the full request chain forever. + if _, e := c.Fetch(context.Background(), []string{"BTC"}); e != nil { + t.Fatal(e) + } + if calls.Load() != first { + t.Fatalf("halted symbol re-fetched: %d -> %d", first, calls.Load()) + } +} |
