build_node_fetch takes buildstep.FetchRequest (dir and paths narrow the workspace) and an
optional package {repository, commit, ref}: the group's package member, built and packed
shut off and published nowhere. Each member's lock decides its SDK: the group's packed
file when the version and integrity are the lock's, else the registry's (integrity
checked); a version the registry lacks fails waits for: @novox/mesh-sdk@<version>. A
package artifact is built by its own script and packed with npm pack, shut off; only a
trunk build publishes it, from this process with the write credential (never in a
workspace): absent is published, the same file is done, another is version taken:. The
registry configuration file now lies outside every workspace.
161 lines
7.1 KiB
Go
161 lines
7.1 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"compress/gzip"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// tgzOf is a packed npm package holding package/package.json of name@version and one more file.
|
|
func tgzOf(t *testing.T, name, version string) []byte {
|
|
t.Helper()
|
|
var buf bytes.Buffer
|
|
zw := gzip.NewWriter(&buf)
|
|
tw := tar.NewWriter(zw)
|
|
for _, f := range [][2]string{{"package/package.json", `{"name":"` + name + `","version":"` + version + `"}`}, {"package/dist/index.js", "export const v = '" + version + "';\n"}} {
|
|
n, body := f[0], f[1]
|
|
tw.WriteHeader(&tar.Header{Name: n, Mode: 0o644, Size: int64(len(body)), Typeflag: tar.TypeReg})
|
|
tw.Write([]byte(body))
|
|
}
|
|
tw.Close()
|
|
zw.Close()
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// fakeRegistry serves the packument and the packed files of the versions it holds; asked counts its answers.
|
|
func fakeRegistry(t *testing.T, m *Module, held map[string][]byte) *atomic.Int32 {
|
|
t.Helper()
|
|
var asked atomic.Int32
|
|
var srv *httptest.Server
|
|
srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
asked.Add(1)
|
|
switch {
|
|
case r.URL.Path == "/npm/@novox%2fmesh-sdk" || r.URL.Path == "/npm/@novox/mesh-sdk":
|
|
if len(held) == 0 {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
versions := map[string]any{}
|
|
for v, tgz := range held {
|
|
versions[v] = map[string]any{"dist": map[string]any{"tarball": srv.URL + "/files/" + v + ".tgz", "integrity": integrityOf(tgz)}}
|
|
}
|
|
json.NewEncoder(w).Encode(map[string]any{"versions": versions})
|
|
case strings.HasPrefix(r.URL.Path, "/files/"):
|
|
v := strings.TrimSuffix(strings.TrimPrefix(r.URL.Path, "/files/"), ".tgz")
|
|
if tgz, ok := held[v]; ok {
|
|
w.Write(tgz)
|
|
return
|
|
}
|
|
http.NotFound(w, r)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
m.Toolchain.Registry = func() (Registry, error) { return Registry{URL: srv.URL + "/npm/"}, nil }
|
|
m.HTTP = srv.Client()
|
|
return &asked
|
|
}
|
|
|
|
// memberLocking is a member's directory whose lock names @novox/mesh-sdk@version with integrity (none when version is "").
|
|
func memberLocking(t *testing.T, version, integrity string) string {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
lock := map[string]any{"lockfileVersion": 3, "packages": map[string]any{"": map[string]any{"name": "m"}}}
|
|
if version != "" {
|
|
lock["packages"].(map[string]any)["node_modules/@novox/mesh-sdk"] = map[string]any{"version": version, "integrity": integrity}
|
|
}
|
|
raw, _ := json.Marshal(lock)
|
|
write(t, filepath.Join(dir, "package-lock.json"), string(raw), 0o644)
|
|
return dir
|
|
}
|
|
|
|
func placedVersion(dir string) string {
|
|
raw, err := os.ReadFile(filepath.Join(dir, "node_modules", "@novox", "mesh-sdk", "package.json"))
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
var p packageJSON
|
|
json.Unmarshal(raw, &p)
|
|
return p.Version
|
|
}
|
|
|
|
// The lead's decision L10 (ADR 0500 §6): a member is built against the SDK version its own lock names, downloaded from
|
|
// the registry and checked against the lock's integrity; a version the registry does not hold is a wait, `waits for:`.
|
|
func TestAMemberGetsTheSDKVersionItsLockNamesOrWaitsForIt(t *testing.T) {
|
|
m, _, _ := newMachine(t)
|
|
held := tgzOf(t, sdkPackage, "0.1.15")
|
|
fakeRegistry(t, m, map[string][]byte{"0.1.15": held})
|
|
dir := memberLocking(t, "0.1.15", integrityOf(held))
|
|
if err := m.placeSDK(context.Background(), dir, "modules/m", nil); err != nil || placedVersion(dir) != "0.1.15" {
|
|
t.Errorf("the registry's 0.1.15: %v, placed %q", err, placedVersion(dir))
|
|
}
|
|
dir = memberLocking(t, "0.1.16", "sha512-x")
|
|
err := m.placeSDK(context.Background(), dir, "modules/m", nil)
|
|
if err == nil || err.Error() != delivery.WaitsFor+"@novox/mesh-sdk@0.1.16" {
|
|
t.Errorf("a version the registry lacks answered %v, want %s@novox/mesh-sdk@0.1.16", err, delivery.WaitsFor)
|
|
}
|
|
dir = memberLocking(t, "0.1.15", "sha512-another")
|
|
if err := m.placeSDK(context.Background(), dir, "modules/m", nil); err == nil || !strings.Contains(err.Error(), "sha512-another") {
|
|
t.Errorf("a registry file of another integrity than the lock's answered %v", err)
|
|
}
|
|
}
|
|
|
|
// ADR 0500 §5 (S3, S4): the group's packed package is served in place of the registry's when the member's lock names its
|
|
// version with its integrity; another integrity, or a version neither the group nor the registry holds, fails naming both.
|
|
// A member whose lock names no SDK keeps the toolchain's and asks the registry nothing.
|
|
func TestTheGroupsPackageIsServedToAMemberLockingItsVersion(t *testing.T) {
|
|
m, _, _ := newMachine(t)
|
|
asked := fakeRegistry(t, m, map[string][]byte{"0.1.14": tgzOf(t, sdkPackage, "0.1.14")})
|
|
tgz := tgzOf(t, sdkPackage, "0.1.15")
|
|
group := &packed{Name: sdkPackage, Version: "0.1.15", Integrity: integrityOf(tgz), Digest: digestOf(tgz), tgz: tgz}
|
|
dir := memberLocking(t, "0.1.15", group.Integrity)
|
|
if err := m.placeSDK(context.Background(), dir, "modules/m", group); err != nil || placedVersion(dir) != "0.1.15" || asked.Load() != 0 {
|
|
t.Errorf("the group's own version: %v, placed %q, the registry asked %d times", err, placedVersion(dir), asked.Load())
|
|
}
|
|
dir = memberLocking(t, "0.1.15", "sha512-other")
|
|
if err := m.placeSDK(context.Background(), dir, "modules/m", group); err == nil || !strings.Contains(err.Error(), "sha512-other") || !strings.Contains(err.Error(), group.Integrity) {
|
|
t.Errorf("another integrity answered %v, want both named", err)
|
|
}
|
|
dir = memberLocking(t, "0.1.14", integrityOf(tgzOf(t, sdkPackage, "0.1.14")))
|
|
if err := m.placeSDK(context.Background(), dir, "modules/m", group); err != nil || placedVersion(dir) != "0.1.14" {
|
|
t.Errorf("a member locking the registry's 0.1.14 in a group: %v, placed %q", err, placedVersion(dir))
|
|
}
|
|
dir = memberLocking(t, "0.1.13", "sha512-x")
|
|
if err := m.placeSDK(context.Background(), dir, "modules/m", group); err == nil || !strings.Contains(err.Error(), "0.1.13") || !strings.Contains(err.Error(), "0.1.15") || strings.HasPrefix(err.Error(), delivery.WaitsFor) {
|
|
t.Errorf("a version neither holds answered %v, want both named and no wait", err)
|
|
}
|
|
before := asked.Load()
|
|
dir = memberLocking(t, "", "")
|
|
if err := m.placeSDK(context.Background(), dir, "modules/m", group); err != nil || placedVersion(dir) != "" || asked.Load() != before {
|
|
t.Errorf("a lock naming no SDK: %v, placed %q, the registry asked", err, placedVersion(dir))
|
|
}
|
|
}
|
|
|
|
// The fetch's `package` argument is a repository and a full commit, with a pull request's head at most; anything else is
|
|
// refused before anything is fetched.
|
|
func TestTheGroupPackageArgumentIsRefusedWhenItIsNotOne(t *testing.T) {
|
|
for _, bad := range []any{"novox/mesh-sdk", map[string]any{"repository": "novox/mesh-sdk", "commit": "main"},
|
|
map[string]any{"repository": "../x", "commit": strings.Repeat("a", 40)},
|
|
map[string]any{"repository": "novox/mesh-sdk", "commit": strings.Repeat("a", 40), "ref": "refs/heads/main"}} {
|
|
if _, err := groupPackageArg(bad); err == nil {
|
|
t.Errorf("%v was taken", bad)
|
|
}
|
|
}
|
|
g, err := groupPackageArg(map[string]any{"repository": "novox/mesh-sdk", "commit": strings.Repeat("a", 40), "ref": "refs/pull/9/head"})
|
|
if err != nil || g.Ref != "refs/pull/9/head" {
|
|
t.Errorf("a pull request's head: %+v %v", g, err)
|
|
}
|
|
}
|