-
Notifications
You must be signed in to change notification settings - Fork 2
/
fixtures.go
130 lines (114 loc) · 2.95 KB
/
fixtures.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package fixtures
import (
"context"
"fmt"
"reflect"
"sync"
"github.com/docker/docker/pkg/namesgenerator"
"go.uber.org/zap"
)
var wg sync.WaitGroup
type FixturesOpt func(*Fixtures)
func NewFixtures(opts ...FixturesOpt) *Fixtures {
f := &Fixtures{}
for _, opt := range opts {
opt(f)
}
if f.log == nil {
f.log = logger()
}
return f
}
func FixturesLogger(logger *zap.Logger) FixturesOpt {
return func(f *Fixtures) {
f.log = logger
}
}
type Fixtures struct {
log *zap.Logger
store map[string]Fixture
order []string
}
func (f *Fixtures) Add(ctx context.Context, fixtures ...Fixture) error {
for _, fix := range fixtures {
if err := f.AddByName(ctx, namesgenerator.GetRandomName(0), fix); err != nil {
return err
}
}
return nil
}
func (f *Fixtures) AddByName(ctx context.Context, name string, fixture Fixture) error {
if f.store == nil {
f.order = []string{}
f.store = map[string]Fixture{}
}
f.order = append(f.order, name)
f.store[name] = fixture
err := fixture.SetUp(ctx)
if err != nil {
return fmt.Errorf("failed to setup fixture '%v': %w", name, err)
}
f.log.Debug("setup", zap.String("type", fmt.Sprint(reflect.TypeOf(fixture).Elem())), zap.String("name", name))
return err
}
func (f *Fixtures) Get(name string) Fixture {
return f.store[name]
}
func (f *Fixtures) SetUp(ctx context.Context) error {
var err error
for name, fixture := range f.store {
err = fixture.SetUp(ctx)
if err != nil {
return fmt.Errorf("failed to setup fixture '%v': %w", name, err)
}
f.log.Debug("setup", zap.String("type", fmt.Sprint(reflect.TypeOf(fixture).Elem())), zap.String("name", name))
}
return err
}
func (f *Fixtures) TearDown(ctx context.Context) error {
fixtureNames := []string{}
for _, name := range f.order {
fixtureNames = append([]string{name}, fixtureNames...)
}
var firstErr error
for _, name := range fixtureNames {
fixture := f.Get(name)
err := fixture.TearDown(ctx)
if err != nil {
f.log.Warn("failed to teardown fixture", zap.String("fixture", name), zap.Error(err))
if firstErr == nil {
firstErr = err
}
}
f.log.Debug("teardown", zap.String("type", fmt.Sprint(reflect.TypeOf(fixture).Elem())), zap.String("name", name))
}
wg.Wait()
return firstErr
}
// RecoverTearDown returns a deferrable function that will teardown in the event of a panic.
func (f *Fixtures) RecoverTearDown(ctx context.Context) {
if r := recover(); r != nil {
if err := f.TearDown(ctx); err != nil {
f.log.Warn("failed to tear down", zap.Error(err))
}
panic(r)
}
}
// Docker() returns the first Docker fixture. If none exists, panic.
func (f *Fixtures) Docker() *Docker {
for _, x := range f.store {
if val, ok := x.(*Docker); ok {
return val
}
}
panic("no docker fixture found")
}
// Postgres() returns the first Postgres fixture. If none exists, panic.
func (f *Fixtures) Postgres() *Postgres {
for _, x := range f.store {
if val, ok := x.(*Postgres); ok {
return val
}
}
panic("no postgres fixture found")
}