refactored WatchSlots_Dynamic test
This commit is contained in:
parent
1582782904
commit
d06edc078e
|
@ -28,21 +28,21 @@ func TestNewExporterConfig(t *testing.T) {
|
|||
httpTimeout: 60 * time.Second,
|
||||
rpcUrl: simulator.Server.URL(),
|
||||
listenAddress: ":8080",
|
||||
nodeKeys: nodekeys,
|
||||
nodeKeys: simulator.Nodekeys,
|
||||
balanceAddresses: []string{"xxx", "yyy", "zzz"},
|
||||
comprehensiveSlotTracking: false,
|
||||
monitorBlockSizes: false,
|
||||
lightMode: false,
|
||||
slotPace: time.Second,
|
||||
wantErr: false,
|
||||
expectedVoteKeys: votekeys,
|
||||
expectedVoteKeys: simulator.Votekeys,
|
||||
},
|
||||
{
|
||||
name: "light mode with incompatible options",
|
||||
httpTimeout: 60 * time.Second,
|
||||
rpcUrl: simulator.Server.URL(),
|
||||
listenAddress: ":8080",
|
||||
nodeKeys: nodekeys,
|
||||
nodeKeys: simulator.Nodekeys,
|
||||
balanceAddresses: []string{"xxx", "yyy", "zzz"},
|
||||
comprehensiveSlotTracking: true,
|
||||
monitorBlockSizes: false,
|
||||
|
|
|
@ -41,7 +41,3 @@ func (c *GaugeDesc) expectedCollection(labeledValues ...LV) string {
|
|||
func (c *GaugeDesc) makeCollectionTest(labeledValues ...LV) collectionTest {
|
||||
return collectionTest{Name: c.Name, ExpectedResponse: c.expectedCollection(labeledValues...)}
|
||||
}
|
||||
|
||||
func abcValues(a, b, c float64) []LV {
|
||||
return []LV{NewLV(a, "aaa", "AAA"), NewLV(b, "bbb", "BBB"), NewLV(c, "ccc", "CCC")}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ const (
|
|||
StateCurrent = "current"
|
||||
StateDelinquent = "delinquent"
|
||||
|
||||
TransactionTypeVote = "vote"
|
||||
TransactionTypeTotal = "total"
|
||||
TransactionTypeVote = "vote"
|
||||
TransactionTypeNonVote = "non_vote"
|
||||
)
|
||||
|
||||
type SolanaCollector struct {
|
||||
|
|
|
@ -17,6 +17,9 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const InflationRewardLamports = 10
|
||||
const FeeRewardLamports = 10
|
||||
|
||||
type (
|
||||
Simulator struct {
|
||||
Server *rpc.MockServer
|
||||
|
@ -24,113 +27,80 @@ type (
|
|||
Slot int
|
||||
BlockHeight int
|
||||
Epoch int
|
||||
EpochSize int
|
||||
SlotTime time.Duration
|
||||
TransactionCount int
|
||||
LeaderSchedule map[string][]int
|
||||
|
||||
SlotTime time.Duration
|
||||
EpochSize int
|
||||
LeaderSchedule map[string][]int
|
||||
Nodekeys []string
|
||||
Votekeys []string
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
nodekeys = []string{"aaa", "bbb", "ccc"}
|
||||
votekeys = []string{"AAA", "BBB", "CCC"}
|
||||
balances = map[string]float64{"aaa": 1, "bbb": 2, "ccc": 3, "AAA": 4, "BBB": 5, "CCC": 6}
|
||||
rawVoteAccounts = map[string]any{
|
||||
"current": []map[string]any{
|
||||
{
|
||||
"activatedStake": 42,
|
||||
"lastVote": 147,
|
||||
"nodePubkey": "bbb",
|
||||
"rootSlot": 18,
|
||||
"votePubkey": "BBB",
|
||||
},
|
||||
{
|
||||
"activatedStake": 43,
|
||||
"lastVote": 148,
|
||||
"nodePubkey": "ccc",
|
||||
"rootSlot": 19,
|
||||
"votePubkey": "CCC",
|
||||
},
|
||||
},
|
||||
"delinquent": []map[string]any{
|
||||
{
|
||||
"activatedStake": 49,
|
||||
"lastVote": 92,
|
||||
"nodePubkey": "aaa",
|
||||
"rootSlot": 3,
|
||||
"votePubkey": "AAA",
|
||||
},
|
||||
},
|
||||
}
|
||||
rawBalances = map[string]int{
|
||||
"aaa": 1 * rpc.LamportsInSol,
|
||||
"bbb": 2 * rpc.LamportsInSol,
|
||||
"ccc": 3 * rpc.LamportsInSol,
|
||||
"AAA": 4 * rpc.LamportsInSol,
|
||||
"BBB": 5 * rpc.LamportsInSol,
|
||||
"CCC": 6 * rpc.LamportsInSol,
|
||||
}
|
||||
balanceMetricResponse = `
|
||||
# HELP solana_account_balance Solana account balances, grouped by address
|
||||
# TYPE solana_account_balance gauge
|
||||
solana_account_balance{address="AAA"} 4
|
||||
solana_account_balance{address="BBB"} 5
|
||||
solana_account_balance{address="CCC"} 6
|
||||
solana_account_balance{address="aaa"} 1
|
||||
solana_account_balance{address="bbb"} 2
|
||||
solana_account_balance{address="ccc"} 3
|
||||
`
|
||||
dynamicLeaderSchedule = map[string][]int{
|
||||
"aaa": {0, 1, 2, 3, 12, 13, 14, 15},
|
||||
"bbb": {4, 5, 6, 7, 16, 17, 18, 19},
|
||||
"ccc": {8, 9, 10, 11, 20, 21, 22, 23},
|
||||
}
|
||||
)
|
||||
|
||||
/*
|
||||
===== DYNAMIC CLIENT =====:
|
||||
*/
|
||||
|
||||
func voteTx(nodekey string) []string {
|
||||
return []string{nodekey, strings.ToUpper(nodekey), VoteProgram}
|
||||
}
|
||||
|
||||
func NewSimulator(t *testing.T, slot int) (*Simulator, *rpc.Client) {
|
||||
nodekeys := []string{"aaa", "bbb", "ccc"}
|
||||
votekeys := []string{"AAA", "BBB", "CCC"}
|
||||
|
||||
validatorInfos := make(map[string]rpc.MockValidatorInfo)
|
||||
for _, nodekey := range nodekeys {
|
||||
for i, nodekey := range nodekeys {
|
||||
validatorInfos[nodekey] = rpc.MockValidatorInfo{
|
||||
Votekey: strings.ToUpper(nodekey),
|
||||
Votekey: votekeys[i],
|
||||
Stake: 1_000_000,
|
||||
Delinquent: false,
|
||||
}
|
||||
}
|
||||
leaderSchedule := map[string][]int{
|
||||
"aaa": {0, 1, 2, 3, 12, 13, 14, 15},
|
||||
"bbb": {4, 5, 6, 7, 16, 17, 18, 19},
|
||||
"ccc": {8, 9, 10, 11, 20, 21, 22, 23},
|
||||
}
|
||||
mockServer, client := rpc.NewMockClient(t,
|
||||
map[string]any{
|
||||
"getVersion": map[string]string{"solana-core": "v1.0.0"},
|
||||
"getLeaderSchedule": dynamicLeaderSchedule,
|
||||
"getLeaderSchedule": leaderSchedule,
|
||||
"getHealth": "ok",
|
||||
},
|
||||
rawBalances,
|
||||
map[string]int{"AAA": 10, "BBB": 10, "CCC": 10},
|
||||
map[string]int{
|
||||
"aaa": 1 * rpc.LamportsInSol,
|
||||
"bbb": 2 * rpc.LamportsInSol,
|
||||
"ccc": 3 * rpc.LamportsInSol,
|
||||
"AAA": 4 * rpc.LamportsInSol,
|
||||
"BBB": 5 * rpc.LamportsInSol,
|
||||
"CCC": 6 * rpc.LamportsInSol,
|
||||
},
|
||||
map[string]int{
|
||||
"AAA": InflationRewardLamports,
|
||||
"BBB": InflationRewardLamports,
|
||||
"CCC": InflationRewardLamports,
|
||||
},
|
||||
nil,
|
||||
validatorInfos,
|
||||
)
|
||||
server := Simulator{
|
||||
simulator := Simulator{
|
||||
Slot: 0,
|
||||
Server: mockServer,
|
||||
EpochSize: 24,
|
||||
SlotTime: 100 * time.Millisecond,
|
||||
LeaderSchedule: dynamicLeaderSchedule,
|
||||
LeaderSchedule: leaderSchedule,
|
||||
Nodekeys: nodekeys,
|
||||
Votekeys: votekeys,
|
||||
}
|
||||
server.PopulateSlot(0)
|
||||
for {
|
||||
server.Slot++
|
||||
server.PopulateSlot(server.Slot)
|
||||
if server.Slot == slot {
|
||||
break
|
||||
simulator.PopulateSlot(0)
|
||||
if slot > 0 {
|
||||
for {
|
||||
simulator.Slot++
|
||||
simulator.PopulateSlot(simulator.Slot)
|
||||
if simulator.Slot == slot {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return &server, client
|
||||
|
||||
return &simulator, client
|
||||
}
|
||||
|
||||
func (c *Simulator) Run(ctx context.Context) {
|
||||
|
@ -173,7 +143,7 @@ func (c *Simulator) PopulateSlot(slot int) {
|
|||
{"xxx", "yyy", "zzz"},
|
||||
}
|
||||
// assume all validators voted
|
||||
for _, nodekey := range nodekeys {
|
||||
for _, nodekey := range c.Nodekeys {
|
||||
transactions = append(transactions, voteTx(nodekey))
|
||||
info := c.Server.GetValidatorInfo(nodekey)
|
||||
info.LastVote = slot
|
||||
|
@ -181,7 +151,7 @@ func (c *Simulator) PopulateSlot(slot int) {
|
|||
}
|
||||
|
||||
c.TransactionCount += len(transactions)
|
||||
block = &rpc.MockBlockInfo{Fee: 100, Transactions: transactions}
|
||||
block = &rpc.MockBlockInfo{Fee: FeeRewardLamports, Transactions: transactions}
|
||||
}
|
||||
// add slot info:
|
||||
c.Server.SetOpt(rpc.SlotInfosOpt, slot, rpc.MockSlotInfo{Leader: leader, Block: block})
|
||||
|
@ -251,17 +221,17 @@ func runCollectionTests(t *testing.T, collector prometheus.Collector, testCases
|
|||
}
|
||||
}
|
||||
|
||||
func newTestConfig(fast bool) *ExporterConfig {
|
||||
func newTestConfig(simulator *Simulator, fast bool) *ExporterConfig {
|
||||
pace := time.Duration(100) * time.Second
|
||||
if fast {
|
||||
pace = time.Duration(500) * time.Millisecond
|
||||
}
|
||||
config := ExporterConfig{
|
||||
time.Second * time.Duration(1),
|
||||
"http://localhost:8899",
|
||||
simulator.Server.URL(),
|
||||
":8080",
|
||||
nodekeys,
|
||||
votekeys,
|
||||
simulator.Nodekeys,
|
||||
simulator.Votekeys,
|
||||
nil,
|
||||
true,
|
||||
true,
|
||||
|
@ -272,19 +242,54 @@ func newTestConfig(fast bool) *ExporterConfig {
|
|||
}
|
||||
|
||||
func TestSolanaCollector(t *testing.T) {
|
||||
_, client := NewSimulator(t, 35)
|
||||
collector := NewSolanaCollector(client, newTestConfig(false))
|
||||
simulator, client := NewSimulator(t, 35)
|
||||
collector := NewSolanaCollector(client, newTestConfig(simulator, false))
|
||||
prometheus.NewPedanticRegistry().MustRegister(collector)
|
||||
|
||||
testCases := []collectionTest{
|
||||
collector.ValidatorActiveStake.makeCollectionTest(abcValues(1_000_000, 1_000_000, 1_000_000)...),
|
||||
collector.ValidatorLastVote.makeCollectionTest(abcValues(34, 34, 34)...),
|
||||
collector.ValidatorRootSlot.makeCollectionTest(abcValues(0, 0, 0)...),
|
||||
collector.ValidatorDelinquent.makeCollectionTest(abcValues(0, 0, 0)...),
|
||||
{Name: "solana_account_balance", ExpectedResponse: balanceMetricResponse},
|
||||
collector.NodeVersion.makeCollectionTest(NewLV(1, "v1.0.0")),
|
||||
collector.NodeIsHealthy.makeCollectionTest(NewLV(1)),
|
||||
collector.NodeNumSlotsBehind.makeCollectionTest(NewLV(0)),
|
||||
collector.ValidatorActiveStake.makeCollectionTest(
|
||||
NewLV(1_000_000, "aaa", "AAA"),
|
||||
NewLV(1_000_000, "bbb", "BBB"),
|
||||
NewLV(1_000_000, "ccc", "CCC"),
|
||||
),
|
||||
collector.ValidatorLastVote.makeCollectionTest(
|
||||
NewLV(34, "aaa", "AAA"),
|
||||
NewLV(34, "bbb", "BBB"),
|
||||
NewLV(34, "ccc", "CCC"),
|
||||
),
|
||||
collector.ValidatorRootSlot.makeCollectionTest(
|
||||
NewLV(0, "aaa", "AAA"),
|
||||
NewLV(0, "bbb", "BBB"),
|
||||
NewLV(0, "ccc", "CCC"),
|
||||
),
|
||||
collector.ValidatorDelinquent.makeCollectionTest(
|
||||
NewLV(0, "aaa", "AAA"),
|
||||
NewLV(0, "bbb", "BBB"),
|
||||
NewLV(0, "ccc", "CCC"),
|
||||
),
|
||||
collector.NodeVersion.makeCollectionTest(
|
||||
NewLV(1, "v1.0.0"),
|
||||
),
|
||||
collector.NodeIsHealthy.makeCollectionTest(
|
||||
NewLV(1),
|
||||
),
|
||||
collector.NodeNumSlotsBehind.makeCollectionTest(
|
||||
NewLV(0),
|
||||
),
|
||||
collector.AccountBalances.makeCollectionTest(
|
||||
NewLV(4, "AAA"),
|
||||
NewLV(5, "BBB"),
|
||||
NewLV(6, "CCC"),
|
||||
NewLV(1, "aaa"),
|
||||
NewLV(2, "bbb"),
|
||||
NewLV(3, "ccc"),
|
||||
),
|
||||
collector.NodeMinimumLedgerSlot.makeCollectionTest(
|
||||
NewLV(11),
|
||||
),
|
||||
collector.NodeFirstAvailableBlock.makeCollectionTest(
|
||||
NewLV(11),
|
||||
),
|
||||
}
|
||||
|
||||
runCollectionTests(t, collector, testCases)
|
||||
|
|
|
@ -80,7 +80,7 @@ func NewSlotWatcher(client *rpc.Client, config *ExporterConfig) *SlotWatcher {
|
|||
NodekeyLabel, SkipStatusLabel, StatusValid, StatusSkipped,
|
||||
),
|
||||
},
|
||||
[]string{SkipStatusLabel, NodekeyLabel},
|
||||
[]string{NodekeyLabel, SkipStatusLabel},
|
||||
),
|
||||
LeaderSlotsByEpochMetric: prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
|
@ -90,7 +90,7 @@ func NewSlotWatcher(client *rpc.Client, config *ExporterConfig) *SlotWatcher {
|
|||
NodekeyLabel, SkipStatusLabel, StatusValid, StatusSkipped, EpochLabel,
|
||||
),
|
||||
},
|
||||
[]string{SkipStatusLabel, NodekeyLabel, EpochLabel},
|
||||
[]string{NodekeyLabel, EpochLabel, SkipStatusLabel},
|
||||
),
|
||||
InflationRewardsMetric: prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
|
@ -314,13 +314,13 @@ func (c *SlotWatcher) fetchAndEmitBlockProduction(ctx context.Context, endSlot i
|
|||
valid := float64(production.BlocksProduced)
|
||||
skipped := float64(production.LeaderSlots - production.BlocksProduced)
|
||||
|
||||
c.LeaderSlotsMetric.WithLabelValues(StatusValid, address).Add(valid)
|
||||
c.LeaderSlotsMetric.WithLabelValues(StatusSkipped, address).Add(skipped)
|
||||
c.LeaderSlotsMetric.WithLabelValues(address, StatusValid).Add(valid)
|
||||
c.LeaderSlotsMetric.WithLabelValues(address, StatusSkipped).Add(skipped)
|
||||
|
||||
if slices.Contains(c.config.NodeKeys, address) || c.config.ComprehensiveSlotTracking {
|
||||
epochStr := toString(c.currentEpoch)
|
||||
c.LeaderSlotsByEpochMetric.WithLabelValues(StatusValid, address, epochStr).Add(valid)
|
||||
c.LeaderSlotsByEpochMetric.WithLabelValues(StatusSkipped, address, epochStr).Add(skipped)
|
||||
c.LeaderSlotsByEpochMetric.WithLabelValues(address, epochStr, StatusValid).Add(valid)
|
||||
c.LeaderSlotsByEpochMetric.WithLabelValues(address, epochStr, StatusSkipped).Add(skipped)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,15 +395,15 @@ func (c *SlotWatcher) fetchAndEmitSingleBlockInfo(
|
|||
|
||||
// track block size:
|
||||
if c.config.MonitorBlockSizes {
|
||||
c.BlockSizeMetric.WithLabelValues(nodekey, TransactionTypeTotal).Set(float64(len(block.Transactions)))
|
||||
// now count and emit votes:
|
||||
voteCount, err := CountVoteTransactions(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.BlockSizeMetric.WithLabelValues(nodekey, TransactionTypeVote).Set(float64(voteCount))
|
||||
nonVoteCount := len(block.Transactions) - voteCount
|
||||
c.BlockSizeMetric.WithLabelValues(nodekey, TransactionTypeNonVote).Set(float64(nonVoteCount))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ type slotMetricValues struct {
|
|||
EpochNumber float64
|
||||
EpochFirstSlot float64
|
||||
EpochLastSlot float64
|
||||
BlockHeight float64
|
||||
}
|
||||
|
||||
func getSlotMetricValues(watcher *SlotWatcher) slotMetricValues {
|
||||
|
@ -26,45 +27,58 @@ func getSlotMetricValues(watcher *SlotWatcher) slotMetricValues {
|
|||
EpochNumber: testutil.ToFloat64(watcher.EpochNumberMetric),
|
||||
EpochFirstSlot: testutil.ToFloat64(watcher.EpochFirstSlotMetric),
|
||||
EpochLastSlot: testutil.ToFloat64(watcher.EpochLastSlotMetric),
|
||||
BlockHeight: testutil.ToFloat64(watcher.BlockHeightMetric),
|
||||
}
|
||||
}
|
||||
|
||||
func assertSlotMetricsChangeCorrectly(t *testing.T, initial slotMetricValues, final slotMetricValues) {
|
||||
// make sure that things have increased
|
||||
assert.Greaterf(
|
||||
t,
|
||||
assert.Greaterf(t,
|
||||
final.SlotHeight,
|
||||
initial.SlotHeight,
|
||||
"Slot has not increased! (%v -> %v)",
|
||||
initial.SlotHeight,
|
||||
final.SlotHeight,
|
||||
initial.SlotHeight, final.SlotHeight,
|
||||
)
|
||||
assert.Greaterf(
|
||||
t,
|
||||
assert.Greaterf(t,
|
||||
final.TotalTransactions,
|
||||
initial.TotalTransactions,
|
||||
"Total transactions have not increased! (%v -> %v)",
|
||||
initial.TotalTransactions,
|
||||
final.TotalTransactions,
|
||||
initial.TotalTransactions, final.TotalTransactions,
|
||||
)
|
||||
assert.GreaterOrEqualf(
|
||||
t,
|
||||
assert.GreaterOrEqualf(t,
|
||||
final.EpochNumber,
|
||||
initial.EpochNumber,
|
||||
"Epoch number has decreased! (%v -> %v)",
|
||||
initial.EpochNumber,
|
||||
final.EpochNumber,
|
||||
initial.EpochNumber, final.EpochNumber,
|
||||
)
|
||||
assert.GreaterOrEqualf(t,
|
||||
final.EpochFirstSlot,
|
||||
initial.EpochFirstSlot,
|
||||
"Epoch first slot has decreased! (%v -> %v)",
|
||||
initial.EpochFirstSlot, final.EpochFirstSlot,
|
||||
)
|
||||
assert.GreaterOrEqualf(t,
|
||||
final.EpochLastSlot,
|
||||
initial.EpochLastSlot,
|
||||
"Epoch last slot has decreased! (%v -> %v)",
|
||||
initial.EpochLastSlot, final.EpochLastSlot,
|
||||
)
|
||||
assert.Greaterf(t,
|
||||
final.BlockHeight,
|
||||
initial.BlockHeight,
|
||||
"Block height has decreased! (%v -> %v)",
|
||||
initial.BlockHeight, final.BlockHeight,
|
||||
)
|
||||
}
|
||||
|
||||
func TestSlotWatcher_WatchSlots_Static(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// TODO: is this test necessary? If not - remove, else, could definitely do with a clean.
|
||||
|
||||
config := newTestConfig(true)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
_, client := NewSimulator(t, 35)
|
||||
|
||||
watcher := NewSlotWatcher(client, config)
|
||||
simulator, client := NewSimulator(t, 35)
|
||||
watcher := NewSlotWatcher(client, newTestConfig(simulator, true))
|
||||
// reset metrics before running tests:
|
||||
watcher.LeaderSlotsMetric.Reset()
|
||||
watcher.LeaderSlotsByEpochMetric.Reset()
|
||||
|
@ -94,7 +108,7 @@ func TestSlotWatcher_WatchSlots_Static(t *testing.T) {
|
|||
}
|
||||
|
||||
// add inflation reward tests:
|
||||
inflationRewards, err := client.GetInflationReward(ctx, rpc.CommitmentFinalized, votekeys, 2)
|
||||
inflationRewards, err := client.GetInflationReward(ctx, rpc.CommitmentFinalized, simulator.Votekeys, 2)
|
||||
assert.NoError(t, err)
|
||||
for i, rewardInfo := range inflationRewards {
|
||||
epoch := fmt.Sprintf("%v", epochInfo.Epoch)
|
||||
|
@ -102,7 +116,7 @@ func TestSlotWatcher_WatchSlots_Static(t *testing.T) {
|
|||
tests,
|
||||
testCase{
|
||||
expectedValue: float64(rewardInfo.Amount) / float64(rpc.LamportsInSol),
|
||||
metric: watcher.InflationRewardsMetric.WithLabelValues(votekeys[i], epoch),
|
||||
metric: watcher.InflationRewardsMetric.WithLabelValues(simulator.Votekeys[i], epoch),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -116,24 +130,23 @@ func TestSlotWatcher_WatchSlots_Static(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSlotWatcher_WatchSlots_Dynamic(t *testing.T) {
|
||||
// TODO: figure out how to get rid of the error logs that happen when this test closes.
|
||||
// This is presumably due to the context cancelling mid-execution of a WatchSlots() iteration
|
||||
|
||||
// create clients:
|
||||
server, client := NewSimulator(t, 35)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
config := newTestConfig(true)
|
||||
collector := NewSolanaCollector(client, config)
|
||||
watcher := NewSlotWatcher(client, config)
|
||||
simulator, client := NewSimulator(t, 23)
|
||||
watcher := NewSlotWatcher(client, newTestConfig(simulator, true))
|
||||
// reset metrics before running tests:
|
||||
watcher.LeaderSlotsMetric.Reset()
|
||||
watcher.LeaderSlotsByEpochMetric.Reset()
|
||||
prometheus.NewPedanticRegistry().MustRegister(collector)
|
||||
|
||||
// start client/collector and wait a bit:
|
||||
|
||||
go server.Run(ctx)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go watcher.WatchSlots(ctx)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
go watcher.WatchSlots(ctx)
|
||||
go simulator.Run(ctx)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
initial := getSlotMetricValues(watcher)
|
||||
|
@ -149,34 +162,87 @@ func TestSlotWatcher_WatchSlots_Dynamic(t *testing.T) {
|
|||
assertSlotMetricsChangeCorrectly(t, initial, final)
|
||||
|
||||
// sense check to make sure the exporter is not "ahead" of the client (due to double counting or whatever)
|
||||
assert.LessOrEqualf(
|
||||
t,
|
||||
assert.LessOrEqualf(t,
|
||||
int(final.SlotHeight),
|
||||
server.Slot,
|
||||
"Exporter slot (%v) ahead of client slot (%v)!",
|
||||
int(final.SlotHeight),
|
||||
server.Slot,
|
||||
simulator.Slot,
|
||||
"Exporter slot (%v) ahead of simulator slot (%v)!",
|
||||
int(final.SlotHeight), simulator.Slot,
|
||||
)
|
||||
assert.LessOrEqualf(
|
||||
t,
|
||||
assert.LessOrEqualf(t,
|
||||
int(final.TotalTransactions),
|
||||
server.TransactionCount,
|
||||
"Exporter transaction count (%v) ahead of client transaction count (%v)!",
|
||||
int(final.TotalTransactions),
|
||||
server.TransactionCount,
|
||||
simulator.TransactionCount,
|
||||
"Exporter transaction count (%v) ahead of simulator transaction count (%v)!",
|
||||
int(final.TotalTransactions), simulator.TransactionCount,
|
||||
)
|
||||
assert.LessOrEqualf(
|
||||
t,
|
||||
assert.LessOrEqualf(t,
|
||||
int(final.EpochNumber),
|
||||
server.Epoch,
|
||||
"Exporter epoch (%v) ahead of client epoch (%v)!",
|
||||
int(final.EpochNumber),
|
||||
server.Epoch,
|
||||
simulator.Epoch,
|
||||
"Exporter epoch (%v) ahead of simulator epoch (%v)!",
|
||||
int(final.EpochNumber), simulator.Epoch,
|
||||
)
|
||||
|
||||
// check block sizes (should always be the same due to simulator design:
|
||||
for _, nodekey := range simulator.Nodekeys {
|
||||
assert.Equalf(t,
|
||||
float64(2),
|
||||
testutil.ToFloat64(watcher.BlockSizeMetric.WithLabelValues(nodekey, TransactionTypeNonVote)),
|
||||
"Incorrect %s block size for %s",
|
||||
TransactionTypeNonVote, nodekey,
|
||||
)
|
||||
assert.Equalf(t,
|
||||
float64(3),
|
||||
testutil.ToFloat64(watcher.BlockSizeMetric.WithLabelValues(nodekey, TransactionTypeVote)),
|
||||
"Incorrect %s block size for %s",
|
||||
TransactionTypeVote, nodekey,
|
||||
)
|
||||
}
|
||||
|
||||
// check if epoch changed
|
||||
if final.EpochNumber > initial.EpochNumber {
|
||||
epochChanged = true
|
||||
|
||||
// run some tests for the previous epoch:
|
||||
leaderSlotsPerEpoch := simulator.EpochSize / len(simulator.Nodekeys)
|
||||
for i, nodekey := range simulator.Nodekeys {
|
||||
// leader slots per epoch:
|
||||
assert.Equalf(t,
|
||||
float64(leaderSlotsPerEpoch*3/4),
|
||||
testutil.ToFloat64(
|
||||
watcher.LeaderSlotsByEpochMetric.WithLabelValues(nodekey, toString(initial.EpochNumber), StatusValid),
|
||||
),
|
||||
"Incorrect %s leader slots for %s at epoch %v",
|
||||
StatusValid, nodekey, initial.EpochNumber,
|
||||
)
|
||||
assert.Equalf(t,
|
||||
float64(leaderSlotsPerEpoch*1/4),
|
||||
testutil.ToFloat64(
|
||||
watcher.LeaderSlotsByEpochMetric.WithLabelValues(nodekey, toString(initial.EpochNumber), StatusSkipped),
|
||||
),
|
||||
"Incorrect %s leader slots for %s at epoch %v",
|
||||
StatusSkipped, nodekey, initial.EpochNumber,
|
||||
)
|
||||
|
||||
// inflation rewards:
|
||||
votekey := simulator.Votekeys[i]
|
||||
assert.Equalf(t,
|
||||
float64(InflationRewardLamports)/rpc.LamportsInSol,
|
||||
testutil.ToFloat64(
|
||||
watcher.InflationRewardsMetric.WithLabelValues(votekey, toString(initial.EpochNumber)),
|
||||
),
|
||||
"Incorrect inflation reward for %s at epoch %v",
|
||||
votekey, initial.EpochNumber,
|
||||
)
|
||||
|
||||
// fee rewards:
|
||||
assert.Equalf(t,
|
||||
float64(FeeRewardLamports*leaderSlotsPerEpoch*3/4)/rpc.LamportsInSol,
|
||||
testutil.ToFloat64(
|
||||
watcher.FeeRewardsMetric.WithLabelValues(nodekey, toString(initial.EpochNumber)),
|
||||
),
|
||||
"Incorrect fee reward for %s at epoch %v",
|
||||
nodekey, initial.EpochNumber,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// make current final the new initial (for next iteration)
|
||||
|
|
|
@ -18,8 +18,8 @@ func assertf(condition bool, format string, args ...any) {
|
|||
}
|
||||
}
|
||||
|
||||
// toString is just a simple utility function for converting int -> string
|
||||
func toString(i int64) string {
|
||||
// toString is just a simple utility function for converting to strings
|
||||
func toString(i any) string {
|
||||
return fmt.Sprintf("%v", i)
|
||||
}
|
||||
|
||||
|
|
|
@ -57,31 +57,28 @@ func TestCombineUnique(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFetchBalances(t *testing.T) {
|
||||
_, client := rpc.NewMockClient(t, nil, rawBalances, nil, nil, nil)
|
||||
simulator, client := NewSimulator(t, 0)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fetchedBalances, err := FetchBalances(ctx, client, CombineUnique(nodekeys, votekeys))
|
||||
fetchedBalances, err := FetchBalances(ctx, client, CombineUnique(simulator.Nodekeys, simulator.Votekeys))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, balances, fetchedBalances)
|
||||
assert.Equal(t,
|
||||
map[string]float64{"aaa": 1, "bbb": 2, "ccc": 3, "AAA": 4, "BBB": 5, "CCC": 6},
|
||||
fetchedBalances,
|
||||
)
|
||||
}
|
||||
|
||||
func TestGetAssociatedVoteAccounts(t *testing.T) {
|
||||
_, client := rpc.NewMockClient(t,
|
||||
map[string]any{"getVoteAccounts": rawVoteAccounts},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
simulator, client := NewSimulator(t, 1)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
voteAccounts, err := GetAssociatedVoteAccounts(ctx, client, rpc.CommitmentFinalized, nodekeys)
|
||||
voteAccounts, err := GetAssociatedVoteAccounts(ctx, client, rpc.CommitmentFinalized, simulator.Nodekeys)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, votekeys, voteAccounts)
|
||||
assert.Equal(t, simulator.Votekeys, voteAccounts)
|
||||
}
|
||||
|
||||
func TestGetEpochBounds(t *testing.T) {
|
||||
|
|
|
@ -35,7 +35,7 @@ type (
|
|||
inflationRewards map[string]int
|
||||
easyResults map[string]any
|
||||
|
||||
slotInfos map[int]MockSlotInfo
|
||||
SlotInfos map[int]MockSlotInfo
|
||||
validatorInfos map[string]MockValidatorInfo
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ func NewMockServer(
|
|||
easyResults: easyResults,
|
||||
balances: balances,
|
||||
inflationRewards: inflationRewards,
|
||||
slotInfos: slotInfos,
|
||||
SlotInfos: slotInfos,
|
||||
validatorInfos: validatorInfos,
|
||||
}
|
||||
|
||||
|
@ -130,10 +130,10 @@ func (s *MockServer) SetOpt(opt MockOpt, key any, value any) {
|
|||
}
|
||||
s.easyResults[key.(string)] = value
|
||||
case SlotInfosOpt:
|
||||
if s.slotInfos == nil {
|
||||
s.slotInfos = make(map[int]MockSlotInfo)
|
||||
if s.SlotInfos == nil {
|
||||
s.SlotInfos = make(map[int]MockSlotInfo)
|
||||
}
|
||||
s.slotInfos[key.(int)] = value.(MockSlotInfo)
|
||||
s.SlotInfos[key.(int)] = value.(MockSlotInfo)
|
||||
case ValidatorInfoOpt:
|
||||
if s.validatorInfos == nil {
|
||||
s.validatorInfos = make(map[string]MockValidatorInfo)
|
||||
|
@ -174,13 +174,13 @@ func (s *MockServer) getResult(method string, params ...any) (any, *RPCError) {
|
|||
return rewards, nil
|
||||
}
|
||||
|
||||
if method == "getBlock" && s.slotInfos != nil {
|
||||
if method == "getBlock" && s.SlotInfos != nil {
|
||||
// get params:
|
||||
slot := int(params[0].(float64))
|
||||
config := params[1].(map[string]any)
|
||||
transactionDetails, rewardsIncluded := config["transactionDetails"].(string), config["rewards"].(bool)
|
||||
|
||||
slotInfo, ok := s.slotInfos[slot]
|
||||
slotInfo, ok := s.SlotInfos[slot]
|
||||
if !ok {
|
||||
s.logger.Warnf("no slot info for slot %d", slot)
|
||||
return nil, &RPCError{Code: BlockCleanedUpCode, Message: "Block cleaned up."}
|
||||
|
@ -211,7 +211,7 @@ func (s *MockServer) getResult(method string, params ...any) (any, *RPCError) {
|
|||
return map[string]any{"rewards": rewards, "transactions": transactions}, nil
|
||||
}
|
||||
|
||||
if method == "getBlockProduction" && s.slotInfos != nil {
|
||||
if method == "getBlockProduction" && s.SlotInfos != nil {
|
||||
// get params:
|
||||
config := params[0].(map[string]any)
|
||||
slotRange := config["range"].(map[string]any)
|
||||
|
@ -222,7 +222,7 @@ func (s *MockServer) getResult(method string, params ...any) (any, *RPCError) {
|
|||
byIdentity[nodekey] = []int{0, 0}
|
||||
}
|
||||
for i := firstSlot; i <= lastSlot; i++ {
|
||||
info := s.slotInfos[i]
|
||||
info := s.SlotInfos[i]
|
||||
production := byIdentity[info.Leader]
|
||||
production[0]++
|
||||
if info.Block != nil {
|
||||
|
|
|
@ -49,6 +49,10 @@ func getEnvLogLevel() zapcore.Level {
|
|||
return zapcore.WarnLevel
|
||||
case "error":
|
||||
return zapcore.ErrorLevel
|
||||
case "panic":
|
||||
return zapcore.PanicLevel
|
||||
case "fatal":
|
||||
return zapcore.FatalLevel
|
||||
default:
|
||||
fmt.Printf("Unrecognised 'LOG_LEVEL' environment variable '%s', using 'info'\n", level)
|
||||
return zapcore.InfoLevel
|
||||
|
|
Loading…
Reference in New Issue