cuelang.org/go@v0.10.1/mod/modcache/modcache_test.go (about)

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