cuelang.org/go@v0.13.0/encoding/jsonschema/internal/externaltest/tests.go (about)

     1  package externaltest
     2  
     3  import (
     4  	"bytes"
     5  	stdjson "encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"cuelang.org/go/cue"
    11  	"cuelang.org/go/cue/cuecontext"
    12  	"cuelang.org/go/cue/interpreter/embed"
    13  	"cuelang.org/go/cue/load"
    14  	"cuelang.org/go/cue/token"
    15  )
    16  
    17  type Schema struct {
    18  	location
    19  	Description string             `json:"description"`
    20  	Comment     string             `json:"comment,omitempty"`
    21  	Schema      stdjson.RawMessage `json:"schema"`
    22  	Skip        Skip               `json:"skip,omitempty"`
    23  	Tests       []*Test            `json:"tests"`
    24  }
    25  
    26  type Test struct {
    27  	location
    28  	Description string             `json:"description"`
    29  	Comment     string             `json:"comment,omitempty"`
    30  	Data        stdjson.RawMessage `json:"data"`
    31  	Valid       bool               `json:"valid"`
    32  	Skip        Skip               `json:"skip,omitempty"`
    33  }
    34  
    35  // Skip records information about whether a given schema
    36  // or test will be skipped when testing. If not present,
    37  // the test will be expected to pass.
    38  //
    39  // Each key in the map represents the name of a point
    40  // in the cuetdtest matrix.
    41  type Skip map[string]string
    42  
    43  type location struct {
    44  	root cue.Value
    45  	path cue.Path
    46  }
    47  
    48  func (loc location) Pos() token.Pos {
    49  	return loc.root.LookupPath(loc.path).Pos()
    50  }
    51  
    52  // WriteTestDir writes test data files as read by ReadTestDir
    53  // to the given directory. The keys of tests are filenames relative
    54  // to dir.
    55  func WriteTestDir(dir string, tests map[string][]*Schema) error {
    56  	for filename, schemas := range tests {
    57  		filename = filepath.Join(dir, filename)
    58  		data, err := stdjson.MarshalIndent(schemas, "", "\t")
    59  		if err != nil {
    60  			return err
    61  		}
    62  		if err != nil {
    63  			return err
    64  		}
    65  		data = append(data, '\n')
    66  		oldData, err := os.ReadFile(filename)
    67  		if err != nil {
    68  			return err
    69  		}
    70  		if bytes.Equal(oldData, data) {
    71  			continue
    72  		}
    73  		err = os.WriteFile(filename, data, 0o666)
    74  		if err != nil {
    75  			return err
    76  		}
    77  	}
    78  	return nil
    79  }
    80  
    81  var ErrNotFound = fmt.Errorf("no external JSON schema tests found")
    82  
    83  // ReadTestDir reads all the external tests from the given directory.
    84  func ReadTestDir(dir string) (tests map[string][]*Schema, err error) {
    85  	if _, err := os.Stat(dir); err != nil {
    86  		if os.IsNotExist(err) {
    87  			return nil, ErrNotFound
    88  		}
    89  		return nil, err
    90  	}
    91  	inst := load.Instances([]string{"."}, &load.Config{
    92  		Dir: dir,
    93  		// Just like in the cue/load tests, prevent Go tests from walking up to the root
    94  		// directory of the git repository, as that almost always causes test cache misses.
    95  		ModuleRoot: ".",
    96  	})[0]
    97  	if err := inst.Err; err != nil {
    98  		return nil, err
    99  	}
   100  	ctx := cuecontext.New(cuecontext.Interpreter(embed.New()))
   101  	instVal := ctx.BuildInstance(inst)
   102  	if err := instVal.Err(); err != nil {
   103  		return nil, err
   104  	}
   105  	val := instVal.LookupPath(cue.MakePath(cue.Str("allTests")))
   106  	if err := val.Err(); err != nil {
   107  		return nil, err
   108  	}
   109  	if err := val.Decode(&tests); err != nil {
   110  		return nil, err
   111  	}
   112  	// Fix up the raw JSON data to avoid running into some decode issues.
   113  	for filename, schemas := range tests {
   114  		for i, schema := range schemas {
   115  			schema.location = location{
   116  				root: val,
   117  				path: cue.MakePath(cue.Str(filename), cue.Index(i)),
   118  			}
   119  			for j, test := range schema.Tests {
   120  				test.location = location{
   121  					root: val,
   122  					path: cue.MakePath(cue.Str(filename), cue.Index(i), cue.Str("tests"), cue.Index(j)),
   123  				}
   124  				if len(test.Data) == 0 {
   125  					// See https://github.com/cue-lang/cue/issues/3397
   126  					test.Data = []byte("null")
   127  					continue
   128  				}
   129  				// See https://github.com/cue-lang/cue/issues/3398
   130  				test.Data = bytes.ReplaceAll(test.Data, []byte("\ufeff"), []byte(`\ufeff`))
   131  			}
   132  		}
   133  	}
   134  	return tests, nil
   135  }