package catalogue import ( "strings" "testing" ) // bundleFor is a manifest whose one artifact is a bundle in the given language and system. func bundleFor(language, system string) Manifest { return Manifest{Module: "a-component", Build: &Build{Artifacts: []Artifact{ {Name: "binary", Kind: ArtifactBundle, Language: language, System: system}, }}} } func problemsOf(t *testing.T, m Manifest) string { t.Helper() return strings.Join(m.Build.problems(m.Module), "\n") } // **A language that compiles to a binary must say which system.** // // A binary is pinned to one operating system at link time, so a host refuses to touch a machine it // was not built for. An artifact that says nothing would be compiled for whatever the build machine // happened to be — which reads as portable and is not, and is the fault this check exists for. func TestABinaryMustSayWhichSystemItIsFor(t *testing.T) { got := problemsOf(t, bundleFor("go", "")) if !strings.Contains(got, "says no system") { t.Fatalf("a compiled bundle with no system was accepted:\n%s", got) } // And the refusal names what it could have said, so a reader is one edit from right. for _, system := range []string{"alpine", "android", "arch"} { if !strings.Contains(got, system) { t.Fatalf("the refusal does not name %q as a choice:\n%s", system, got) } } } func TestABinaryThatNamesASystemIsAccepted(t *testing.T) { if got := problemsOf(t, bundleFor("go", "arch")); got != "" { t.Fatalf("a compiled bundle naming a system was refused:\n%s", got) } } // A system the mesh does not build for is refused where it is written. These are the host's own // names, not a compiler's: the difference between two of them is a C library rather than a kernel, // so a value that looks like an operating system to a toolchain is still wrong here. func TestASystemTheMeshDoesNotBuildForIsRefused(t *testing.T) { for _, wrong := range []string{"linux", "debian", "darwin"} { got := problemsOf(t, bundleFor("go", wrong)) if !strings.Contains(got, "and a system is") { t.Fatalf("%q was accepted as a system:\n%s", wrong, got) } } } // **And a language that runs anywhere must not name one.** A system that decides nothing reads as // though it did, which is the same fault as a restriction that restricts nothing (novox/hq ADR 0045). func TestAPortableBundleMayNotNameASystem(t *testing.T) { got := problemsOf(t, bundleFor("typescript", "arch")) if !strings.Contains(got, "runs anywhere") { t.Fatalf("a portable bundle was allowed to name a system:\n%s", got) } } func TestAPortableBundleNamingNoSystemIsAccepted(t *testing.T) { if got := problemsOf(t, bundleFor("typescript", "")); got != "" { t.Fatalf("an ordinary bundle was refused:\n%s", got) } } // One component, one artifact per system: the shape the mesh's own binaries are declared in, and the // reason the target is the artifact's rather than the recipe's. func TestOneArtifactPerSystemIsAccepted(t *testing.T) { m := Manifest{Module: "the-host", Build: &Build{Artifacts: []Artifact{ {Name: "arch", Kind: ArtifactBundle, Language: "go", System: "arch"}, {Name: "alpine", Kind: ArtifactBundle, Language: "go", System: "alpine"}, {Name: "android", Kind: ArtifactBundle, Language: "go", System: "android"}, }}} if got := problemsOf(t, m); got != "" { t.Fatalf("one artifact per system was refused:\n%s", got) } }