-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
orm.go
174 lines (150 loc) · 6.31 KB
/
orm.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package heavyweight
// The heavyweight package contains cltest items that are costly and you should
// think **real carefully** before using in your tests.
import (
"database/sql"
"errors"
"fmt"
"math/rand"
"net/url"
"os"
"path"
"runtime"
"testing"
"github.com/smartcontractkit/sqlx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/guregu/null.v4"
"github.com/smartcontractkit/chainlink/core/cmd"
"github.com/smartcontractkit/chainlink/core/config"
"github.com/smartcontractkit/chainlink/core/internal/testutils"
"github.com/smartcontractkit/chainlink/core/internal/testutils/configtest"
configtest2 "github.com/smartcontractkit/chainlink/core/internal/testutils/configtest/v2"
"github.com/smartcontractkit/chainlink/core/services/chainlink"
"github.com/smartcontractkit/chainlink/core/services/pg"
"github.com/smartcontractkit/chainlink/core/store/dialects"
"github.com/smartcontractkit/chainlink/core/store/models"
)
// FullTestDBEmpty creates an empty DB (without migrations).
// Deprecated: https://app.shortcut.com/chainlinklabs/story/33622/remove-legacy-config
func FullTestDBEmpty(t *testing.T, name string) (*configtest.TestGeneralConfig, *sqlx.DB) {
return prepareFullTestDB(t, name, true, false)
}
// Deprecated: https://app.shortcut.com/chainlinklabs/story/33622/remove-legacy-config
func prepareFullTestDB(t *testing.T, name string, empty bool, loadFixtures bool) (*configtest.TestGeneralConfig, *sqlx.DB) {
testutils.SkipShort(t, "FullTestDB")
if empty && loadFixtures {
t.Fatal("could not load fixtures into an empty DB")
}
gcfg := configtest.NewTestGeneralConfig(t)
gcfg.Overrides.Dialect = dialects.Postgres
require.NoError(t, os.MkdirAll(gcfg.RootDir(), 0700))
migrationTestDBURL, err := dropAndCreateThrowawayTestDB(gcfg.DatabaseURL(), name, empty)
require.NoError(t, err)
db, err := pg.NewConnection(migrationTestDBURL, dialects.Postgres, gcfg)
require.NoError(t, err)
t.Cleanup(func() {
assert.NoError(t, db.Close())
os.RemoveAll(gcfg.RootDir())
})
gcfg.Overrides.DatabaseURL = null.StringFrom(migrationTestDBURL)
if loadFixtures {
_, filename, _, ok := runtime.Caller(1)
if !ok {
t.Fatal("could not get runtime.Caller(1)")
}
filepath := path.Join(path.Dir(filename), "../../../store/fixtures/fixtures.sql")
fixturesSQL, err := os.ReadFile(filepath)
require.NoError(t, err)
_, err = db.Exec(string(fixturesSQL))
require.NoError(t, err)
}
return gcfg, db
}
// FullTestDBV2 creates a pristine DB which runs in a separate database than the normal
// unit tests, so you can do things like use other Postgres connection types with it.
func FullTestDBV2(t *testing.T, name string, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (config.GeneralConfig, *sqlx.DB) {
return prepareFullTestDBV2(t, name, false, true, overrideFn)
}
// FullTestDBNoFixturesV2 is the same as FullTestDB, but it does not load fixtures.
func FullTestDBNoFixturesV2(t *testing.T, name string, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (config.GeneralConfig, *sqlx.DB) {
return prepareFullTestDBV2(t, name, false, false, overrideFn)
}
// FullTestDBEmptyV2 creates an empty DB (without migrations).
func FullTestDBEmptyV2(t *testing.T, name string, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (config.GeneralConfig, *sqlx.DB) {
return prepareFullTestDBV2(t, name, true, false, overrideFn)
}
func prepareFullTestDBV2(t *testing.T, name string, empty bool, loadFixtures bool, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (config.GeneralConfig, *sqlx.DB) {
testutils.SkipShort(t, "FullTestDB")
if empty && loadFixtures {
t.Fatal("could not load fixtures into an empty DB")
}
gcfg := configtest2.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) {
c.Database.Dialect = dialects.Postgres
if overrideFn != nil {
overrideFn(c, s)
}
})
require.NoError(t, os.MkdirAll(gcfg.RootDir(), 0700))
name = fmt.Sprintf("%s_%x", name, rand.Intn(0xFFF)) // to avoid name collisions
migrationTestDBURL, err := dropAndCreateThrowawayTestDB(gcfg.DatabaseURL(), name, empty)
require.NoError(t, err)
db, err := pg.NewConnection(migrationTestDBURL, dialects.Postgres, gcfg)
require.NoError(t, err)
t.Cleanup(func() {
assert.NoError(t, db.Close())
os.RemoveAll(gcfg.RootDir())
})
gcfg = configtest2.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) {
c.Database.Dialect = dialects.Postgres
s.Database.URL = models.MustSecretURL(migrationTestDBURL)
if overrideFn != nil {
overrideFn(c, s)
}
})
if loadFixtures {
_, filename, _, ok := runtime.Caller(1)
if !ok {
t.Fatal("could not get runtime.Caller(1)")
}
filepath := path.Join(path.Dir(filename), "../../../store/fixtures/fixtures.sql")
fixturesSQL, err := os.ReadFile(filepath)
require.NoError(t, err)
_, err = db.Exec(string(fixturesSQL))
require.NoError(t, err)
}
return gcfg, db
}
func dropAndCreateThrowawayTestDB(parsed url.URL, postfix string, empty bool) (string, error) {
if parsed.Path == "" {
return "", errors.New("path missing from database URL")
}
// Match the naming schema that our dangling DB cleanup methods expect
dbname := cmd.TestDBNamePrefix + postfix
if l := len(dbname); l > 63 {
return "", fmt.Errorf("dbname %v too long (%d), max is 63 bytes. Try a shorter postfix", dbname, l)
}
// Cannot drop test database if we are connected to it, so we must connect
// to a different one. 'postgres' should be present on all postgres installations
parsed.Path = "/postgres"
db, err := sql.Open(string(dialects.Postgres), parsed.String())
if err != nil {
return "", fmt.Errorf("In order to drop the test database, we need to connect to a separate database"+
" called 'postgres'. But we are unable to open 'postgres' database: %+v\n", err)
}
defer db.Close()
_, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", dbname))
if err != nil {
return "", fmt.Errorf("unable to drop postgres migrations test database: %v", err)
}
if empty {
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", dbname))
} else {
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s WITH TEMPLATE %s", dbname, cmd.PristineDBName))
}
if err != nil {
return "", fmt.Errorf("unable to create postgres test database with name '%s': %v", dbname, err)
}
parsed.Path = fmt.Sprintf("/%s", dbname)
return parsed.String(), nil
}