...

Source file src/cuelang.org/go/mod/modconfig/modconfig_test.go

Documentation: cuelang.org/go/mod/modconfig

     1  package modconfig
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/fs"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"net/url"
    11  	"os"
    12  	"path"
    13  	"path/filepath"
    14  	"slices"
    15  	"strings"
    16  	"sync/atomic"
    17  	"testing"
    18  
    19  	"github.com/go-quicktest/qt"
    20  	"golang.org/x/tools/txtar"
    21  
    22  	"cuelang.org/go/internal/cueversion"
    23  	"cuelang.org/go/internal/registrytest"
    24  	"cuelang.org/go/internal/txtarfs"
    25  	"cuelang.org/go/mod/modcache"
    26  	"cuelang.org/go/mod/module"
    27  )
    28  
    29  // TODO: the test below acts as a smoke test for the functionality here,
    30  // but more of the behavior is tested in the cmd/cue script tests.
    31  // We should do more of it here too.
    32  
    33  func TestNewRegistry(t *testing.T) {
    34  	modules := txtar.Parse([]byte(`
    35  -- r1/foo.example_v0.0.1/cue.mod/module.cue --
    36  module: "foo.example@v0"
    37  deps: "bar.example@v0": v: "v0.0.1"
    38  -- r1/foo.example_v0.0.1/bar/bar.cue --
    39  package bar
    40  -- r1/bar.example_v0.0.1/cue.mod/module.cue --
    41  module: "bar.example@v0"
    42  -- r1/bar.example_v0.0.1/y/y.cue --
    43  package y
    44  
    45  -- r2/auth.json --
    46  {
    47  	"username": "bob",
    48  	"password": "somePassword"
    49  }
    50  -- r2/bar.example_v0.0.1/cue.mod/module.cue --
    51  module: "bar.example@v0"
    52  -- r2/bar.example_v0.0.1/x/x.cue --
    53  package x
    54  `))
    55  	fsys := txtarfs.FS(modules)
    56  	r1fs, err := fs.Sub(fsys, "r1")
    57  	qt.Assert(t, qt.IsNil(err))
    58  	r1, err := registrytest.New(r1fs, "")
    59  	qt.Assert(t, qt.IsNil(err))
    60  	r2fs, err := fs.Sub(fsys, "r2")
    61  	qt.Assert(t, qt.IsNil(err))
    62  	r2, err := registrytest.New(r2fs, "")
    63  	qt.Assert(t, qt.IsNil(err))
    64  
    65  	dir := t.TempDir()
    66  	t.Setenv("DOCKER_CONFIG", dir)
    67  	dockerCfg, err := json.Marshal(dockerConfig{
    68  		Auths: map[string]authConfig{
    69  			r2.Host(): {
    70  				Username: "bob",
    71  				Password: "somePassword",
    72  			},
    73  		},
    74  	})
    75  	qt.Assert(t, qt.IsNil(err))
    76  	err = os.WriteFile(filepath.Join(dir, "config.json"), dockerCfg, 0o666)
    77  	qt.Assert(t, qt.IsNil(err))
    78  
    79  	t.Setenv("CUE_REGISTRY",
    80  		fmt.Sprintf("foo.example=%s+insecure,%s+insecure",
    81  			r1.Host(),
    82  			r2.Host(),
    83  		))
    84  	cacheDir := filepath.Join(dir, "cache")
    85  	t.Setenv("CUE_CACHE_DIR", cacheDir)
    86  	t.Cleanup(func() {
    87  		modcache.RemoveAll(cacheDir)
    88  	})
    89  
    90  	var transportInvoked atomic.Bool
    91  	r, err := NewRegistry(&Config{
    92  		Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) {
    93  			transportInvoked.Store(true)
    94  			return http.DefaultTransport.RoundTrip(req)
    95  		}),
    96  	})
    97  	qt.Assert(t, qt.IsNil(err))
    98  	ctx := context.Background()
    99  	gotRequirements, err := r.Requirements(ctx, module.MustNewVersion("foo.example@v0", "v0.0.1"))
   100  	qt.Assert(t, qt.IsNil(err))
   101  	qt.Assert(t, qt.DeepEquals(gotRequirements, []module.Version{
   102  		module.MustNewVersion("bar.example@v0", "v0.0.1"),
   103  	}))
   104  
   105  	loc, err := r.Fetch(ctx, module.MustNewVersion("bar.example@v0", "v0.0.1"))
   106  	qt.Assert(t, qt.IsNil(err))
   107  	data, err := fs.ReadFile(loc.FS, path.Join(loc.Dir, "x/x.cue"))
   108  	qt.Assert(t, qt.IsNil(err))
   109  	qt.Assert(t, qt.Equals(string(data), "package x\n"))
   110  
   111  	// Check that we can make a Resolver with the same configuration.
   112  	resolver, err := NewResolver(nil)
   113  	qt.Assert(t, qt.IsNil(err))
   114  	gotAllHosts := resolver.AllHosts()
   115  	wantAllHosts := []Host{{Name: r1.Host(), Insecure: true}, {Name: r2.Host(), Insecure: true}}
   116  
   117  	byHostname := func(a, b Host) int { return strings.Compare(a.Name, b.Name) }
   118  	slices.SortFunc(gotAllHosts, byHostname)
   119  	slices.SortFunc(wantAllHosts, byHostname)
   120  
   121  	qt.Assert(t, qt.DeepEquals(gotAllHosts, wantAllHosts))
   122  
   123  	// Check that the underlying custom transport was used.
   124  	qt.Assert(t, qt.IsTrue(transportInvoked.Load()))
   125  }
   126  
   127  func TestDefaultTransportSetsUserAgent(t *testing.T) {
   128  	// This test also checks that providing a nil Config.Transport
   129  	// does the right thing.
   130  
   131  	modules := txtar.Parse([]byte(`
   132  -- bar.example_v0.0.1/cue.mod/module.cue --
   133  module: "bar.example@v0"
   134  -- bar.example_v0.0.1/x/x.cue --
   135  package x
   136  `))
   137  	rh, err := registrytest.NewHandler(txtarfs.FS(modules), "")
   138  	qt.Assert(t, qt.IsNil(err))
   139  	agent := cueversion.UserAgent("cuelang.org/go")
   140  	checked := false
   141  	checkUserAgentHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   142  		qt.Check(t, qt.Equals(req.UserAgent(), agent))
   143  		checked = true
   144  		rh.ServeHTTP(w, req)
   145  	})
   146  	srv := httptest.NewServer(checkUserAgentHandler)
   147  	u, err := url.Parse(srv.URL)
   148  	qt.Assert(t, qt.IsNil(err))
   149  
   150  	dir := t.TempDir()
   151  	t.Setenv("DOCKER_CONFIG", dir)
   152  	t.Setenv("CUE_REGISTRY", u.Host+"+insecure")
   153  	cacheDir := filepath.Join(dir, "cache")
   154  	t.Setenv("CUE_CACHE_DIR", cacheDir)
   155  	t.Cleanup(func() {
   156  		modcache.RemoveAll(cacheDir)
   157  	})
   158  
   159  	r, err := NewRegistry(nil)
   160  	qt.Assert(t, qt.IsNil(err))
   161  	ctx := context.Background()
   162  	gotRequirements, err := r.Requirements(ctx, module.MustNewVersion("bar.example@v0", "v0.0.1"))
   163  	qt.Assert(t, qt.IsNil(err))
   164  	qt.Assert(t, qt.HasLen(gotRequirements, 0))
   165  
   166  	qt.Assert(t, qt.IsTrue(checked))
   167  }
   168  
   169  // dockerConfig describes the minimal subset of the docker
   170  // configuration file necessary to check that authentication
   171  // is correction hooked up.
   172  type dockerConfig struct {
   173  	Auths map[string]authConfig `json:"auths"`
   174  }
   175  
   176  // authConfig contains authorization information for connecting to a Registry.
   177  type authConfig struct {
   178  	Username string `json:"username,omitempty"`
   179  	Password string `json:"password,omitempty"`
   180  }
   181  
   182  type roundTripperFunc func(*http.Request) (*http.Response, error)
   183  
   184  func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
   185  	return f(req)
   186  }
   187  

View as plain text