github.com/influxdata/influxdb/v2@v2.7.6/influxql/v1tests/server_helpers.go (about) 1 package v1tests 2 3 import ( 4 "context" 5 "encoding/json" 6 "io" 7 "net/http" 8 "net/url" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/influxdata/influxdb/v2" 14 "github.com/influxdata/influxdb/v2/cmd/influxd/launcher" 15 icontext "github.com/influxdata/influxdb/v2/context" 16 "github.com/influxdata/influxdb/v2/kit/platform" 17 "github.com/influxdata/influxdb/v2/tests" 18 "github.com/influxdata/influxdb/v2/tests/pipeline" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 "go.uber.org/zap/zapcore" 22 ) 23 24 func OpenServer(t *testing.T, extra ...launcher.OptSetter) *tests.DefaultPipeline { 25 t.Helper() 26 27 defaults := []launcher.OptSetter{ 28 func(o *launcher.InfluxdOpts) { 29 o.LogLevel = zapcore.ErrorLevel 30 }, 31 } 32 33 p := tests.NewDefaultPipeline(t, append(defaults, extra...)...) 34 p.MustOpen() 35 return p 36 } 37 38 type Query struct { 39 name string 40 command string 41 params url.Values 42 exp, got string 43 skip string 44 skipOthers bool // set to true to only run this test 45 repeat int 46 } 47 48 // Execute runs the command and returns an err if it fails 49 func (q *Query) Execute(ctx context.Context, t *testing.T, db string, c *tests.Client) (err error) { 50 t.Helper() 51 52 params := [][2]string{{"q", q.command}} 53 if qdb := q.params.Get("db"); len(qdb) > 0 { 54 params = append(params, [2]string{"db", qdb}) 55 } 56 57 if epoch := q.params.Get("epoch"); len(epoch) > 0 { 58 params = append(params, [2]string{"epoch", epoch}) 59 } 60 61 if parameters := q.params.Get("params"); len(parameters) > 0 { 62 params = append(params, [2]string{"params", parameters}) 63 } 64 65 if chunked := q.params.Get("chunked"); len(chunked) > 0 { 66 params = append(params, [2]string{"chunked", chunked}) 67 } 68 69 if chunkSize := q.params.Get("chunk_size"); len(chunkSize) > 0 { 70 params = append(params, [2]string{"chunk_size", chunkSize}) 71 } 72 73 err = c.Client.Get("/query"). 74 QueryParams(params...). 75 Header("Accept", "application/json"). 76 RespFn(func(resp *http.Response) error { 77 require.Equal(t, "application/json", resp.Header.Get("Content-Type")) 78 b, err := io.ReadAll(resp.Body) 79 q.got = strings.TrimSpace(string(b)) 80 return err 81 }). 82 Do(ctx) 83 84 return 85 } 86 87 type Write struct { 88 data string 89 bucketID platform.ID 90 } 91 92 type Writes []*Write 93 94 type Test struct { 95 orgID platform.ID 96 bucketID platform.ID 97 db string 98 rp string 99 writes Writes 100 queries []*Query 101 noDefaultMapping bool 102 noWrites bool 103 } 104 105 func NewTest(db, rp string) Test { 106 return Test{ 107 db: db, 108 rp: rp, 109 } 110 } 111 112 // NewEmptyTest creates an empty test without a default database and retention policy mapping or 113 // any expected writes. 114 func NewEmptyTest() Test { 115 return Test{noDefaultMapping: true, noWrites: true} 116 } 117 118 func (qt *Test) Run(ctx context.Context, t *testing.T, p *tests.DefaultPipeline) { 119 t.Helper() 120 fx, auth := qt.init(ctx, t, p) 121 ctx = icontext.SetAuthorizer(ctx, auth) 122 123 skipOthers := false 124 for _, query := range qt.queries { 125 skipOthers = skipOthers || query.skipOthers 126 } 127 128 var queries []*Query 129 if skipOthers { 130 queries = make([]*Query, 0, len(qt.queries)) 131 for _, query := range qt.queries { 132 if query.skipOthers { 133 queries = append(queries, query) 134 } 135 } 136 } else { 137 queries = qt.queries 138 } 139 140 for _, query := range queries { 141 t.Run(query.name, func(t *testing.T) { 142 if query.skip != "" { 143 t.Skipf("SKIP:: %s", query.skip) 144 } 145 err := query.Execute(ctx, t, qt.db, fx.Admin) 146 assert.NoError(t, err) 147 assert.Equal(t, query.exp, query.got, 148 "%s: unexpected results\nquery: %s\nparams: %v\nexp: %s\nactual: %s\n", 149 query.name, query.command, query.params, query.exp, query.got) 150 }) 151 } 152 } 153 154 func (qt *Test) addQueries(q ...*Query) { 155 qt.queries = append(qt.queries, q...) 156 } 157 158 func (qt *Test) init(ctx context.Context, t *testing.T, p *tests.DefaultPipeline) (fx pipeline.BaseFixture, auth *influxdb.Authorization) { 159 t.Helper() 160 161 qt.orgID = p.DefaultOrgID 162 qt.bucketID = p.DefaultBucketID 163 164 fx = pipeline.NewBaseFixture(t, p.Pipeline, qt.orgID, qt.bucketID) 165 166 if !qt.noWrites { 167 require.Greater(t, len(qt.writes), 0) 168 qt.writeTestData(ctx, t, fx.Admin) 169 p.Flush() 170 } 171 172 auth = tests.MakeAuthorization(qt.orgID, p.DefaultUserID, influxdb.OperPermissions()) 173 174 if !qt.noDefaultMapping { 175 ctx = icontext.SetAuthorizer(ctx, auth) 176 err := p.Launcher. 177 DBRPMappingService(). 178 Create(ctx, &influxdb.DBRPMapping{ 179 Database: qt.db, 180 RetentionPolicy: qt.rp, 181 Default: true, 182 OrganizationID: qt.orgID, 183 BucketID: qt.bucketID, 184 }) 185 require.NoError(t, err) 186 } 187 188 return 189 } 190 191 func (qt *Test) writeTestData(ctx context.Context, t *testing.T, c *tests.Client) { 192 t.Helper() 193 for _, w := range qt.writes { 194 bucketID := &qt.bucketID 195 if w.bucketID != 0 { 196 bucketID = &w.bucketID 197 } 198 err := c.WriteTo(ctx, influxdb.BucketFilter{ID: bucketID, OrganizationID: &qt.orgID}, strings.NewReader(w.data)) 199 require.NoError(t, err) 200 } 201 } 202 203 func maxInt64() string { 204 maxInt64, _ := json.Marshal(^int64(0)) 205 return string(maxInt64) 206 } 207 208 func now() time.Time { 209 return time.Now().UTC() 210 } 211 212 func yesterday() time.Time { 213 return now().Add(-1 * time.Hour * 24) 214 } 215 216 func mustParseTime(layout, value string) time.Time { 217 tm, err := time.Parse(layout, value) 218 if err != nil { 219 panic(err) 220 } 221 return tm 222 } 223 224 func mustParseLocation(tzname string) *time.Location { 225 loc, err := time.LoadLocation(tzname) 226 if err != nil { 227 panic(err) 228 } 229 return loc 230 } 231 232 var LosAngeles = mustParseLocation("America/Los_Angeles")