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  }