Open pwdel opened 2 months ago
I'm starting this off as I'm working through an MR and thinking about this function:
// singleShareYesNoAllocator determines the outcome of a single bet.
// Logs a fatal error and exits if the input condition (len(bets) == 1) is not met.
func singleShareYesNoAllocator(bets []models.Bet) string {
if len(bets) != 1 {
log.Fatalf("singleShareYesNoAllocator: expected len(bets) = 1, got %d", len(bets))
}
// Return the outcome of the single bet
return bets[0].Outcome
}
Basically, singleShareYesNoAllocator()
should never have been called if there wasn't a scenario where there wasn't a single share, so this represents a fatal error because it's logically inconsistent.
From the standpoint of testing conditions which are going to have the FatalF function, I added additional logging;
// Logger interface for flexible logging.
type Logger interface {
Fatalf(format string, args ...interface{})
Printf(format string, args ...interface{}) // Add other log levels as needed.
}
// DefaultLogger provides default implementations for Logger methods.
type DefaultLogger struct{}
func (DefaultLogger) Fatalf(format string, args ...interface{}) {
log.Fatalf(format, args...)
}
func (DefaultLogger) Printf(format string, args ...interface{}) {
log.Printf(format, args...)
}
Which can be mocked in the same package with:
// MockLogger is a mock implementation of Logger for testing.
type MockLogger struct {
CalledFatalf bool
MessageFatalf string
CalledPrintf bool
MessagePrintf string
}
func (m *MockLogger) Fatalf(format string, args ...interface{}) {
m.CalledFatalf = true
m.MessageFatalf = fmt.Sprintf(format, args...)
panic(m.MessageFatalf) // Simulate Fatalf by panicking
}
func (m *MockLogger) Printf(format string, args ...interface{}) {
m.CalledPrintf = true
m.MessagePrintf = fmt.Sprintf(format, args...)
}
This means that for example, we can do:
singleShareDirection := singleShareYesNoAllocator(bets, logging.DefaultLogger{})
Which is defined as:
// singleShareYesNoAllocator determines the outcome of a single bet.
// Logs a fatal error and exits if the input condition (len(bets) == 1) is not met.
func singleShareYesNoAllocator(bets []models.Bet, logger logging.Logger) string {
if len(bets) != 1 {
logger.Fatalf("singleShareYesNoAllocator: expected len(bets) = 1, got %d", len(bets))
}
return bets[0].Outcome
}
And then to test fatal conditions, we can do the following, and pass the test if a fatal error was expected:
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
mockLogger := &logging.MockLogger{}
defer func() {
if r := recover(); r != nil {
if tc.expectFatal {
// Test passes if fatal was expected
t.Logf("[DEBUG] Test case %q: Expected fatal error occurred. Recovered: %v", tc.name, r)
} else {
// Test fails if panic was not expected
t.Errorf("Unexpected fatal error for case %q: %v", tc.name, r)
}
} else if tc.expectFatal {
// Test fails if no fatal occurred but was expected
t.Errorf("Expected fatal error for case %q, but none occurred", tc.name)
}
}()
// Call the function
result := singleShareYesNoAllocator(tc.bets, mockLogger)
// If no fatal error was expected, check the result
if !tc.expectFatal {
if result != tc.expected {
t.Errorf("Expected outcome %q, got %q", tc.expected, result)
}
}
})
}
Reference this conversation for when and why these features were first added.
We should decide: