...

Source file src/cuelang.org/go/mod/modcache/modcache_test.go

Documentation: cuelang.org/go/mod/modcache

     1  package modcache
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/fs"
     8  	"os"
     9  	"path/filepath"
    10  	"sync"
    11  	"testing"
    12  
    13  	"cuelabs.dev/go/oci/ociregistry"
    14  	"cuelabs.dev/go/oci/ociregistry/ociclient"
    15  	"github.com/go-quicktest/qt"
    16  	"golang.org/x/tools/txtar"
    17  
    18  	"cuelang.org/go/internal/registrytest"
    19  	"cuelang.org/go/internal/txtarfs"
    20  	"cuelang.org/go/mod/modregistry"
    21  	"cuelang.org/go/mod/module"
    22  )
    23  
    24  func TestRequirements(t *testing.T) {
    25  	dir := t.TempDir()
    26  	ctx := context.Background()
    27  	r := newRegistry(t, `
    28  -- example.com_foo_v0.0.1/cue.mod/module.cue --
    29  module: "example.com/foo@v0"
    30  deps: {
    31  	"foo.com/bar/hello@v0": v: "v0.2.3"
    32  	"bar.com@v0": v: "v0.5.0"
    33  }
    34  `)
    35  	wantRequirements := []module.Version{
    36  		module.MustNewVersion("bar.com", "v0.5.0"),
    37  		module.MustNewVersion("foo.com/bar/hello", "v0.2.3"),
    38  	}
    39  	// Test two concurrent fetches both using the same directory.
    40  	var wg sync.WaitGroup
    41  	fetch := func(r ociregistry.Interface) {
    42  		defer wg.Done()
    43  		cr, err := New(modregistry.NewClient(r), dir)
    44  		if !qt.Check(t, qt.IsNil(err)) {
    45  			return
    46  		}
    47  		summary, err := cr.Requirements(ctx, module.MustNewVersion("example.com/foo", "v0.0.1"))
    48  		if !qt.Check(t, qt.IsNil(err)) {
    49  			return
    50  		}
    51  		if !qt.Check(t, qt.DeepEquals(summary, wantRequirements)) {
    52  			return
    53  		}
    54  		// Fetch again so that we test the in-memory cache-hit path.
    55  		summary, err = cr.Requirements(ctx, module.MustNewVersion("example.com/foo", "v0.0.1"))
    56  		if !qt.Check(t, qt.IsNil(err)) {
    57  			return
    58  		}
    59  		if !qt.Check(t, qt.DeepEquals(summary, wantRequirements)) {
    60  			return
    61  		}
    62  	}
    63  	wg.Add(2)
    64  	go fetch(r)
    65  	go fetch(r)
    66  	wg.Wait()
    67  
    68  	// Check that it still functions without a functional registry.
    69  	wg.Add(1)
    70  	fetch(nil)
    71  
    72  	// Check that the file is stored in the expected place.
    73  	data, err := os.ReadFile(filepath.Join(dir, "cache/download/example.com/foo/@v/v0.0.1.mod"))
    74  	qt.Assert(t, qt.IsNil(err))
    75  	qt.Assert(t, qt.Matches(string(data), `(?s).*module: "example.com/foo@v0".*`))
    76  }
    77  
    78  func TestFetch(t *testing.T) {
    79  	dir := t.TempDir()
    80  	t.Cleanup(func() {
    81  		RemoveAll(dir)
    82  	})
    83  	ctx := context.Background()
    84  	registryContents := `
    85  -- example.com_foo_v0.0.1/cue.mod/module.cue --
    86  module: "example.com/foo@v0"
    87  deps: {
    88  	"foo.com/bar/hello@v0": v: "v0.2.3"
    89  	"bar.com@v0": v: "v0.5.0"
    90  }
    91  -- example.com_foo_v0.0.1/example.cue --
    92  package example
    93  -- example.com_foo_v0.0.1/x/x.cue --
    94  package x
    95  `
    96  	r := newRegistry(t, registryContents)
    97  	wantContents, err := txtarContents(fsSub(txtarfs.FS(txtar.Parse([]byte(registryContents))), "example.com_foo_v0.0.1"))
    98  	qt.Assert(t, qt.IsNil(err))
    99  	checkContents := func(t *testing.T, loc module.SourceLoc) bool {
   100  		gotContents, err := txtarContents(fsSub(loc.FS, loc.Dir))
   101  		if !qt.Check(t, qt.IsNil(err)) {
   102  			return false
   103  		}
   104  		if !qt.Check(t, qt.Equals(string(gotContents), string(wantContents))) {
   105  			return false
   106  		}
   107  		// Check that the location can be used to retrieve the OS file path.
   108  		osrFS, ok := loc.FS.(module.OSRootFS)
   109  		if !qt.Check(t, qt.IsTrue(ok)) {
   110  			return false
   111  		}
   112  		root := osrFS.OSRoot()
   113  		if !qt.Check(t, qt.Not(qt.Equals(root, ""))) {
   114  			return false
   115  		}
   116  		// Check that we can access a module file directly.
   117  		srcPath := filepath.Join(root, loc.Dir, "example.cue")
   118  		data, err := os.ReadFile(srcPath)
   119  		qt.Assert(t, qt.IsNil(err))
   120  		qt.Assert(t, qt.Equals(string(data), "package example\n"))
   121  		// Check that the actual paths are as expected.
   122  		qt.Check(t, qt.Equals(srcPath, filepath.Join(dir, "example.com", "foo@v0.0.1", "example.cue")))
   123  		return true
   124  	}
   125  	var wg sync.WaitGroup
   126  	fetch := func(r ociregistry.Interface) {
   127  		defer wg.Done()
   128  		cr, err := New(modregistry.NewClient(r), dir)
   129  		if !qt.Check(t, qt.IsNil(err)) {
   130  			return
   131  		}
   132  		loc, err := cr.Fetch(ctx, module.MustNewVersion("example.com/foo", "v0.0.1"))
   133  		if !qt.Check(t, qt.IsNil(err)) {
   134  			return
   135  		}
   136  		checkContents(t, loc)
   137  	}
   138  	wg.Add(2)
   139  	go fetch(r)
   140  	go fetch(r)
   141  	wg.Wait()
   142  	// Check that it still functions without a functional registry.
   143  	wg.Add(1)
   144  	fetch(nil)
   145  }
   146  
   147  func fsSub(fsys fs.FS, sub string) fs.FS {
   148  	fsys, err := fs.Sub(fsys, sub)
   149  	if err != nil {
   150  		panic(err)
   151  	}
   152  	return fsys
   153  }
   154  
   155  // txtarContents returns the contents of fsys in txtar format.
   156  // It assumes that all files end in a newline and do not contain
   157  // a txtar separator.
   158  func txtarContents(fsys fs.FS) ([]byte, error) {
   159  	var buf bytes.Buffer
   160  	err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
   161  		if err != nil {
   162  			return err
   163  		}
   164  		if d.IsDir() {
   165  			return nil
   166  		}
   167  		data, err := fs.ReadFile(fsys, path)
   168  		if err != nil {
   169  			return err
   170  		}
   171  		fmt.Fprintf(&buf, "-- %s --\n", path)
   172  		buf.Write(data)
   173  		return nil
   174  	})
   175  	return buf.Bytes(), err
   176  }
   177  
   178  func newRegistry(t *testing.T, registryContents string) ociregistry.Interface {
   179  	regSrv, err := registrytest.New(txtarfs.FS(txtar.Parse([]byte(registryContents))), "")
   180  	qt.Assert(t, qt.IsNil(err))
   181  	t.Cleanup(regSrv.Close)
   182  	regOCI, err := ociclient.New(regSrv.Host(), &ociclient.Options{
   183  		Insecure: true,
   184  	})
   185  	qt.Assert(t, qt.IsNil(err))
   186  	return regOCI
   187  }
   188  

View as plain text