Commit 06edba14 authored by refcell's avatar refcell Committed by GitHub

fix(op-dispute-mon): forecast metrics and test enhancements (#9468)

parent 0584913a
......@@ -34,7 +34,8 @@ func TestDetector_Detect(t *testing.T) {
t.Run("CheckAgreementFails", func(t *testing.T) {
detector, metrics, creator, rollup, _ := setupDetectorTest(t)
rollup.err = errors.New("boom")
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
creator.caller.rootClaim = []common.Hash{{}}
detector.Detect(context.Background(), []types.GameMetadata{{}})
metrics.Equals(t, 1, 0, 0) // Status should still be metriced here!
metrics.Mapped(t, map[string]int{})
......@@ -42,7 +43,8 @@ func TestDetector_Detect(t *testing.T) {
t.Run("SingleGame", func(t *testing.T) {
detector, metrics, creator, _, _ := setupDetectorTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
creator.caller.rootClaim = []common.Hash{{}}
detector.Detect(context.Background(), []types.GameMetadata{{}})
metrics.Equals(t, 1, 0, 0)
metrics.Mapped(t, map[string]int{"in_progress": 1})
......@@ -50,7 +52,12 @@ func TestDetector_Detect(t *testing.T) {
t.Run("MultipleGames", func(t *testing.T) {
detector, metrics, creator, _, _ := setupDetectorTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
creator.caller.status = []types.GameStatus{
types.GameStatusInProgress,
types.GameStatusInProgress,
types.GameStatusInProgress,
}
creator.caller.rootClaim = []common.Hash{{}, {}, {}}
detector.Detect(context.Background(), []types.GameMetadata{{}, {}, {}})
metrics.Equals(t, 3, 0, 0)
metrics.Mapped(t, map[string]int{"in_progress": 3})
......@@ -128,13 +135,16 @@ func TestDetector_FetchGameMetadata(t *testing.T) {
t.Run("GetGameMetadataFails", func(t *testing.T) {
detector, _, creator, _, _ := setupDetectorTest(t)
creator.caller = &mockGameCaller{err: errors.New("boom")}
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
creator.caller.rootClaim = []common.Hash{{}}
_, _, _, err := detector.fetchGameMetadata(context.Background(), types.GameMetadata{})
require.Error(t, err)
})
t.Run("Success", func(t *testing.T) {
detector, _, creator, _, _ := setupDetectorTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
creator.caller.rootClaim = []common.Hash{{}}
_, _, status, err := detector.fetchGameMetadata(context.Background(), types.GameMetadata{})
require.NoError(t, err)
require.Equal(t, types.GameStatusInProgress, status)
......@@ -266,27 +276,29 @@ func (m *mockGameCallerCreator) CreateContract(game types.GameMetadata) (GameCal
type mockGameCaller struct {
calls int
claimsCalls int
claims []faultTypes.Claim
status types.GameStatus
rootClaim common.Hash
claims [][]faultTypes.Claim
status []types.GameStatus
rootClaim []common.Hash
err error
claimsErr error
}
func (m *mockGameCaller) GetGameMetadata(ctx context.Context) (uint64, common.Hash, types.GameStatus, error) {
idx := m.calls
m.calls++
if m.err != nil {
return 0, m.rootClaim, m.status, m.err
return 0, m.rootClaim[idx], m.status[idx], m.err
}
return 0, m.rootClaim, m.status, nil
return 0, m.rootClaim[idx], m.status[idx], nil
}
func (m *mockGameCaller) GetAllClaims(ctx context.Context) ([]faultTypes.Claim, error) {
idx := m.claimsCalls
m.claimsCalls++
if m.claimsErr != nil {
return nil, m.claimsErr
}
return m.claims, nil
return m.claims[idx], nil
}
type mockDetectorMetricer struct {
......
......@@ -18,32 +18,44 @@ var (
ErrRootAgreement = errors.New("failed to check root agreement")
)
type ForecastMetrics interface {
RecordGameAgreement(status string, count int)
}
type forecast struct {
logger log.Logger
// TODO(client-pod#536): Add forecast metrics.
// These should only fire if a game is in progress.
// otherwise, the detector should record the game status.
logger log.Logger
metrics ForecastMetrics
creator GameCallerCreator
validator OutputValidator
}
func newForecast(logger log.Logger, creator GameCallerCreator, validator OutputValidator) *forecast {
func newForecast(logger log.Logger, metrics ForecastMetrics, creator GameCallerCreator, validator OutputValidator) *forecast {
return &forecast{
logger: logger,
metrics: metrics,
creator: creator,
validator: validator,
}
}
func (f *forecast) Forecast(ctx context.Context, games []types.GameMetadata) {
batch := forecastBatch{}
for _, game := range games {
if err := f.forecastGame(ctx, game); err != nil {
if err := f.forecastGame(ctx, game, &batch); err != nil {
f.logger.Error("Failed to forecast game", "err", err)
}
}
f.recordBatch(batch)
}
func (f *forecast) recordBatch(batch forecastBatch) {
f.metrics.RecordGameAgreement("agree_challenger_ahead", batch.AgreeChallengerAhead)
f.metrics.RecordGameAgreement("disagree_challenger_ahead", batch.DisagreeChallengerAhead)
f.metrics.RecordGameAgreement("agree_defender_ahead", batch.AgreeDefenderAhead)
f.metrics.RecordGameAgreement("disagree_defender_ahead", batch.DisagreeDefenderAhead)
}
func (f *forecast) forecastGame(ctx context.Context, game types.GameMetadata) error {
func (f *forecast) forecastGame(ctx context.Context, game types.GameMetadata, metrics *forecastBatch) error {
loader, err := f.creator.CreateContract(game)
if err != nil {
return fmt.Errorf("%w: %w", ErrContractCreation, err)
......@@ -80,15 +92,19 @@ func (f *forecast) forecastGame(ctx context.Context, game types.GameMetadata) er
if agreement {
// If we agree with the output root proposal, the Defender should win, defending that claim.
if status == types.GameStatusChallengerWon {
metrics.AgreeChallengerAhead++
f.logger.Warn("Forecasting unexpected game result", "status", status, "game", game, "rootClaim", rootClaim, "expected", expected)
} else {
metrics.AgreeDefenderAhead++
f.logger.Debug("Forecasting expected game result", "status", status, "game", game, "rootClaim", rootClaim, "expected", expected)
}
} else {
// If we disagree with the output root proposal, the Challenger should win, challenging that claim.
if status == types.GameStatusDefenderWon {
metrics.DisagreeDefenderAhead++
f.logger.Warn("Forecasting unexpected game result", "status", status, "game", game, "rootClaim", rootClaim, "expected", expected)
} else {
metrics.DisagreeChallengerAhead++
f.logger.Debug("Forecasting expected game result", "status", status, "game", game, "rootClaim", rootClaim, "expected", expected)
}
}
......
......@@ -6,6 +6,7 @@ import (
"fmt"
"testing"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
......@@ -14,7 +15,7 @@ import (
)
var (
failedForecaseLog = "Failed to forecast game"
failedForecastLog = "Failed to forecast game"
expectedInProgressLog = "Game is not in progress, skipping forecast"
unexpectedResultLog = "Forecasting unexpected game result"
expectedResultLog = "Forecasting expected game result"
......@@ -24,19 +25,19 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
t.Parallel()
t.Run("NoGames", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
forecast, _, creator, rollup, logs := setupForecastTest(t)
forecast.Forecast(context.Background(), []types.GameMetadata{})
require.Equal(t, 0, creator.calls)
require.Equal(t, 0, creator.caller.calls)
require.Equal(t, 0, creator.caller.claimsCalls)
require.Equal(t, 0, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
})
t.Run("ContractCreationFails", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.err = errors.New("boom")
forecast.Forecast(context.Background(), []types.GameMetadata{{}})
require.Equal(t, 1, creator.calls)
......@@ -44,7 +45,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
require.Equal(t, 0, creator.caller.claimsCalls)
require.Equal(t, 0, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
l := logs.FindLog(levelFilter, messageFilter)
require.NotNil(t, l)
err := l.AttrValue("err")
......@@ -53,16 +54,17 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
})
t.Run("MetadataFetchFails", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller.err = errors.New("boom")
creator.caller.rootClaim = []common.Hash{mockRootClaim}
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
forecast.Forecast(context.Background(), []types.GameMetadata{{}})
require.Equal(t, 1, creator.calls)
require.Equal(t, 1, creator.caller.calls)
require.Equal(t, 0, creator.caller.claimsCalls)
require.Equal(t, 0, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
l := logs.FindLog(levelFilter, messageFilter)
require.NotNil(t, l)
err := l.AttrValue("err")
......@@ -71,16 +73,17 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
})
t.Run("ClaimsFetchFails", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller.claimsErr = errors.New("boom")
creator.caller.rootClaim = []common.Hash{mockRootClaim}
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
forecast.Forecast(context.Background(), []types.GameMetadata{{}})
require.Equal(t, 1, creator.calls)
require.Equal(t, 1, creator.caller.calls)
require.Equal(t, 1, creator.caller.claimsCalls)
require.Equal(t, 0, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
l := logs.FindLog(levelFilter, messageFilter)
require.NotNil(t, l)
err := l.AttrValue("err")
......@@ -89,15 +92,18 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
})
t.Run("RollupFetchFails", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
forecast, _, creator, rollup, logs := setupForecastTest(t)
rollup.err = errors.New("boom")
creator.caller.claims = [][]faultTypes.Claim{createDeepClaimList()[:1]}
creator.caller.rootClaim = []common.Hash{mockRootClaim}
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
forecast.Forecast(context.Background(), []types.GameMetadata{{}})
require.Equal(t, 1, creator.calls)
require.Equal(t, 1, creator.caller.calls)
require.Equal(t, 1, creator.caller.claimsCalls)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
l := logs.FindLog(levelFilter, messageFilter)
require.NotNil(t, l)
err := l.AttrValue("err")
......@@ -106,9 +112,9 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
})
t.Run("ChallengerWonGameSkipped", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusChallengerWon}
creator.caller.claims = createDeepClaimList()[:1]
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller.status = []types.GameStatus{types.GameStatusChallengerWon}
creator.caller.claims = [][]faultTypes.Claim{createDeepClaimList()[:1]}
expectedGame := types.GameMetadata{}
forecast.Forecast(context.Background(), []types.GameMetadata{expectedGame})
require.Equal(t, 1, creator.calls)
......@@ -116,7 +122,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
require.Equal(t, 0, creator.caller.claimsCalls)
require.Equal(t, 0, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedInProgressLog)
......@@ -127,9 +133,9 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
})
t.Run("DefenderWonGameSkipped", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusDefenderWon}
creator.caller.claims = createDeepClaimList()[:1]
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller.status = []types.GameStatus{types.GameStatusDefenderWon}
creator.caller.claims = [][]faultTypes.Claim{createDeepClaimList()[:1]}
expectedGame := types.GameMetadata{}
forecast.Forecast(context.Background(), []types.GameMetadata{expectedGame})
require.Equal(t, 1, creator.calls)
......@@ -137,7 +143,7 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
require.Equal(t, 0, creator.caller.claimsCalls)
require.Equal(t, 0, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedInProgressLog)
......@@ -148,16 +154,16 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
})
t.Run("SingleGame", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
creator.caller.claims = createDeepClaimList()[:1]
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
creator.caller.claims = [][]faultTypes.Claim{createDeepClaimList()[:1]}
forecast.Forecast(context.Background(), []types.GameMetadata{{}})
require.Equal(t, 1, creator.calls)
require.Equal(t, 1, creator.caller.calls)
require.Equal(t, 1, creator.caller.claimsCalls)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedInProgressLog)
......@@ -165,16 +171,17 @@ func TestForecast_Forecast_BasicTests(t *testing.T) {
})
t.Run("MultipleGames", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller.claims = createDeepClaimList()[:1]
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller.status = []types.GameStatus{types.GameStatusInProgress, types.GameStatusInProgress, types.GameStatusInProgress}
creator.caller.claims = [][]faultTypes.Claim{createDeepClaimList()[:1], createDeepClaimList()[:1], createDeepClaimList()[:1]}
creator.caller.rootClaim = []common.Hash{{}, {}, {}}
forecast.Forecast(context.Background(), []types.GameMetadata{{}, {}, {}})
require.Equal(t, 3, creator.calls)
require.Equal(t, 3, creator.caller.calls)
require.Equal(t, 3, creator.caller.claimsCalls)
require.Equal(t, 3, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedInProgressLog)
......@@ -186,65 +193,66 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
t.Parallel()
t.Run("AgreeDefenderWins", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
creator.caller.claims = createDeepClaimList()[:1]
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
creator.caller.claims = [][]faultTypes.Claim{createDeepClaimList()[:1]}
forecast.Forecast(context.Background(), []types.GameMetadata{{}})
require.Equal(t, 1, creator.calls)
require.Equal(t, 1, creator.caller.calls)
require.Equal(t, 1, creator.caller.claimsCalls)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedInProgressLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelWarn)
messageFilter = testlog.NewMessageFilter(unexpectedResultLog)
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedResultLog)
l := logs.FindLog(levelFilter, messageFilter)
require.NotNil(t, l)
require.Equal(t, common.Hash{}, l.AttrValue("rootClaim"))
require.Equal(t, mockRootClaim, l.AttrValue("rootClaim"))
require.Equal(t, mockRootClaim, l.AttrValue("expected"))
require.Equal(t, types.GameStatusDefenderWon, l.AttrValue("status"))
})
t.Run("AgreeChallengerWins", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
creator.caller.claims = createDeepClaimList()[:2]
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller.status = []types.GameStatus{types.GameStatusInProgress}
creator.caller.claims = [][]faultTypes.Claim{createDeepClaimList()[:2]}
creator.caller.rootClaim = []common.Hash{mockRootClaim}
forecast.Forecast(context.Background(), []types.GameMetadata{{}})
require.Equal(t, 1, creator.calls)
require.Equal(t, 1, creator.caller.calls)
require.Equal(t, 1, creator.caller.claimsCalls)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedInProgressLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedResultLog)
levelFilter = testlog.NewLevelFilter(log.LevelWarn)
messageFilter = testlog.NewMessageFilter(unexpectedResultLog)
l := logs.FindLog(levelFilter, messageFilter)
require.NotNil(t, l)
require.Equal(t, common.Hash{}, l.AttrValue("rootClaim"))
require.Equal(t, mockRootClaim, l.AttrValue("rootClaim"))
require.Equal(t, mockRootClaim, l.AttrValue("expected"))
require.Equal(t, types.GameStatusChallengerWon, l.AttrValue("status"))
})
t.Run("DisagreeChallengerWins", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
creator.caller.rootClaim = common.Hash{}
creator.caller.claims = createDeepClaimList()[:2]
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: []types.GameStatus{types.GameStatusInProgress}}
creator.caller.rootClaim = []common.Hash{{}}
creator.caller.claims = [][]faultTypes.Claim{createDeepClaimList()[:2]}
forecast.Forecast(context.Background(), []types.GameMetadata{{}})
require.Equal(t, 1, creator.calls)
require.Equal(t, 1, creator.caller.calls)
require.Equal(t, 1, creator.caller.claimsCalls)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedInProgressLog)
......@@ -259,17 +267,17 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
})
t.Run("DisagreeDefenderWins", func(t *testing.T) {
forecast, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: types.GameStatusInProgress}
creator.caller.rootClaim = common.Hash{}
creator.caller.claims = createDeepClaimList()[:1]
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller = &mockGameCaller{status: []types.GameStatus{types.GameStatusInProgress}}
creator.caller.rootClaim = []common.Hash{{}}
creator.caller.claims = [][]faultTypes.Claim{createDeepClaimList()[:1]}
forecast.Forecast(context.Background(), []types.GameMetadata{{}})
require.Equal(t, 1, creator.calls)
require.Equal(t, 1, creator.caller.calls)
require.Equal(t, 1, creator.caller.claimsCalls)
require.Equal(t, 1, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecaseLog)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedInProgressLog)
......@@ -284,10 +292,75 @@ func TestForecast_Forecast_EndLogs(t *testing.T) {
})
}
func setupForecastTest(t *testing.T) (*forecast, *mockGameCallerCreator, *stubOutputValidator, *testlog.CapturingHandler) {
func TestForecast_Forecast_MultipleGames(t *testing.T) {
forecast, _, creator, rollup, logs := setupForecastTest(t)
creator.caller.status = []types.GameStatus{
types.GameStatusChallengerWon,
types.GameStatusInProgress,
types.GameStatusInProgress,
types.GameStatusDefenderWon,
types.GameStatusInProgress,
types.GameStatusInProgress,
types.GameStatusDefenderWon,
types.GameStatusChallengerWon,
types.GameStatusChallengerWon,
}
creator.caller.claims = [][]faultTypes.Claim{
createDeepClaimList()[:1],
createDeepClaimList()[:2],
createDeepClaimList()[:2],
createDeepClaimList()[:1],
}
creator.caller.rootClaim = []common.Hash{
{},
{},
mockRootClaim,
{},
{},
mockRootClaim,
{},
{},
{},
}
games := make([]types.GameMetadata, 9)
forecast.Forecast(context.Background(), games)
require.Equal(t, 9, creator.calls)
require.Equal(t, 9, creator.caller.calls)
require.Equal(t, 4, creator.caller.claimsCalls)
require.Equal(t, 4, rollup.calls)
levelFilter := testlog.NewLevelFilter(log.LevelError)
messageFilter := testlog.NewMessageFilter(failedForecastLog)
require.Nil(t, logs.FindLog(levelFilter, messageFilter))
levelFilter = testlog.NewLevelFilter(log.LevelDebug)
messageFilter = testlog.NewMessageFilter(expectedInProgressLog)
require.Len(t, logs.FindLogs(levelFilter, messageFilter), 5)
}
func setupForecastTest(t *testing.T) (*forecast, *mockForecastMetrics, *mockGameCallerCreator, *stubOutputValidator, *testlog.CapturingHandler) {
logger, capturedLogs := testlog.CaptureLogger(t, log.LvlDebug)
validator := &stubOutputValidator{}
caller := &mockGameCaller{rootClaim: mockRootClaim}
caller := &mockGameCaller{rootClaim: []common.Hash{mockRootClaim}}
creator := &mockGameCallerCreator{caller: caller}
return newForecast(logger, creator, validator), creator, validator, capturedLogs
metrics := &mockForecastMetrics{}
return newForecast(logger, metrics, creator, validator), metrics, creator, validator, capturedLogs
}
type mockForecastMetrics struct {
agreeDefenderAhead int
disagreeDefenderAhead int
agreeChallengerAhead int
disagreeChallengerAhead int
}
func (m *mockForecastMetrics) RecordGameAgreement(status string, count int) {
switch status {
case "agree_defender_ahead":
m.agreeDefenderAhead = count
case "disagree_defender_ahead":
m.disagreeDefenderAhead = count
case "agree_challenger_ahead":
m.agreeChallengerAhead = count
case "disagree_challenger_ahead":
m.disagreeChallengerAhead = count
}
}
......@@ -79,11 +79,14 @@ func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error
if err := s.initOutputRollupClient(ctx, cfg); err != nil {
return fmt.Errorf("failed to init rollup client: %w", err)
}
s.initOutputValidator()
s.initGameCallerCreator()
s.initOutputValidator() // Must be called before initForecast
s.initGameCallerCreator() // Must be called before initForecast
s.initForecast(cfg)
s.initDetector()
s.initMonitor(ctx, cfg)
s.initMonitor(ctx, cfg) // Monitor must be initialized last
s.metrics.RecordInfo(version.SimpleWithMeta)
s.metrics.RecordUp()
......@@ -100,7 +103,7 @@ func (s *Service) initGameCallerCreator() {
}
func (s *Service) initForecast(cfg *config.Config) {
s.forecast = newForecast(s.logger, s.game, s.validator)
s.forecast = newForecast(s.logger, s.metrics, s.game, s.validator)
}
func (s *Service) initDetector() {
......
......@@ -19,6 +19,13 @@ func (s *statusBatch) Add(status types.GameStatus) {
}
}
type forecastBatch struct {
AgreeDefenderAhead int
DisagreeDefenderAhead int
AgreeChallengerAhead int
DisagreeChallengerAhead int
}
type detectionBatch struct {
inProgress int
agreeDefenderWins int
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment