A module is one piece of software and may still carry a daemon in one language, tools in another and a package in a third. The first cut compiled every bundle into the toolchain's single output directory, so two of them would have overwritten each other and then been packed together — one artifact containing both, published twice. So output is a property of the artifact, not of the toolchain, and the toolchain says how it is told where to write rather than where it writes. Under a directory named for the build rather than beside the source, so a pack never sweeps up the module's own working files. A second toolchain is declared so the multi-language path is exercised rather than asserted — a list with one entry cannot fail the way a list with four will. And the fake compiler in the tests now writes where it was TOLD to. One that always wrote to a fixed place would have passed whether or not each artifact got its own directory, which is the whole of what these tests are for. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
90 lines
3.2 KiB
Go
90 lines
3.2 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 {
|
|
t.Fatalf("a toolchain says nothing about how to compile: %+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)
|
|
}
|
|
}
|
|
|
|
// **One module, several languages, and each bundle packed alone.**
|
|
//
|
|
// A module is one piece of software (ADR 0040) and may still carry a daemon in one language, tools
|
|
// in another and a package in a third. Compiling them into one output directory would have them
|
|
// overwrite each other and then be packed together, so output is a property of the artifact rather
|
|
// than of the toolchain.
|
|
func TestTwoBundlesInOneModuleDoNotShareAnOutputDirectory(t *testing.T) {
|
|
first, second := Out("daemon"), Out("tools")
|
|
if first == second {
|
|
t.Fatalf("two artifacts compile into the same place (%q), so one would overwrite the "+
|
|
"other and both would be packed together", first)
|
|
}
|
|
for _, out := range []string{first, second} {
|
|
if strings.HasPrefix(out, "/") || strings.Contains(out, "..") {
|
|
t.Fatalf("%q leaves the module's own directory", out)
|
|
}
|
|
}
|
|
}
|
|
|
|
// And the mesh can say what it builds, which is what a refusal quotes.
|
|
func TestTheMeshSaysWhichLanguagesItBuilds(t *testing.T) {
|
|
spoken := Languages()
|
|
if len(spoken) < 2 {
|
|
t.Fatalf("only %v — this test exists to keep the multi-language path real rather than "+
|
|
"theoretical", spoken)
|
|
}
|
|
for _, language := range spoken {
|
|
if _, err := ToolchainFor(language); err != nil {
|
|
t.Fatalf("%q is listed as buildable and has no toolchain: %v", language, err)
|
|
}
|
|
}
|
|
}
|