134 lines
5.2 KiB
Go
134 lines
5.2 KiB
Go
//go:build integration
|
|
|
|
package builder
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// A module's own packages (dependencies.go): installed into its own directory, in the toolchain,
|
|
// before the compile, so the bundler inlines them — the SDK always the toolchain's.
|
|
|
|
func buildWithPackageJSON(t *testing.T, pkg string, extra map[string]string, registry Npmrc) (*recorded, error) {
|
|
t.Helper()
|
|
files := map[string]string{"index.ts": "console.log(1)"}
|
|
if pkg != "" {
|
|
files["package.json"] = pkg
|
|
}
|
|
for k, v := range extra {
|
|
files[k] = v
|
|
}
|
|
r, workspace := aRepository(t, aBundle, files)
|
|
held := map[string]string{"mesh-tools/build": "registry.invalid/mesh-tools/build@sha256:" + strings.Repeat("b", 64)}
|
|
_, err := Build(context.Background(), compiling{r}.run, r,
|
|
"https://forge.invalid/greeter.git", "", "", workspace, held, registry, GitCredential{}, nil)
|
|
return r, err
|
|
}
|
|
|
|
func installs(r *recorded) []string {
|
|
var out []string
|
|
for _, line := range r.ran {
|
|
if strings.HasPrefix(line, "docker run") && strings.Contains(line, "npm ci") {
|
|
out = append(out, line)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func compileIndex(r *recorded) int {
|
|
for i, line := range r.ran {
|
|
if strings.Contains(line, "--outDir") {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func TestAModulesOwnPackagesAreInstalledInTheToolchainBeforeTheCompile(t *testing.T) {
|
|
r, err := buildWithPackageJSON(t, `{"type":"module","dependencies":{"@novox/mesh-sdk":"^0.1.0","pg":"^8"},"devDependencies":{"typescript":"^5"}}`,
|
|
nil, Npmrc{Scope: "@novox", Registry: "https://forge.invalid/api/packages/novox/npm"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := installs(r)
|
|
if len(got) != 1 {
|
|
t.Fatalf("want one install of the module's own packages:\n%s", strings.Join(r.ran, "\n"))
|
|
}
|
|
line := got[0]
|
|
for _, want := range []string{
|
|
"mesh-tools/build@sha256:", // in the toolchain image
|
|
":/app/modules/module", // into the module's own directory
|
|
"--workdir /app/modules/module", //
|
|
npmCache + ":/root/.npm", // npm's verified download cache, and only that
|
|
"--omit=dev", "--ignore-scripts", // production packages, no build-node scripts
|
|
"npm ci", "npm install", "--no-package-lock", // the lockfile when there is one, else the ranges
|
|
"--@novox:registry=https://forge.invalid/api/packages/novox/npm/", // the scope from the mesh's registry
|
|
"--network host",
|
|
"@novox/mesh-sdk", // named, to be taken out of what is installed
|
|
} {
|
|
if !strings.Contains(line, want) {
|
|
t.Errorf("the install lacks %q:\n%s", want, line)
|
|
}
|
|
}
|
|
// The SDK is the toolchain's: never installed from the module's range, and any copy removed.
|
|
if !strings.Contains(line, `delete p[k][sdk]`) || !strings.Contains(line, `-path "*/node_modules/@novox/mesh-sdk" -exec rm -rf`) {
|
|
t.Errorf("the module's own SDK range could shadow the toolchain's SDK:\n%s", line)
|
|
}
|
|
if i, c := strings.Index(strings.Join(r.ran, "\n"), "npm ci"), compileIndex(r); c < 0 ||
|
|
i > strings.Index(strings.Join(r.ran, "\n"), "--outDir") {
|
|
t.Fatalf("the install did not run before the compile:\n%s", strings.Join(r.ran, "\n"))
|
|
}
|
|
}
|
|
|
|
// **A module with nothing beyond the SDK builds exactly as before**: the same commands, no install.
|
|
func TestAModuleDependingOnlyOnTheSDKBuildsExactlyAsBefore(t *testing.T) {
|
|
without, err := buildWithPackageJSON(t, "", nil, Npmrc{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, pkg := range []string{
|
|
`{"type":"module","dependencies":{"@novox/mesh-sdk":"^0.1.0"},"devDependencies":{"typescript":"^5"}}`,
|
|
`{"type":"module"}`,
|
|
} {
|
|
with, err := buildWithPackageJSON(t, pkg, map[string]string{"package-lock.json": "{}"}, Npmrc{Scope: "@novox", Registry: "https://forge.invalid/npm/"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(strings.Join(with.ran, "\n"), "npm ") {
|
|
t.Fatalf("a module depending on nothing but the SDK ran npm:\n%s", strings.Join(with.ran, "\n"))
|
|
}
|
|
if len(with.ran) != len(without.ran) {
|
|
t.Fatalf("a module depending only on the SDK built differently from one with no package.json:\n%s\n---\n%s",
|
|
strings.Join(with.ran, "\n"), strings.Join(without.ran, "\n"))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Without the mesh's registry a scoped package would resolve on the public one: refused by name.
|
|
func TestAScopedPackageWithNoRegistryIsRefused(t *testing.T) {
|
|
r, err := buildWithPackageJSON(t, `{"dependencies":{"@novox/mesh-sdk":"^0.1.0","@novox/other":"^1"}}`, nil, Npmrc{})
|
|
if err == nil || !strings.Contains(err.Error(), "@novox/other") {
|
|
t.Fatalf("a scoped package was installed with no registry for its scope: %v", err)
|
|
}
|
|
if strings.Contains(strings.Join(r.ran, "\n"), "--outDir") {
|
|
t.Fatal("the compile ran after the refusal")
|
|
}
|
|
// A public package installs without one, from the public registry and nothing else.
|
|
r, err = buildWithPackageJSON(t, `{"dependencies":{"mssql":"^11"}}`, nil, Npmrc{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := installs(r); len(got) != 1 || strings.Contains(got[0], ":registry=") || strings.Contains(got[0], "--network host") {
|
|
t.Fatalf("a public package's install: %v", got)
|
|
}
|
|
}
|
|
|
|
func TestAnUnreadablePackageJSONIsRefusedByName(t *testing.T) {
|
|
_, err := buildWithPackageJSON(t, `{"dependencies":`, nil, Npmrc{})
|
|
if err == nil || !strings.Contains(err.Error(), "package.json") {
|
|
t.Fatalf("a broken package.json was not refused by name: %v", err)
|
|
}
|
|
}
|