Files
mesh-controller/internal/builder/bundle_test.go
T

81 lines
3.2 KiB
Go

package builder
import (
"context"
"os"
"path/filepath"
"testing"
)
const aBundle = `{"module":"greeter","version":"1",
"build":{"artifacts":[
{"name":"code","kind":"bundle","language":"typescript","entrypoints":["index.js"]}]},
"resources":[
{"id":"files","type":"archive","path":"/opt/greeter","artifact":"code"}]}`
// compiling is a runner that behaves like a toolchain: when asked to compile, it leaves output
// where the toolchain says output lands. Without this the pack step has nothing to pack, and the
// test would be asserting on a failure rather than on a build.
type compiling struct{ *recorded }
func (c compiling) run(ctx context.Context, dir, name string, args ...string) (string, error) {
out, err := c.recorded.run(ctx, dir, name, args...)
if name != "docker" || len(args) == 0 || args[0] != "run" {
return out, err
}
// **Writes where it was TOLD to**, rather than to a fixed directory. A fake that always wrote
// to one place would pass whether or not the builder gave each artifact its own — which is the
// thing being tested.
where := ""
for i, a := range args {
if a == "--outDir" && i+1 < len(args) {
where = args[i+1]
}
}
if where == "" {
return out, err
}
made := filepath.Join(dir, where)
if err := os.MkdirAll(made, 0o755); err != nil {
return "", err
}
if err := os.WriteFile(filepath.Join(made, "index.js"), []byte("console.log(1)"), 0o644); err != nil {
return "", err
}
return out, err
}
// **A module says what it is written in, and needs no Dockerfile.** This is the whole point of the
// bundle recipe: the same module previously needed a hand-written recipe repeating an incantation
// that is easy to get wrong in ways that fail somewhere else.
// A bundle naming packages it keeps external is bundled with them as imports, and carries the
// toolchain's node_modules for them — the one case it still does.
// A language whose bundle carries its own dependencies copies nothing in: a Go binary is static.
func TestOnlyALanguageWithARuntimeDirectoryCopiesDependenciesIn(t *testing.T) {
ts, _ := ToolchainFor("typescript")
if ts.Dependencies != "/app/runtime" {
t.Fatalf("typescript bundles run with %q", ts.Dependencies)
}
for _, language := range []string{"go", "python"} {
chain, _ := ToolchainFor(language)
if chain.Dependencies != "" {
t.Fatalf("%s copies %q into every bundle, and its bundles carry their own", language, chain.Dependencies)
}
}
}
// **Refused before anything is built, naming what to build first.** A base the mesh has not built
// is not a compile that fails on its first line — it is a question somebody can answer, and saying
// it early is the difference between a fixable message and one about a missing image.
// A language the mesh does not build is refused the same way, and names what it can build.
// **One module, two bundles, and neither packs the other.**
//
// The case that matters for real modules: a module is one piece of software and may still carry a
// daemon in one language and tools in another (ADR 0040). An earlier version of this compiled
// every bundle into the toolchain's single output directory, so two of them would overwrite each
// other and then be packed together — one artifact containing both, twice.