File: modcache_test.go

package info (click to toggle)
golang-github-cue-lang-cue 0.14.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,644 kB
  • sloc: makefile: 20; sh: 15
file content (213 lines) | stat: -rw-r--r-- 5,755 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package modcache

import (
	"bytes"
	"context"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"sync"
	"testing"

	"cuelabs.dev/go/oci/ociregistry"
	"cuelabs.dev/go/oci/ociregistry/ociclient"
	"github.com/go-quicktest/qt"
	"golang.org/x/tools/txtar"

	"cuelang.org/go/mod/modregistry"
	"cuelang.org/go/mod/modregistrytest"
	"cuelang.org/go/mod/module"
)

func TestRequirements(t *testing.T) {
	dir := t.TempDir()
	ctx := context.Background()
	registryFS, err := txtar.FS(txtar.Parse([]byte(`
-- example.com_foo_v0.0.1/cue.mod/module.cue --
module: "example.com/foo@v0"
language: version: "v0.8.0"
deps: {
	"foo.com/bar/hello@v0": v: "v0.2.3"
	"bar.com@v0": v: "v0.5.0"
}
`)))
	qt.Assert(t, qt.IsNil(err))
	r := newRegistry(t, registryFS)
	wantRequirements := []module.Version{
		module.MustNewVersion("bar.com", "v0.5.0"),
		module.MustNewVersion("foo.com/bar/hello", "v0.2.3"),
	}
	// Test two concurrent fetches both using the same directory.
	var wg sync.WaitGroup
	fetch := func(r ociregistry.Interface) {
		defer wg.Done()
		cr, err := New(modregistry.NewClient(r), dir)
		if !qt.Check(t, qt.IsNil(err)) {
			return
		}
		summary, err := cr.Requirements(ctx, module.MustNewVersion("example.com/foo", "v0.0.1"))
		if !qt.Check(t, qt.IsNil(err)) {
			return
		}
		if !qt.Check(t, qt.DeepEquals(summary, wantRequirements)) {
			return
		}
		// Fetch again so that we test the in-memory cache-hit path.
		summary, err = cr.Requirements(ctx, module.MustNewVersion("example.com/foo", "v0.0.1"))
		if !qt.Check(t, qt.IsNil(err)) {
			return
		}
		if !qt.Check(t, qt.DeepEquals(summary, wantRequirements)) {
			return
		}
	}
	wg.Add(2)
	go fetch(r)
	go fetch(r)
	wg.Wait()

	// Check that it still functions without a functional registry.
	wg.Add(1)
	fetch(nil)

	// Check that the file is stored in the expected place.
	data, err := os.ReadFile(filepath.Join(dir, "mod/download/example.com/foo/@v/v0.0.1.mod"))
	qt.Assert(t, qt.IsNil(err))
	qt.Assert(t, qt.Matches(string(data), `(?s).*module: "example.com/foo@v0".*`))
}

func TestFetchFromCacheNotFound(t *testing.T) {
	dir := t.TempDir()
	t.Cleanup(func() {
		RemoveAll(dir)
	})
	// The cache should never be hit, so just use a nil registry value.
	// We'll get a panic if it gets used.
	cr, err := New(nil, dir)
	qt.Assert(t, qt.IsNil(err))
	_, err = cr.FetchFromCache(module.MustNewVersion("example.com/foo", "v0.0.1"))
	qt.Assert(t, qt.Not(qt.IsNil(err)))
	qt.Assert(t, qt.ErrorIs(err, modregistry.ErrNotFound))
}

func TestFetch(t *testing.T) {
	dir := t.TempDir()
	t.Cleanup(func() {
		RemoveAll(dir)
	})
	ctx := context.Background()
	registryFS, err := txtar.FS(txtar.Parse([]byte(`
-- example.com_foo_v0.0.1/cue.mod/module.cue --
module: "example.com/foo@v0"
language: version: "v0.8.0"
deps: {
	"foo.com/bar/hello@v0": v: "v0.2.3"
	"bar.com@v0": v: "v0.5.0"
}
-- example.com_foo_v0.0.1/example.cue --
package example
-- example.com_foo_v0.0.1/x/x.cue --
package x
`)))
	qt.Assert(t, qt.IsNil(err))
	r := newRegistry(t, registryFS)
	wantContents, err := txtarContents(fsSub(registryFS, "example.com_foo_v0.0.1"))
	qt.Assert(t, qt.IsNil(err))
	checkContents := func(t *testing.T, loc module.SourceLoc) bool {
		gotContents, err := txtarContents(fsSub(loc.FS, loc.Dir))
		if !qt.Check(t, qt.IsNil(err)) {
			return false
		}
		if !qt.Check(t, qt.Equals(string(gotContents), string(wantContents))) {
			return false
		}
		// Check that the location can be used to retrieve the OS file path.
		osrFS, ok := loc.FS.(module.OSRootFS)
		if !qt.Check(t, qt.IsTrue(ok)) {
			return false
		}
		root := osrFS.OSRoot()
		if !qt.Check(t, qt.Not(qt.Equals(root, ""))) {
			return false
		}
		// Check that we can access a module file directly.
		srcPath := filepath.Join(root, loc.Dir, "example.cue")
		data, err := os.ReadFile(srcPath)
		qt.Assert(t, qt.IsNil(err))
		qt.Assert(t, qt.Equals(string(data), "package example\n"))
		// Check that the actual paths are as expected.
		qt.Check(t, qt.Equals(srcPath, filepath.Join(dir, "mod", "extract", "example.com", "foo@v0.0.1", "example.cue")))
		return true
	}
	var wg sync.WaitGroup
	fetch := func(r ociregistry.Interface) {
		defer wg.Done()
		cr, err := New(modregistry.NewClient(r), dir)
		if !qt.Check(t, qt.IsNil(err)) {
			return
		}
		loc, err := cr.Fetch(ctx, module.MustNewVersion("example.com/foo", "v0.0.1"))
		if !qt.Check(t, qt.IsNil(err)) {
			return
		}
		checkContents(t, loc)

		// After Fetch has succeeded, FetchFromCache should also succeed
		// and return the same thing.
		loc, err = cr.FetchFromCache(module.MustNewVersion("example.com/foo", "v0.0.1"))
		if !qt.Check(t, qt.IsNil(err)) {
			return
		}
		checkContents(t, loc)
	}
	wg.Add(2)
	go fetch(r)
	go fetch(r)
	wg.Wait()
	// Check that it still functions without a functional registry.
	wg.Add(1)
	fetch(nil)
}

func fsSub(fsys fs.FS, sub string) fs.FS {
	fsys, err := fs.Sub(fsys, sub)
	if err != nil {
		panic(err)
	}
	return fsys
}

// txtarContents returns the contents of fsys in txtar format.
// It assumes that all files end in a newline and do not contain
// a txtar separator.
func txtarContents(fsys fs.FS) ([]byte, error) {
	var buf bytes.Buffer
	err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if d.IsDir() {
			return nil
		}
		data, err := fs.ReadFile(fsys, path)
		if err != nil {
			return err
		}
		fmt.Fprintf(&buf, "-- %s --\n", path)
		buf.Write(data)
		return nil
	})
	return buf.Bytes(), err
}

func newRegistry(t *testing.T, fsys fs.FS) ociregistry.Interface {
	regSrv, err := modregistrytest.New(fsys, "")
	qt.Assert(t, qt.IsNil(err))
	t.Cleanup(regSrv.Close)
	regOCI, err := ociclient.New(regSrv.Host(), &ociclient.Options{
		Insecure: true,
	})
	qt.Assert(t, qt.IsNil(err))
	return regOCI
}