mirror of
https://github.com/blindlobstar/go-interview-problems
synced 2025-04-25 19:25:14 +00:00
161 lines
3.4 KiB
Go
161 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Response represents a mock response with optional error and delay
|
|
type Response struct {
|
|
Value string
|
|
Error error
|
|
Delay time.Duration
|
|
}
|
|
|
|
// MockGetter implements the Getter interface for testing
|
|
type MockGetter struct {
|
|
Responses map[string]map[string]Response
|
|
}
|
|
|
|
func NewMockGetter(responses map[string]map[string]Response) *MockGetter {
|
|
return &MockGetter{
|
|
Responses: responses,
|
|
}
|
|
}
|
|
|
|
func (m *MockGetter) Get(ctx context.Context, address, key string) (string, error) {
|
|
if responses, exists := m.Responses[address]; exists {
|
|
if resp, keyExists := responses[key]; keyExists {
|
|
// Simulate delay if set
|
|
if resp.Delay > 0 {
|
|
select {
|
|
case <-time.After(resp.Delay):
|
|
case <-ctx.Done():
|
|
return "", ctx.Err()
|
|
}
|
|
}
|
|
|
|
// Return the error if set
|
|
if resp.Error != nil {
|
|
return "", resp.Error
|
|
}
|
|
|
|
// Return the response value
|
|
return resp.Value, nil
|
|
}
|
|
}
|
|
|
|
return "", errors.New("key not found")
|
|
}
|
|
|
|
func TestGet(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
responses map[string]map[string]Response
|
|
addresses []string
|
|
key string
|
|
ttl time.Duration
|
|
wantValue string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "first address fails second succeeds",
|
|
responses: map[string]map[string]Response{
|
|
"addr1": {
|
|
"key1": {Error: errors.New("connection error")},
|
|
},
|
|
"addr2": {
|
|
"key1": {Value: "value2"},
|
|
},
|
|
},
|
|
addresses: []string{"addr1", "addr2"},
|
|
key: "key1",
|
|
wantValue: "value2",
|
|
ttl: 1 * time.Millisecond,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "all addresses fail",
|
|
responses: map[string]map[string]Response{
|
|
"addr1": {
|
|
"key1": {Error: errors.New("error 1")},
|
|
},
|
|
"addr2": {
|
|
"key1": {Error: errors.New("error 2")},
|
|
},
|
|
},
|
|
addresses: []string{"addr1", "addr2"},
|
|
key: "key1",
|
|
ttl: 1 * time.Millisecond,
|
|
wantValue: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "context cancellation",
|
|
responses: map[string]map[string]Response{
|
|
"addr1": {
|
|
"key1": {Value: "value1", Delay: 200 * time.Millisecond},
|
|
},
|
|
},
|
|
addresses: []string{"addr1"},
|
|
key: "key1",
|
|
ttl: 50 * time.Millisecond,
|
|
wantValue: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "fast address wins over slow",
|
|
responses: map[string]map[string]Response{
|
|
"addr1": {
|
|
"key1": {Value: "value1", Delay: 200 * time.Millisecond},
|
|
},
|
|
"addr2": {
|
|
"key1": {Value: "value2", Delay: 50 * time.Millisecond},
|
|
},
|
|
},
|
|
addresses: []string{"addr1", "addr2"},
|
|
key: "key1",
|
|
ttl: 100 * time.Millisecond,
|
|
wantValue: "value2",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "empty address list",
|
|
responses: map[string]map[string]Response{},
|
|
addresses: []string{},
|
|
key: "key1",
|
|
ttl: 50 * time.Millisecond,
|
|
wantValue: "",
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mock := NewMockGetter(tt.responses)
|
|
|
|
var getter Getter
|
|
if tt.name == "nil getter" {
|
|
getter = nil
|
|
} else {
|
|
getter = mock
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), tt.ttl)
|
|
got, err := Get(ctx, getter, tt.addresses, tt.key)
|
|
cancel()
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
if got != tt.wantValue {
|
|
t.Errorf("Get() = %v, want %v", got, tt.wantValue)
|
|
}
|
|
})
|
|
}
|
|
}
|