github.com/influxdata/influxdb/v2@v2.7.6/influxql/v1validation/validation_test.go (about) 1 package v1validation 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "path/filepath" 10 "strconv" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/influxdata/influxdb/v2" 16 "github.com/influxdata/influxdb/v2/cmd/influxd/launcher" 17 icontext "github.com/influxdata/influxdb/v2/context" 18 "github.com/influxdata/influxdb/v2/mock" 19 datagen "github.com/influxdata/influxdb/v2/pkg/data/gen" 20 "github.com/influxdata/influxdb/v2/storage/reads" 21 "github.com/influxdata/influxdb/v2/tests" 22 "github.com/influxdata/influxdb/v2/tests/pipeline" 23 "github.com/stretchr/testify/assert" 24 "go.uber.org/zap/zapcore" 25 "gopkg.in/yaml.v2" 26 ) 27 28 var skipMap = map[string]string{ 29 // file_name_without_extension: skip_reason 30 } 31 32 type GeneratedDataset struct { 33 Start string `yaml:"start"` 34 End string `yaml:"end"` 35 Toml string `yaml:"toml"` 36 } 37 38 type TestSuite struct { 39 Tests []Test `yaml:"tests"` 40 Dataset string `yaml:"dataset"` // Line protocol OR 41 Generated *GeneratedDataset `yaml:"generated"` // TOML schema description 42 } 43 44 type Test struct { 45 Name string `yaml:"name"` 46 Query string `yaml:"query"` 47 Result string `yaml:"result"` 48 } 49 50 func TestGoldenFiles(t *testing.T) { 51 err := filepath.WalkDir("./goldenfiles", func(path string, info os.DirEntry, err error) error { 52 if info.IsDir() { 53 return nil 54 } 55 base := filepath.Base(path) 56 ext := filepath.Ext(base) 57 testName := strings.TrimSuffix(base, ext) 58 t.Run(testName, func(t *testing.T) { 59 if reason, ok := skipMap[testName]; ok { 60 t.Skip(reason) 61 } 62 gf := testSuiteFromPath(t, path) 63 validate(t, gf) 64 }) 65 return nil 66 }) 67 if err != nil { 68 t.Fatal(err) 69 } 70 } 71 72 // Unmarshal a TestSuite from a YAML file 73 func testSuiteFromPath(t *testing.T, path string) *TestSuite { 74 f, err := os.Open(path) 75 if err != nil { 76 t.Fatal(err) 77 } 78 b, err := io.ReadAll(f) 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 gf := &TestSuite{} 84 err = yaml.Unmarshal(b, gf) 85 if err != nil { 86 t.Fatal(err) 87 } 88 return gf 89 } 90 91 func validate(t *testing.T, gf *TestSuite) { 92 t.Helper() 93 ctx := context.Background() 94 p := tests.NewDefaultPipeline(t, func(o *launcher.InfluxdOpts) { 95 o.LogLevel = zapcore.ErrorLevel 96 }) 97 p.MustOpen() 98 defer p.MustClose() 99 orgID := p.DefaultOrgID 100 bucketID := p.DefaultBucketID 101 fx := pipeline.NewBaseFixture(t, p.Pipeline, orgID, bucketID) 102 103 var dataset string 104 105 if gf.Generated != nil { 106 spec, err := datagen.NewSpecFromToml(gf.Generated.Toml) 107 if err != nil { 108 t.Fatalf("error processing TOML: %v", err) 109 } 110 111 tryParse := func(s string) (time.Time, error) { 112 if v, err := strconv.Atoi(s); err == nil { 113 return time.Unix(0, int64(v)), nil 114 } 115 116 return time.Parse(time.RFC3339, s) 117 } 118 119 start, err := tryParse(gf.Generated.Start) 120 if err != nil { 121 t.Fatalf("error parsing start: %v", err) 122 } 123 end, err := tryParse(gf.Generated.End) 124 if err != nil { 125 t.Fatalf("error parsing end: %v", err) 126 } 127 128 if end.Before(start) { 129 t.Fatal("error: start must be before end") 130 } 131 132 sg := datagen.NewSeriesGeneratorFromSpec(spec, datagen.TimeRange{ 133 Start: start, 134 End: end, 135 }) 136 137 rs := mock.NewResultSetFromSeriesGenerator(sg, mock.WithGeneratorMaxValues(10000)) 138 var sb strings.Builder 139 if err := reads.ResultSetToLineProtocol(&sb, rs); err != nil { 140 t.Fatalf("error generating data: %v", err) 141 } 142 dataset = sb.String() 143 if len(dataset) == 0 { 144 t.Fatal("no data generated") 145 } 146 } else { 147 dataset = gf.Dataset 148 } 149 150 if err := fx.Admin.WriteBatch(dataset); err != nil { 151 t.Fatal(err) 152 } 153 p.Flush() 154 155 ctx = icontext.SetAuthorizer(ctx, tests.MakeAuthorization(p.DefaultOrgID, p.DefaultUserID, influxdb.OperPermissions())) 156 157 if err := p.Launcher.DBRPMappingService().Create(ctx, &influxdb.DBRPMapping{ 158 Database: "mydb", 159 RetentionPolicy: "autogen", 160 Default: true, 161 OrganizationID: orgID, 162 BucketID: bucketID, 163 }); err != nil { 164 t.Fatal(err) 165 } 166 167 for i := range gf.Tests { 168 test := &gf.Tests[i] 169 name := test.Name 170 if len(name) == 0 { 171 name = fmt.Sprintf("query_%02d", i) 172 } 173 t.Run(name, func(t *testing.T) { 174 err := fx.Admin.Client.Get("/query"). 175 QueryParams([2]string{"db", "mydb"}). 176 QueryParams([2]string{"q", test.Query}). 177 QueryParams([2]string{"epoch", "ns"}). 178 Header("Content-Type", "application/vnd.influxql"). 179 Header("Accept", "application/csv"). 180 RespFn(func(resp *http.Response) error { 181 b, err := io.ReadAll(resp.Body) 182 assert.NoError(t, err) 183 assert.Equal(t, test.Result, string(b)) 184 return nil 185 }). 186 Do(ctx) 187 if err != nil { 188 t.Fatal(err) 189 } 190 }) 191 } 192 }