Files
mesh-controller/internal/builder/toolchain_test.go
T
jschoubben 4b7bd1b3e2 A module says what it is written in, and needs no Dockerfile
The bundle recipe: the one that both builds and packs. An archive packs a
directory as it stands, so shipping compiled output meant compiling somewhere
first — which meant a Dockerfile repeating the same incantation in every module.
Two base arguments with no defaults, a working directory chosen so the SDK
resolves upward, the compiler invoked by absolute path because the usual symlink
is resolved away when the base image is assembled, a second stage, an environment
variable naming the entrypoints. Most of the catalogue is unconverted and that is
why; two conversions done in one session were each wrong twice with a working
example open in the next window.

A bundle says a language and a list of entrypoints. The mesh knows what the
language implies. Anything a module could override there it would be writing a
Dockerfile to override, so a toolchain is deliberately not configurable.

Declared rather than inferred, both of them: guessing the language from which
files are present makes a build depend on a directory listing, and guessing the
entrypoints makes it change meaning when somebody adds a helper.

A toolchain the mesh does not hold is refused before anything is compiled, naming
what to build first — the same treatment a missing base already gets, because it
is the same question and somebody can answer it. A language the mesh does not
build is refused saying what would have worked, since the author is usually one
word away.

The list of languages is closed and adding to it is a decision. Every language is
another implementation of the contracts every module shares, and those change
rarely and cascade when they do (ADR 0039) — a mesh whose SDKs disagree about the
envelope fails by ignoring messages rather than by failing to compile.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2026-09-15 01:59:07 +02:00

57 lines
1.9 KiB
Go

package builder
import (
"strings"
"testing"
)
// A language the mesh builds resolves to the toolchain that builds it.
func TestADeclaredLanguageSelectsItsToolchain(t *testing.T) {
chain, err := ToolchainFor("typescript")
if err != nil {
t.Fatalf("typescript is not buildable: %v", err)
}
if chain.Base == "" || chain.Artifact == "" {
t.Fatalf("a toolchain names no base to compile in: %+v", chain)
}
if len(chain.Compile) == 0 || chain.Output == "" {
t.Fatalf("a toolchain says nothing about how to compile or where output lands: %+v", chain)
}
}
// Case and stray whitespace are a module author's slip, not a different language.
func TestALanguageIsMatchedLoosely(t *testing.T) {
for _, said := range []string{"TypeScript", " typescript ", "TYPESCRIPT"} {
if _, err := ToolchainFor(said); err != nil {
t.Fatalf("%q was refused: %v", said, err)
}
}
}
// **A refusal says what would have worked.** A module author who names a language the mesh does
// not build is one word away from a language it does, and a bare "unsupported" makes them go
// looking for a list that exists in one place in the source.
func TestAnUnknownLanguageSaysWhatIsBuildable(t *testing.T) {
_, err := ToolchainFor("cobol")
if err == nil {
t.Fatal("a language nothing can build was accepted")
}
for _, want := range Languages() {
if !strings.Contains(err.Error(), want) {
t.Fatalf("the refusal does not mention %q, which would have worked: %v", want, err)
}
}
}
// And saying nothing is its own message: the mesh chooses the compiler, so a bundle that names no
// language has not asked for anything in particular — which is a mistake rather than a default.
func TestABundleMustSayWhatItIsWrittenIn(t *testing.T) {
_, err := ToolchainFor("")
if err == nil {
t.Fatal("a bundle with no language was accepted, so the mesh guessed a compiler")
}
if !strings.Contains(err.Error(), "must say") {
t.Fatalf("the refusal does not say a language is required: %v", err)
}
}