package embeddedcli import ( "archive/tar" "bytes" "compress/gzip" "crypto/sha256" "os" "path/filepath" "runtime" "strings" "testing" ) func resetGlobals() { setupMu.Lock() defer setupMu.Unlock() config = Config{} setupDone = false pathInitialized = false runtimeLibPath = "" runtimePath = "" runtimeAssetsInstalled = false linuxMuslBundle = false } func TestInstallRuntimeWritesRetainedAssets(t *testing.T) { resetGlobals() tempDir := t.TempDir() cli := []byte("cli") wrapper := []byte("wrapper") node := []byte("runtime") assets := runtimeAssetsArchive(t, map[string]assetFixture{ "ripgrep/bin/test-platform/rg": {content: []byte("ripgrep"), mode: 0755}, "definitions/future.json": {content: []byte("{}"), mode: 0644}, }) cliHash := sha256.Sum256(cli) wrapperHash := sha256.Sum256(wrapper) nodeHash := sha256.Sum256(node) assetsHash := sha256.Sum256(assets) Setup(Config{ Cli: bytes.NewReader(cli), CliHash: cliHash[:], RuntimeExecutable: bytes.NewReader(wrapper), RuntimeExecutableHash: wrapperHash[:], RuntimeNode: bytes.NewReader(node), RuntimeNodeHash: nodeHash[:], RuntimeAssets: bytes.NewReader(assets), RuntimeAssetsHash: assetsHash[:], Version: "1.2.3", Dir: tempDir, }) gotWrapper, err := installRuntimeAt(tempDir) if err != nil { t.Fatal(err) } installDir := filepath.Dir(gotWrapper) if got, err := os.ReadFile(filepath.Join(installDir, "ripgrep", "bin", "test-platform", "rg")); err != nil || string(got) != "ripgrep" { t.Fatalf("ripgrep content=%q err=%v", got, err) } if got, err := os.ReadFile(filepath.Join(installDir, "definitions", "future.json")); err != nil || string(got) != "{}" { t.Fatalf("definition content=%q err=%v", got, err) } } type assetFixture struct { content []byte mode int64 } func runtimeAssetsArchive(t *testing.T, files map[string]assetFixture) []byte { t.Helper() var buffer bytes.Buffer gzipWriter := gzip.NewWriter(&buffer) tarWriter := tar.NewWriter(gzipWriter) for name, fixture := range files { header := &tar.Header{Name: name, Mode: fixture.mode, Size: int64(len(fixture.content)), Typeflag: tar.TypeReg} if err := tarWriter.WriteHeader(header); err != nil { t.Fatal(err) } if _, err := tarWriter.Write(fixture.content); err != nil { t.Fatal(err) } } if err := tarWriter.Close(); err != nil { t.Fatal(err) } if err := gzipWriter.Close(); err != nil { t.Fatal(err) } return buffer.Bytes() } func TestInstallRuntimeWritesAdjacentPairWithoutCLI(t *testing.T) { resetGlobals() tempDir := t.TempDir() cli := []byte("cli") wrapper := []byte("wrapper") node := []byte("runtime") cliHash := sha256.Sum256(cli) wrapperHash := sha256.Sum256(wrapper) nodeHash := sha256.Sum256(node) Setup(Config{ Cli: bytes.NewReader(cli), CliHash: cliHash[:], RuntimeExecutable: bytes.NewReader(wrapper), RuntimeExecutableHash: wrapperHash[:], RuntimeNode: bytes.NewReader(node), RuntimeNodeHash: nodeHash[:], Version: "1.2.3", Dir: tempDir, }) gotWrapper, err := installRuntimeAt(tempDir) if err != nil { t.Fatal(err) } if gotWrapper != filepath.Join(tempDir, "1.2.3", runtimeExecutableName()) { t.Fatalf("RuntimePath() = %q", gotWrapper) } if got, err := os.ReadFile(filepath.Join(filepath.Dir(gotWrapper), "runtime.node")); err != nil || !bytes.Equal(got, node) { t.Fatalf("runtime.node content=%q err=%v", got, err) } if cliPath := filepath.Join(filepath.Dir(gotWrapper), binaryNameForOS()); fileExists(cliPath) { t.Fatalf("managed runtime installation unexpectedly materialized CLI host at %q", cliPath) } } func fileExists(path string) bool { _, err := os.Stat(path) return err == nil } func TestInstallVerifiedFileRestoresExecutablePermission(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("Windows does not use Unix execute bits") } path := filepath.Join(t.TempDir(), "copilot-runtime") content := []byte("wrapper") hash := sha256.Sum256(content) if err := os.WriteFile(path, content, 0644); err != nil { t.Fatal(err) } if err := installVerifiedFile(path, bytes.NewReader(content), hash[:], 0755, "runtime wrapper"); err != nil { t.Fatal(err) } info, err := os.Stat(path) if err != nil { t.Fatal(err) } if info.Mode().Perm()&0111 == 0 { t.Fatal("existing runtime wrapper was not made executable") } } func mustPanic(t *testing.T, fn func()) { t.Helper() defer func() { if r := recover(); r == nil { t.Fatalf("expected panic") } }() fn() } func binaryNameForOS() string { name := "copilot" if runtime.GOOS == "windows" { name += ".exe" } return name } func TestLinuxMuslConfigSelectsAlternativeArtifacts(t *testing.T) { glibcCLI := strings.NewReader("glibc-cli") glibcRuntime := strings.NewReader("glibc-runtime") muslCLI := strings.NewReader("musl-cli") muslRuntime := strings.NewReader("musl-runtime") muslCLIHash := bytes.Repeat([]byte{1}, sha256.Size) muslRuntimeHash := bytes.Repeat([]byte{2}, sha256.Size) selected := linuxMuslConfig(Config{ Cli: glibcCLI, RuntimeLib: glibcRuntime, LinuxMuslCli: muslCLI, LinuxMuslCliHash: muslCLIHash, LinuxMuslRuntimeLib: muslRuntime, LinuxMuslRuntimeLibHash: muslRuntimeHash, }) if selected.Cli != muslCLI || selected.RuntimeLib != muslRuntime { t.Fatal("Expected Linux musl artifacts to replace the glibc artifacts") } if !bytes.Equal(selected.CliHash, muslCLIHash) || !bytes.Equal(selected.RuntimeLibHash, muslRuntimeHash) { t.Fatal("Expected Linux musl hashes to replace the glibc hashes") } } func TestSetupPanicsOnNilCli(t *testing.T) { resetGlobals() mustPanic(t, func() { Setup(Config{}) }) } func TestSetupPanicsOnSecondCall(t *testing.T) { resetGlobals() hash := sha256.Sum256([]byte("ok")) Setup(Config{Cli: bytes.NewReader([]byte("ok")), CliHash: hash[:]}) hash2 := sha256.Sum256([]byte("ok")) mustPanic(t, func() { Setup(Config{Cli: bytes.NewReader([]byte("ok")), CliHash: hash2[:]}) }) resetGlobals() } func TestInstallAtWritesBinaryAndLicense(t *testing.T) { resetGlobals() tempDir := t.TempDir() content := []byte("hello") hash := sha256.Sum256(content) Setup(Config{ Cli: bytes.NewReader(content), CliHash: hash[:], License: []byte("license"), Version: "1.2.3", Dir: tempDir, }) path := Path() expectedPath := filepath.Join(tempDir, "1.2.3", binaryNameForOS()) if path != expectedPath { t.Fatalf("unexpected path: got %q want %q", path, expectedPath) } got, err := os.ReadFile(path) if err != nil { t.Fatalf("read binary: %v", err) } if !bytes.Equal(got, content) { t.Fatalf("binary content mismatch") } licensePath := path + ".license" license, err := os.ReadFile(licensePath) if err != nil { t.Fatalf("read license: %v", err) } if string(license) != "license" { t.Fatalf("license content mismatch") } gotHash, err := hashFile(path) if err != nil { t.Fatalf("hash file: %v", err) } if !bytes.Equal(gotHash, hash[:]) { t.Fatalf("hash mismatch") } } func TestInstallAtInstallsRuntimePairAndAssetsWithoutRuntimeLib(t *testing.T) { resetGlobals() tempDir := t.TempDir() wrapper := []byte("wrapper") node := []byte("runtime") assets := runtimeAssetsArchive(t, map[string]assetFixture{ "definitions/future.json": {content: []byte("{}"), mode: 0644}, }) wrapperHash := sha256.Sum256(wrapper) nodeHash := sha256.Sum256(node) assetsHash := sha256.Sum256(assets) Setup(Config{ Cli: bytes.NewReader(wrapper), CliHash: wrapperHash[:], RuntimeExecutable: bytes.NewReader(wrapper), RuntimeExecutableHash: wrapperHash[:], RuntimeNode: bytes.NewReader(node), RuntimeNodeHash: nodeHash[:], RuntimeAssets: bytes.NewReader(assets), RuntimeAssetsHash: assetsHash[:], Version: "1.2.3", Dir: tempDir, }) path, err := installAt(tempDir) if err != nil { t.Fatal(err) } installDir := filepath.Dir(path) if got, err := os.ReadFile(filepath.Join(installDir, runtimeExecutableName())); err != nil || !bytes.Equal(got, wrapper) { t.Fatalf("runtime wrapper content=%q err=%v", got, err) } if got, err := os.ReadFile(filepath.Join(installDir, "runtime.node")); err != nil || !bytes.Equal(got, node) { t.Fatalf("runtime.node content=%q err=%v", got, err) } if got, err := os.ReadFile(filepath.Join(installDir, "definitions", "future.json")); err != nil || string(got) != "{}" { t.Fatalf("definition content=%q err=%v", got, err) } } func TestInstallAtExistingBinaryHashMismatch(t *testing.T) { resetGlobals() tempDir := t.TempDir() binaryPath := filepath.Join(tempDir, binaryNameForOS()) if err := os.MkdirAll(filepath.Dir(binaryPath), 0755); err != nil { t.Fatalf("mkdir: %v", err) } if err := os.WriteFile(binaryPath, []byte("bad"), 0755); err != nil { t.Fatalf("write binary: %v", err) } goodHash := sha256.Sum256([]byte("good")) config = Config{ Cli: bytes.NewReader([]byte("good")), CliHash: goodHash[:], } _, err := installAt(tempDir) if err == nil || !strings.Contains(err.Error(), "hash mismatch") { t.Fatalf("expected hash mismatch error, got %v", err) } } func TestSanitizeVersion(t *testing.T) { tests := map[string]string{ "v1.2.3+build/abc": "v1.2.3_build_abc", ".": "_", "..": "__", } for input, want := range tests { if got := sanitizeVersion(input); got != want { t.Errorf("sanitizeVersion(%q) = %q want %q", input, got, want) } } } func TestInstallAtAllowsMultipleRuntimeVersions(t *testing.T) { resetGlobals() tempDir := t.TempDir() installVersion := func(version string, cliContent, runtimeContent []byte) (string, string) { t.Helper() cliHash := sha256.Sum256(cliContent) runtimeHash := sha256.Sum256(runtimeContent) config = Config{ Cli: bytes.NewReader(cliContent), CliHash: cliHash[:], RuntimeLib: bytes.NewReader(runtimeContent), RuntimeLibHash: runtimeHash[:], Version: version, } cliPath, err := installAt(tempDir) if err != nil { t.Fatalf("install version %s: %v", version, err) } return cliPath, runtimeLibPath } cli1, runtime1 := installVersion("1.0.0", []byte("cli-one"), []byte("runtime-one")) cli2, runtime2 := installVersion("2.0.0", []byte("cli-two"), []byte("runtime-two")) if cli1 == cli2 { t.Fatalf("Expected versioned CLI paths to differ, got %q", cli1) } if runtime1 == runtime2 { t.Fatalf("Expected versioned runtime paths to differ, got %q", runtime1) } if got, want := filepath.Base(cli1), binaryNameForOS(); got != want { t.Fatalf("First CLI filename = %q, want %q", got, want) } if got, want := filepath.Base(runtime1), naturalRuntimeLibName(); got != want { t.Fatalf("First runtime filename = %q, want %q", got, want) } if got, want := filepath.Base(filepath.Dir(cli1)), "1.0.0"; got != want { t.Fatalf("First CLI version directory = %q, want %q", got, want) } if filepath.Dir(cli1) != filepath.Dir(runtime1) { t.Fatalf("CLI and runtime were installed in different directories: %q and %q", cli1, runtime1) } if got, err := os.ReadFile(runtime1); err != nil || string(got) != "runtime-one" { t.Fatalf("Unexpected first runtime: content=%q err=%v", got, err) } if got, err := os.ReadFile(runtime2); err != nil || string(got) != "runtime-two" { t.Fatalf("Unexpected second runtime: content=%q err=%v", got, err) } } func TestInstallAtExistingBinaryInstallsMissingRuntime(t *testing.T) { resetGlobals() tempDir := t.TempDir() versionDir := filepath.Join(tempDir, "1.2.3") if err := os.MkdirAll(versionDir, 0755); err != nil { t.Fatalf("mkdir: %v", err) } cliContent := []byte("cli") cliPath := filepath.Join(versionDir, binaryNameForOS()) if err := os.WriteFile(cliPath, cliContent, 0755); err != nil { t.Fatalf("write CLI: %v", err) } cliHash := sha256.Sum256(cliContent) runtimeContent := []byte("runtime") runtimeHash := sha256.Sum256(runtimeContent) config = Config{ Cli: bytes.NewReader(cliContent), CliHash: cliHash[:], RuntimeLib: bytes.NewReader(runtimeContent), RuntimeLibHash: runtimeHash[:], Version: "1.2.3", } gotCLIPath, err := installAt(tempDir) if err != nil { t.Fatalf("installAt(): %v", err) } if gotCLIPath != cliPath { t.Fatalf("installAt() = %q, want %q", gotCLIPath, cliPath) } if got, err := os.ReadFile(filepath.Join(versionDir, naturalRuntimeLibName())); err != nil || string(got) != "runtime" { t.Fatalf("Unexpected runtime: content=%q err=%v", got, err) } }