2026-03-30 00:54:20 +00:00
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
2026-03-30 00:19:43 +00:00
|
|
|
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
package collect
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2026-03-29 23:59:48 +00:00
|
|
|
json "dappco.re/go/core/scm/internal/ax/jsonx"
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"testing"
|
|
|
|
|
|
2026-03-21 23:54:23 +00:00
|
|
|
"dappco.re/go/core/io"
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-30 06:37:20 +00:00
|
|
|
func TestMarketCollector_Collect_Good_HistoricalWithFromDate_Good(t *testing.T) {
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
callCount := 0
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
callCount++
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
|
|
|
|
if callCount == 1 {
|
|
|
|
|
data := coinData{
|
|
|
|
|
ID: "lethean",
|
|
|
|
|
Symbol: "lthn",
|
|
|
|
|
Name: "Lethean",
|
|
|
|
|
MarketData: marketData{
|
|
|
|
|
CurrentPrice: map[string]float64{"usd": 0.001},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
_ = json.NewEncoder(w).Encode(data)
|
|
|
|
|
} else {
|
|
|
|
|
// Historical data with FromDate param.
|
|
|
|
|
assert.Contains(t, r.URL.RawQuery, "days=")
|
|
|
|
|
data := historicalData{
|
|
|
|
|
Prices: [][]float64{{1705305600000, 0.001}},
|
|
|
|
|
MarketCaps: [][]float64{{1705305600000, 10000}},
|
|
|
|
|
TotalVolumes: [][]float64{{1705305600000, 500}},
|
|
|
|
|
}
|
|
|
|
|
_ = json.NewEncoder(w).Encode(data)
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
oldURL := coinGeckoBaseURL
|
|
|
|
|
coinGeckoBaseURL = srv.URL
|
|
|
|
|
defer func() { coinGeckoBaseURL = oldURL }()
|
|
|
|
|
|
|
|
|
|
m := io.NewMockMedium()
|
|
|
|
|
cfg := NewConfigWithMedium(m, "/output")
|
|
|
|
|
cfg.Limiter = nil
|
|
|
|
|
|
|
|
|
|
mc := &MarketCollector{CoinID: "lethean", Historical: true, FromDate: "2025-01-01"}
|
|
|
|
|
result, err := mc.Collect(context.Background(), cfg)
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Equal(t, 3, result.Items)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 06:37:20 +00:00
|
|
|
func TestMarketCollector_Collect_Good_HistoricalInvalidDate_Good(t *testing.T) {
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
callCount := 0
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
callCount++
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
|
|
|
|
if callCount == 1 {
|
|
|
|
|
data := coinData{
|
|
|
|
|
ID: "test",
|
|
|
|
|
Symbol: "tst",
|
|
|
|
|
Name: "Test",
|
|
|
|
|
MarketData: marketData{
|
|
|
|
|
CurrentPrice: map[string]float64{"usd": 1.0},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
_ = json.NewEncoder(w).Encode(data)
|
|
|
|
|
} else {
|
|
|
|
|
// Should fall back to 365 days with invalid date.
|
|
|
|
|
assert.Contains(t, r.URL.RawQuery, "days=365")
|
|
|
|
|
data := historicalData{
|
|
|
|
|
Prices: [][]float64{{1705305600000, 1.0}},
|
|
|
|
|
}
|
|
|
|
|
_ = json.NewEncoder(w).Encode(data)
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
oldURL := coinGeckoBaseURL
|
|
|
|
|
coinGeckoBaseURL = srv.URL
|
|
|
|
|
defer func() { coinGeckoBaseURL = oldURL }()
|
|
|
|
|
|
|
|
|
|
m := io.NewMockMedium()
|
|
|
|
|
cfg := NewConfigWithMedium(m, "/output")
|
|
|
|
|
cfg.Limiter = nil
|
|
|
|
|
|
|
|
|
|
mc := &MarketCollector{CoinID: "test", Historical: true, FromDate: "not-a-date"}
|
|
|
|
|
result, err := mc.Collect(context.Background(), cfg)
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Equal(t, 3, result.Items)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 06:37:20 +00:00
|
|
|
func TestMarketCollector_Collect_Bad_HistoricalServerError_Good(t *testing.T) {
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
callCount := 0
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
callCount++
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
|
|
|
|
if callCount == 1 {
|
|
|
|
|
data := coinData{
|
|
|
|
|
ID: "test",
|
|
|
|
|
Symbol: "tst",
|
|
|
|
|
Name: "Test",
|
|
|
|
|
MarketData: marketData{
|
|
|
|
|
CurrentPrice: map[string]float64{"usd": 1.0},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
_ = json.NewEncoder(w).Encode(data)
|
|
|
|
|
} else {
|
|
|
|
|
// Historical endpoint fails.
|
|
|
|
|
w.WriteHeader(http.StatusTooManyRequests)
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
oldURL := coinGeckoBaseURL
|
|
|
|
|
coinGeckoBaseURL = srv.URL
|
|
|
|
|
defer func() { coinGeckoBaseURL = oldURL }()
|
|
|
|
|
|
|
|
|
|
m := io.NewMockMedium()
|
|
|
|
|
cfg := NewConfigWithMedium(m, "/output")
|
|
|
|
|
cfg.Limiter = nil
|
|
|
|
|
|
|
|
|
|
mc := &MarketCollector{CoinID: "test", Historical: true}
|
|
|
|
|
result, err := mc.Collect(context.Background(), cfg)
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Equal(t, 2, result.Items) // current.json + summary.md
|
|
|
|
|
assert.Equal(t, 1, result.Errors) // historical failed
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 06:37:20 +00:00
|
|
|
func TestMarketCollector_Collect_Good_EmitsEvents_Good(t *testing.T) {
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
data := coinData{
|
|
|
|
|
ID: "bitcoin",
|
|
|
|
|
Symbol: "btc",
|
|
|
|
|
Name: "Bitcoin",
|
|
|
|
|
MarketData: marketData{
|
|
|
|
|
CurrentPrice: map[string]float64{"usd": 50000},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
_ = json.NewEncoder(w).Encode(data)
|
|
|
|
|
}))
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
oldURL := coinGeckoBaseURL
|
|
|
|
|
coinGeckoBaseURL = srv.URL
|
|
|
|
|
defer func() { coinGeckoBaseURL = oldURL }()
|
|
|
|
|
|
|
|
|
|
m := io.NewMockMedium()
|
|
|
|
|
cfg := NewConfigWithMedium(m, "/output")
|
|
|
|
|
cfg.Limiter = nil
|
|
|
|
|
|
|
|
|
|
var starts, completes int
|
|
|
|
|
cfg.Dispatcher.On(EventStart, func(e Event) { starts++ })
|
|
|
|
|
cfg.Dispatcher.On(EventComplete, func(e Event) { completes++ })
|
|
|
|
|
|
|
|
|
|
mc := &MarketCollector{CoinID: "bitcoin"}
|
|
|
|
|
_, err := mc.Collect(context.Background(), cfg)
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Equal(t, 1, starts)
|
|
|
|
|
assert.Equal(t, 1, completes)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 06:37:20 +00:00
|
|
|
func TestMarketCollector_Collect_Good_CancelledContext_Good(t *testing.T) {
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}))
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
oldURL := coinGeckoBaseURL
|
|
|
|
|
coinGeckoBaseURL = srv.URL
|
|
|
|
|
defer func() { coinGeckoBaseURL = oldURL }()
|
|
|
|
|
|
|
|
|
|
m := io.NewMockMedium()
|
|
|
|
|
cfg := NewConfigWithMedium(m, "/output")
|
|
|
|
|
cfg.Limiter = nil
|
|
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
cancel()
|
|
|
|
|
|
|
|
|
|
mc := &MarketCollector{CoinID: "bitcoin"}
|
|
|
|
|
result, err := mc.Collect(ctx, cfg)
|
|
|
|
|
|
|
|
|
|
// Context cancellation causes error in fetchJSON.
|
|
|
|
|
require.NoError(t, err) // outer Collect doesn't return errors from currentData fetch
|
|
|
|
|
assert.Equal(t, 1, result.Errors)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 06:37:20 +00:00
|
|
|
func TestFormatMarketSummary_Good_AllFields_Good(t *testing.T) {
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
data := &coinData{
|
|
|
|
|
Name: "Lethean",
|
|
|
|
|
Symbol: "lthn",
|
|
|
|
|
MarketData: marketData{
|
|
|
|
|
CurrentPrice: map[string]float64{"usd": 0.001},
|
|
|
|
|
MarketCap: map[string]float64{"usd": 100000},
|
|
|
|
|
TotalVolume: map[string]float64{"usd": 5000},
|
|
|
|
|
High24h: map[string]float64{"usd": 0.0015},
|
|
|
|
|
Low24h: map[string]float64{"usd": 0.0005},
|
|
|
|
|
PriceChange24h: 0.0002,
|
|
|
|
|
PriceChangePct24h: 5.5,
|
|
|
|
|
MarketCapRank: 500,
|
|
|
|
|
CirculatingSupply: 1000000000,
|
|
|
|
|
TotalSupply: 2000000000,
|
|
|
|
|
LastUpdated: "2025-01-15T12:00:00Z",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
summary := FormatMarketSummary(data)
|
|
|
|
|
|
|
|
|
|
assert.Contains(t, summary, "# Lethean (LTHN)")
|
|
|
|
|
assert.Contains(t, summary, "24h Volume")
|
|
|
|
|
assert.Contains(t, summary, "24h High")
|
|
|
|
|
assert.Contains(t, summary, "24h Low")
|
|
|
|
|
assert.Contains(t, summary, "24h Price Change")
|
|
|
|
|
assert.Contains(t, summary, "#500")
|
|
|
|
|
assert.Contains(t, summary, "Circulating Supply")
|
|
|
|
|
assert.Contains(t, summary, "Total Supply")
|
|
|
|
|
assert.Contains(t, summary, "Last updated")
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 06:37:20 +00:00
|
|
|
func TestFormatMarketSummary_Good_Minimal_Good(t *testing.T) {
|
test(collect): push coverage from 57.3% to 83.0%
Add HTTP mock tests for BitcoinTalk (fetchPage, Collect with server),
papers (IACR HTML parsing, arXiv XML parsing, PaperSourceAll), market
(historical with FromDate, invalid date, server errors), process
(ordered lists, blockquotes, h4-h6, nested objects, cancelled context),
excavate (resume skips completed, progress events), and state (copy
safety, cursor round-trip, null JSON).
Uses httptest.Server with rewriteTransport to intercept external HTTP
calls without touching the production code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:55:18 +00:00
|
|
|
data := &coinData{
|
|
|
|
|
Name: "Unknown",
|
|
|
|
|
Symbol: "ukn",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
summary := FormatMarketSummary(data)
|
|
|
|
|
assert.Contains(t, summary, "# Unknown (UKN)")
|
|
|
|
|
// No price data, so these should be absent.
|
|
|
|
|
assert.NotContains(t, summary, "Market Cap Rank")
|
|
|
|
|
}
|