89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
package runtime
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
type fakeComponent struct {
|
|
mu sync.Mutex
|
|
running string
|
|
starts []string
|
|
stops int
|
|
fail map[string]error
|
|
}
|
|
|
|
func (f *fakeComponent) Start(_ context.Context, cfg Credentials) error {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
if f.running != "" {
|
|
return errors.New("already_running")
|
|
}
|
|
f.starts = append(f.starts, cfg.ClientID)
|
|
if err := f.fail[cfg.ClientID]; err != nil {
|
|
return err
|
|
}
|
|
f.running = cfg.ClientID
|
|
return nil
|
|
}
|
|
func (f *fakeComponent) Stop(context.Context) error {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
f.stops++
|
|
f.running = ""
|
|
return nil
|
|
}
|
|
|
|
func TestManagerSwitchesWithAtMostOneRuntime(t *testing.T) {
|
|
component := &fakeComponent{fail: map[string]error{}}
|
|
m := NewManager(component)
|
|
if err := m.Start(context.Background(), Credentials{ClientID: "old", Secret: "s1"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := m.Switch(context.Background(), Credentials{ClientID: "new", Secret: "s2"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if component.running != "new" || component.stops != 1 {
|
|
t.Fatalf("running=%q stops=%d", component.running, component.stops)
|
|
}
|
|
status := m.Status()
|
|
if status.ClientID != "new" || status.State != "running" {
|
|
t.Fatalf("status=%+v", status)
|
|
}
|
|
}
|
|
|
|
func TestManagerRollsBackWhenCandidateFails(t *testing.T) {
|
|
component := &fakeComponent{fail: map[string]error{"bad": errors.New("stream_failed")}}
|
|
m := NewManager(component)
|
|
if err := m.Start(context.Background(), Credentials{ClientID: "old", Secret: "s1"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := m.Switch(context.Background(), Credentials{ClientID: "bad", Secret: "s2"}); err == nil {
|
|
t.Fatal("expected failure")
|
|
}
|
|
if component.running != "old" {
|
|
t.Fatalf("running=%q starts=%v", component.running, component.starts)
|
|
}
|
|
status := m.Status()
|
|
if status.State != "running" || status.LastCode != "candidate_start_failed_rolled_back" {
|
|
t.Fatalf("status=%+v", status)
|
|
}
|
|
}
|
|
|
|
func TestManagerReportsRollbackFailure(t *testing.T) {
|
|
component := &fakeComponent{fail: map[string]error{"bad": errors.New("bad"), "old": nil}}
|
|
m := NewManager(component)
|
|
if err := m.Start(context.Background(), Credentials{ClientID: "old", Secret: "s1"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
component.fail["old"] = errors.New("old_failed")
|
|
if err := m.Switch(context.Background(), Credentials{ClientID: "bad", Secret: "s2"}); err == nil {
|
|
t.Fatal("expected failure")
|
|
}
|
|
if m.Status().State != "error" || m.Status().LastCode != "rollback_failed" {
|
|
t.Fatalf("status=%+v", m.Status())
|
|
}
|
|
}
|