-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
257 lines (231 loc) · 6.44 KB
/
main.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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
// create_test_sql generates a .sql file with the complete DB state of a cleosrv instance.
// This is used for upgrade tests to ensure the database is migrated correctly.
// The tool will start 'cleosrv' (provided as a path),
// execute certain GraphQL operations and then dump the resulting SQL into a file.
// This SQL file can later be used in a test to import it into a DB,
// run the migrations on that DB and verify that the resulting DB state is as expected.
// This script will rarely be needed (probably only after making a release) and all it does could
// be replicated manually.
package main
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"os/exec"
"path"
"strings"
"time"
"github.com/Khan/genqlient/graphql"
"github.com/cleodora-forecasting/cleodora/cleosrv/integrationtest"
)
func main() {
if err := do(); err != nil {
_, _ = fmt.Fprintf(
os.Stderr,
"%v\n",
err,
)
os.Exit(1)
}
}
func do() error {
args := os.Args
usage := `Usage:
create_test_sql CLEOSRV_BIN RESULT_DB
Where CLEOSRV_BIN is the path to a cleosrv binary and RESULT_DB is the path
where the final DB should be stored.
Example:
go run cleosrv/integrationtest/create_test_sql/main.go \
~/software/cleosrv \
cleosrv/integrationtest/testdata/TestUpdate_From_0_1_1/test.db
`
if len(args) != 3 {
return errors.New(usage)
}
cleosrv := os.Args[1]
resultDb := os.Args[2]
tDir, err := os.MkdirTemp("", "cleosrv")
if err != nil {
return err
}
dbPath := path.Join(tDir, "test.db")
cleosrvCmd := exec.Command(cleosrv, "--database", dbPath)
var cleosrvStdout, cleosrvStderr bytes.Buffer
cleosrvCmd.Stdout = &cleosrvStdout
cleosrvCmd.Stderr = &cleosrvStderr
err = cleosrvCmd.Start()
if err != nil {
return err
}
defer func() {
if err := cleosrvCmd.Process.Kill(); err != nil {
fmt.Printf("error stopping cleosrv: %v\n", err)
}
fmt.Println(strings.Repeat("=", 80))
fmt.Println("cleosrv stdout:")
fmt.Println(cleosrvStdout.String())
fmt.Println(strings.Repeat("=", 80))
fmt.Println("cleosrv stderr:")
fmt.Println(cleosrvStderr.String())
fmt.Println(strings.Repeat("=", 80))
}()
time.Sleep(10 * time.Second) // give time to start
c := graphql.NewClient("http://localhost:8080/query", nil)
version, err := getVersion(c)
if err != nil {
return fmt.Errorf("getting version: %w", err)
}
fmt.Println("cleosrv version:", version)
if err = executeQueries(c, version); err != nil {
return err
}
err = cleosrvCmd.Process.Kill()
if err != nil {
return fmt.Errorf("stopping cleosrvCmd: %w", err)
}
err = integrationtest.CopyFile(dbPath, resultDb)
if err != nil {
return err
}
return nil
}
func getVersion(c graphql.Client) (string, error) {
resp, err := integrationtest.GetMetadata(context.Background(), c)
if err != nil {
return "", err
}
return resp.Metadata.Version, nil
}
func executeQueries(c graphql.Client, version string) error {
now := time.Now().UTC()
f1 := integrationtest.NewForecast{
Title: fmt.Sprintf("Just a regular forecast (%v)", version),
Description: "",
Resolves: now.Add(30 * 24 * time.Hour),
}
e1 := integrationtest.NewEstimate{
Reason: "Just a hunch.",
Probabilities: []integrationtest.NewProbability{
{
Value: 20,
Outcome: integrationtest.NewOutcome{Text: "Yes"},
},
{
Value: 80,
Outcome: integrationtest.NewOutcome{Text: "No"},
},
},
}
resp, err := integrationtest.CreateForecast(context.Background(), c, f1, e1)
if err != nil {
return fmt.Errorf("create f1: %w", err)
}
if resp.CreateForecast.Id == "" {
return fmt.Errorf("unexpected response f1: %v", resp)
}
// Create another forecast with 'created', 'resolves' and 'closes' in the
// past.
// Also use some strange timezone for them.
newYork, err := time.LoadLocation("America/New_York")
if err != nil {
return fmt.Errorf("can't get TZ loc: %w", err)
}
f2 := integrationtest.NewForecast{
Title: fmt.Sprintf(
"Forecast with created/resolves/closes in the past (%v)",
version,
),
Description: "",
Created: timePointer(now.Add(-30 * 24 * time.Hour).In(newYork)),
Closes: timePointer(now.Add(-20 * 24 * time.Hour).In(newYork)),
Resolves: now.Add(-10 * 24 * time.Hour).In(newYork),
}
e2 := integrationtest.NewEstimate{
Reason: "Just a hunch.",
Probabilities: []integrationtest.NewProbability{
{
Value: 20,
Outcome: integrationtest.NewOutcome{Text: "Yes"},
},
{
Value: 80,
Outcome: integrationtest.NewOutcome{Text: "No"},
},
},
}
resp, err = integrationtest.CreateForecast(context.Background(), c, f2, e2)
if err != nil {
return fmt.Errorf("create f2: %w", err)
}
if resp.CreateForecast.Id == "" {
return fmt.Errorf("unexpected response f2: %v", resp)
}
f3 := integrationtest.NewForecast{
Title: fmt.Sprintf(
"Forecast with closes set to Go time null value and 3 outcomes (%v)",
version,
),
Description: "",
Closes: timePointer(time.Time{}),
Resolves: time.Now().UTC().Add(30 * 24 * time.Hour),
}
e3 := integrationtest.NewEstimate{
Reason: "Just a hunch.",
Probabilities: []integrationtest.NewProbability{
{
Value: 20,
Outcome: integrationtest.NewOutcome{Text: "Yes"},
},
{
Value: 30,
Outcome: integrationtest.NewOutcome{Text: "No"},
},
{
Value: 50,
Outcome: integrationtest.NewOutcome{Text: "Maybe"},
},
},
}
resp, err = integrationtest.CreateForecast(context.Background(), c, f3, e3)
if err != nil {
return fmt.Errorf("create f3: %w", err)
}
if resp.CreateForecast.Id == "" {
return fmt.Errorf("unexpected response f3: %v", resp)
}
// Resolve the forecast f3
f3Id := resp.CreateForecast.Id
yesOutcomeId := ""
for _, p := range resp.CreateForecast.Estimates[0].Probabilities {
if p.Outcome.Text == "Yes" {
yesOutcomeId = p.Outcome.Id
}
}
if yesOutcomeId == "" {
return fmt.Errorf("could not find outcome 'Yes': %v", resp)
}
// Backport this script to 0.2.0 then overwrite the DB
resolutionResolved := integrationtest.ResolutionResolved
resolveResp, err := integrationtest.ResolveForecast(
context.Background(),
c,
f3Id,
&yesOutcomeId,
&resolutionResolved,
)
if err != nil {
return fmt.Errorf("resolve f3 err: %w", err)
}
if resolveResp.ResolveForecast.Resolution != integrationtest.ResolutionResolved {
return fmt.Errorf("resolution is not RESOLVED: %v", resolveResp)
}
if resolveResp.ResolveForecast.Id != f3Id {
return fmt.Errorf("the IDs don't match: %v", resolveResp)
}
return nil
}
func timePointer(t time.Time) *time.Time {
return &t
}