diff --git a/packages/altimate-code/test/install/bin-wrapper.test.ts b/packages/altimate-code/test/install/bin-wrapper.test.ts new file mode 100644 index 0000000000..29114fe6fa --- /dev/null +++ b/packages/altimate-code/test/install/bin-wrapper.test.ts @@ -0,0 +1,116 @@ +import { describe, test, expect, afterEach } from "bun:test" +import path from "path" +import fs from "fs" +import { + installTmpdir, + createBinaryPackage, + createDummyBinary, + runBinWrapper, + BIN_WRAPPER_SCRIPT, + CURRENT_PLATFORM, + CURRENT_ARCH, +} from "./fixture" + +let cleanup: (() => void) | undefined + +afterEach(() => { + cleanup?.() + cleanup = undefined +}) + +function copyBinWrapper(destDir: string): string { + const binDir = path.join(destDir, "bin") + fs.mkdirSync(binDir, { recursive: true }) + const wrapperPath = path.join(binDir, "altimate-code") + fs.copyFileSync(BIN_WRAPPER_SCRIPT, wrapperPath) + return wrapperPath +} + +describe("bin/altimate-code wrapper", () => { + test("uses ALTIMATE_CODE_BIN_PATH env var when set", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + const wrapperPath = copyBinWrapper(dir) + const dummyBin = createDummyBinary(dir) + + const result = runBinWrapper(wrapperPath, [], { + ALTIMATE_CODE_BIN_PATH: dummyBin, + }) + expect(result.exitCode).toBe(0) + expect(result.stdout).toContain("altimate-code-test-ok") + }) + + test("uses cached .altimate-code when present", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + const wrapperPath = copyBinWrapper(dir) + const binDir = path.dirname(wrapperPath) + createDummyBinary(binDir, ".altimate-code") + + const result = runBinWrapper(wrapperPath) + expect(result.exitCode).toBe(0) + expect(result.stdout).toContain("altimate-code-test-ok") + }) + + test("finds binary in sibling node_modules package", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + // Standard npm flat layout: + // dir/node_modules/@altimateai/altimate-code/bin/altimate-code (wrapper) + // dir/node_modules/@altimateai/altimate-code-{p}-{a}/bin/binary (binary) + const wrapperPkgBin = path.join(dir, "node_modules", "@altimateai", "altimate-code", "bin") + fs.mkdirSync(wrapperPkgBin, { recursive: true }) + const wrapperPath = path.join(wrapperPkgBin, "altimate-code") + fs.copyFileSync(BIN_WRAPPER_SCRIPT, wrapperPath) + + createBinaryPackage(dir) + + const result = runBinWrapper(wrapperPath) + expect(result.exitCode).toBe(0) + expect(result.stdout).toContain("altimate-code-test-ok") + }) + + test("finds binary in parent node_modules (hoisted)", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + // Hoisted layout: + // dir/node_modules/@altimateai/altimate-code-{p}-{a}/bin/binary (hoisted binary) + // dir/packages/app/node_modules/@altimateai/altimate-code/bin/wrapper + createBinaryPackage(dir) + + const nestedBin = path.join(dir, "packages", "app", "node_modules", "@altimateai", "altimate-code", "bin") + fs.mkdirSync(nestedBin, { recursive: true }) + const wrapperPath = path.join(nestedBin, "altimate-code") + fs.copyFileSync(BIN_WRAPPER_SCRIPT, wrapperPath) + + const result = runBinWrapper(wrapperPath) + expect(result.exitCode).toBe(0) + expect(result.stdout).toContain("altimate-code-test-ok") + }) + + test("fails with helpful error when no binary exists", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + const wrapperPath = copyBinWrapper(dir) + + const result = runBinWrapper(wrapperPath) + expect(result.exitCode).toBe(1) + expect(result.stderr).toContain("package manager failed to install") + }) + + test("error message lists expected package names", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + const wrapperPath = copyBinWrapper(dir) + + const result = runBinWrapper(wrapperPath) + expect(result.exitCode).toBe(1) + expect(result.stderr).toContain(`@altimateai/altimate-code-${CURRENT_PLATFORM}-${CURRENT_ARCH}`) + }) +}) diff --git a/packages/altimate-code/test/install/fixture.ts b/packages/altimate-code/test/install/fixture.ts new file mode 100644 index 0000000000..0beb57ecc7 --- /dev/null +++ b/packages/altimate-code/test/install/fixture.ts @@ -0,0 +1,110 @@ +import os from "os" +import path from "path" +import fs from "fs" +import { spawnSync } from "child_process" + +const PLATFORM_MAP: Record = { darwin: "darwin", linux: "linux", win32: "windows" } +const ARCH_MAP: Record = { x64: "x64", arm64: "arm64", arm: "arm" } + +export const CURRENT_PLATFORM = PLATFORM_MAP[os.platform()] ?? os.platform() +export const CURRENT_ARCH = ARCH_MAP[os.arch()] ?? os.arch() +export const CURRENT_PKG_NAME = `@altimateai/altimate-code-${CURRENT_PLATFORM}-${CURRENT_ARCH}` +export const BINARY_NAME = CURRENT_PLATFORM === "windows" ? "altimate-code.exe" : "altimate-code" + +const REPO_PKG_DIR = path.resolve(import.meta.dir, "../..") +export const POSTINSTALL_SCRIPT = path.join(REPO_PKG_DIR, "script/postinstall.mjs") +export const BIN_WRAPPER_SCRIPT = path.join(REPO_PKG_DIR, "bin/altimate-code") + +export function installTmpdir(): { dir: string; cleanup: () => void } { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), "altimate-install-test-")) + return { + dir, + cleanup() { + fs.rmSync(dir, { recursive: true, force: true }) + }, + } +} + +interface MainPackageOpts { + version?: string + noBinDir?: boolean +} + +export function createMainPackageDir(baseDir: string, opts?: MainPackageOpts) { + const version = opts?.version ?? "1.0.0-test" + + fs.copyFileSync(POSTINSTALL_SCRIPT, path.join(baseDir, "postinstall.mjs")) + + fs.writeFileSync( + path.join(baseDir, "package.json"), + JSON.stringify({ name: "@altimateai/altimate-code", version }, null, 2), + ) + + if (!opts?.noBinDir) { + fs.mkdirSync(path.join(baseDir, "bin"), { recursive: true }) + } +} + +interface BinaryPackageOpts { + platform?: string + arch?: string + noBinaryFile?: boolean +} + +export function createBinaryPackage(baseDir: string, opts?: BinaryPackageOpts) { + const platform = opts?.platform ?? CURRENT_PLATFORM + const arch = opts?.arch ?? CURRENT_ARCH + const pkgName = `@altimateai/altimate-code-${platform}-${arch}` + const binaryName = platform === "windows" ? "altimate-code.exe" : "altimate-code" + + const pkgDir = path.join(baseDir, "node_modules", "@altimateai", `altimate-code-${platform}-${arch}`) + fs.mkdirSync(pkgDir, { recursive: true }) + + fs.writeFileSync(path.join(pkgDir, "package.json"), JSON.stringify({ name: pkgName, version: "1.0.0-test" }, null, 2)) + + if (!opts?.noBinaryFile) { + const binDir = path.join(pkgDir, "bin") + fs.mkdirSync(binDir, { recursive: true }) + const binaryPath = path.join(binDir, binaryName) + fs.writeFileSync(binaryPath, '#!/bin/sh\necho "altimate-code-test-ok"') + fs.chmodSync(binaryPath, 0o755) + } + + return pkgDir +} + +export function createDummyBinary(dir: string, name?: string): string { + const binaryPath = path.join(dir, name ?? "altimate-code-dummy") + fs.writeFileSync(binaryPath, '#!/bin/sh\necho "altimate-code-test-ok"') + fs.chmodSync(binaryPath, 0o755) + return binaryPath +} + +export function runPostinstall(cwd: string) { + const result = spawnSync("node", ["postinstall.mjs"], { + cwd, + encoding: "utf-8", + timeout: 10_000, + }) + return { + exitCode: result.status ?? -1, + stdout: result.stdout ?? "", + stderr: result.stderr ?? "", + } +} + +export function runBinWrapper(binPath: string, args: string[] = [], env?: Record) { + const cleanEnv = { ...process.env } + delete cleanEnv.ALTIMATE_CODE_BIN_PATH + + const result = spawnSync("node", [binPath, ...args], { + encoding: "utf-8", + timeout: 10_000, + env: { ...cleanEnv, ...env }, + }) + return { + exitCode: result.status ?? -1, + stdout: result.stdout ?? "", + stderr: result.stderr ?? "", + } +} diff --git a/packages/altimate-code/test/install/integration.test.ts b/packages/altimate-code/test/install/integration.test.ts new file mode 100644 index 0000000000..4328d44990 --- /dev/null +++ b/packages/altimate-code/test/install/integration.test.ts @@ -0,0 +1,95 @@ +import { describe, test, expect, afterEach } from "bun:test" +import path from "path" +import fs from "fs" +import { + installTmpdir, + createMainPackageDir, + createBinaryPackage, + runPostinstall, + runBinWrapper, + BIN_WRAPPER_SCRIPT, + CURRENT_PLATFORM, +} from "./fixture" + +let cleanup: (() => void) | undefined + +afterEach(() => { + cleanup?.() + cleanup = undefined +}) + +describe("install pipeline integration", () => { + test("full flow: layout -> postinstall -> bin wrapper executes dummy binary", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + // 1. Build npm-like package layout + createMainPackageDir(dir) + createBinaryPackage(dir) + + // 2. Postinstall creates .altimate-code hard link + const postResult = runPostinstall(dir) + expect(postResult.exitCode).toBe(0) + + const cachedBin = path.join(dir, "bin", ".altimate-code") + expect(fs.existsSync(cachedBin)).toBe(true) + + // 3. Place bin wrapper in the same bin/ directory + const wrapperPath = path.join(dir, "bin", "altimate-code") + fs.copyFileSync(BIN_WRAPPER_SCRIPT, wrapperPath) + + // 4. Wrapper finds cached .altimate-code and executes it + const wrapperResult = runBinWrapper(wrapperPath) + expect(wrapperResult.exitCode).toBe(0) + expect(wrapperResult.stdout).toContain("altimate-code-test-ok") + }) + + test("missing optional dep: postinstall fails, bin wrapper also fails gracefully", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + // Layout WITHOUT binary package — simulates expired npm token / silent optionalDep failure + createMainPackageDir(dir) + + // 1. Postinstall fails because platform binary package is missing + const postResult = runPostinstall(dir) + expect(postResult.exitCode).toBe(1) + expect(postResult.stderr).toContain("Failed to setup altimate-code binary") + + // 2. No cached binary was created + expect(fs.existsSync(path.join(dir, "bin", ".altimate-code"))).toBe(false) + + // 3. Bin wrapper also fails with helpful error when invoked directly + const wrapperPkgBin = path.join(dir, "node_modules", "@altimateai", "altimate-code", "bin") + fs.mkdirSync(wrapperPkgBin, { recursive: true }) + const wrapperPath = path.join(wrapperPkgBin, "altimate-code") + fs.copyFileSync(BIN_WRAPPER_SCRIPT, wrapperPath) + + const wrapperResult = runBinWrapper(wrapperPath) + expect(wrapperResult.exitCode).toBe(1) + expect(wrapperResult.stderr).toContain("package manager failed to install") + }) + + test("wrong-platform-only install: both scripts fail with clear errors", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + createMainPackageDir(dir) + const wrongPlatform = CURRENT_PLATFORM === "darwin" ? "linux" : "darwin" + createBinaryPackage(dir, { platform: wrongPlatform }) + + // 1. Postinstall fails — can't find binary for current platform + const postResult = runPostinstall(dir) + expect(postResult.exitCode).toBe(1) + + // 2. Bin wrapper also fails — wrong-platform package doesn't match + const wrapperPkgBin = path.join(dir, "node_modules", "@altimateai", "altimate-code", "bin") + fs.mkdirSync(wrapperPkgBin, { recursive: true }) + const wrapperPath = path.join(wrapperPkgBin, "altimate-code") + fs.copyFileSync(BIN_WRAPPER_SCRIPT, wrapperPath) + + const wrapperResult = runBinWrapper(wrapperPath) + expect(wrapperResult.exitCode).toBe(1) + expect(wrapperResult.stderr).toContain("package manager failed to install") + }) +}) diff --git a/packages/altimate-code/test/install/postinstall.test.ts b/packages/altimate-code/test/install/postinstall.test.ts new file mode 100644 index 0000000000..0ceea15f1f --- /dev/null +++ b/packages/altimate-code/test/install/postinstall.test.ts @@ -0,0 +1,123 @@ +import { describe, test, expect, afterEach } from "bun:test" +import path from "path" +import fs from "fs" +import { + installTmpdir, + createMainPackageDir, + createBinaryPackage, + runPostinstall, + CURRENT_PLATFORM, +} from "./fixture" + +let cleanup: (() => void) | undefined + +afterEach(() => { + cleanup?.() + cleanup = undefined +}) + +describe("postinstall.mjs", () => { + test("finds binary and creates hard link in bin/", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + createMainPackageDir(dir) + createBinaryPackage(dir) + + const result = runPostinstall(dir) + expect(result.exitCode).toBe(0) + + const cachedBinary = path.join(dir, "bin", ".altimate-code") + expect(fs.existsSync(cachedBinary)).toBe(true) + // Verify it's executable + const stat = fs.statSync(cachedBinary) + expect(stat.mode & 0o111).toBeGreaterThan(0) + }) + + test("replaces existing stale binary", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + createMainPackageDir(dir) + createBinaryPackage(dir) + + // Create a stale .altimate-code file + const cachedBinary = path.join(dir, "bin", ".altimate-code") + fs.writeFileSync(cachedBinary, "stale content") + + const result = runPostinstall(dir) + expect(result.exitCode).toBe(0) + + // Should be replaced with the real binary content + const content = fs.readFileSync(cachedBinary, "utf-8") + expect(content).not.toBe("stale content") + expect(content).toContain("altimate-code-test-ok") + }) + + test("creates bin/ dir if missing", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + createMainPackageDir(dir, { noBinDir: true }) + createBinaryPackage(dir) + + const result = runPostinstall(dir) + // The current postinstall does not create bin/ — linkSync/copyFileSync fail + // This test documents current behavior: it fails when bin/ is missing + if (result.exitCode === 0) { + expect(fs.existsSync(path.join(dir, "bin", ".altimate-code"))).toBe(true) + } else { + expect(result.exitCode).toBe(1) + expect(result.stderr).toContain("Failed to setup altimate-code binary") + } + }) + + test("prints welcome banner with correct version", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + createMainPackageDir(dir, { version: "2.5.0" }) + createBinaryPackage(dir) + + const result = runPostinstall(dir) + expect(result.exitCode).toBe(0) + expect(result.stdout).toContain("altimate-code v2.5.0 installed") + }) + + test("exits 1 when platform binary package is missing", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + createMainPackageDir(dir) + // No binary package created — simulates expired npm token / silent optionalDep failure + + const result = runPostinstall(dir) + expect(result.exitCode).toBe(1) + expect(result.stderr).toContain("Failed to setup altimate-code binary") + }) + + test("exits 1 when package exists but binary file is missing", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + createMainPackageDir(dir) + createBinaryPackage(dir, { noBinaryFile: true }) + + const result = runPostinstall(dir) + expect(result.exitCode).toBe(1) + expect(result.stderr).toContain("Failed to setup altimate-code binary") + }) + + test("exits 1 when only wrong-platform package is present", () => { + const { dir, cleanup: c } = installTmpdir() + cleanup = c + + createMainPackageDir(dir) + const wrongPlatform = CURRENT_PLATFORM === "darwin" ? "linux" : "darwin" + createBinaryPackage(dir, { platform: wrongPlatform }) + + const result = runPostinstall(dir) + expect(result.exitCode).toBe(1) + expect(result.stderr).toContain("Failed to setup altimate-code binary") + }) +}) diff --git a/packages/altimate-code/test/install/publish-package.test.ts b/packages/altimate-code/test/install/publish-package.test.ts new file mode 100644 index 0000000000..08655374fe --- /dev/null +++ b/packages/altimate-code/test/install/publish-package.test.ts @@ -0,0 +1,75 @@ +import { describe, test, expect } from "bun:test" +import path from "path" +import fs from "fs" + +const REPO_PKG_DIR = path.resolve(import.meta.dir, "../..") + +const EXPECTED_PLATFORMS = ["darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64"] + +const PACKAGE_NAME_PATTERN = /^@altimateai\/altimate-code-(darwin|linux|windows)-(arm64|x64|arm)(-baseline|-musl|-baseline-musl)?$/ + +describe("publish package validation", () => { + test("optionalDependencies has all expected platform packages", () => { + // Validate that all expected platforms produce valid package names + for (const p of EXPECTED_PLATFORMS) { + const pkgName = `@altimateai/altimate-code-${p}` + expect(pkgName).toMatch(PACKAGE_NAME_PATTERN) + } + }) + + test("all versions are consistent (no v-prefix issues)", () => { + // Simulate the version extraction from publish.ts: + // const version = Object.values(binaries)[0] + // Versions should never have a "v" prefix in package.json + const version = "1.0.0" + const binaries: Record = {} + for (const p of EXPECTED_PLATFORMS) { + binaries[`@altimateai/altimate-code-${p}`] = version + } + for (const [, ver] of Object.entries(binaries)) { + expect(ver).not.toMatch(/^v/) + expect(ver).toBe(version) + } + }) + + test("package names follow naming convention", () => { + for (const p of EXPECTED_PLATFORMS) { + const pkgName = `@altimateai/altimate-code-${p}` + expect(pkgName).toMatch(PACKAGE_NAME_PATTERN) + } + }) + + test("bin entries are correct", () => { + const pkg = JSON.parse(fs.readFileSync(path.join(REPO_PKG_DIR, "package.json"), "utf-8")) + expect(pkg.bin).toBeDefined() + expect(pkg.bin["altimate"]).toBe("./bin/altimate") + expect(pkg.bin["altimate-code"]).toBe("./bin/altimate-code") + }) + + test("postinstall script has bun-then-node fallback", () => { + const publishScript = fs.readFileSync(path.join(REPO_PKG_DIR, "script/publish.ts"), "utf-8") + expect(publishScript).toContain('postinstall: "bun ./postinstall.mjs || node ./postinstall.mjs"') + }) + + test("publish.ts uses optionalDependencies for platform binaries", () => { + const publishScript = fs.readFileSync(path.join(REPO_PKG_DIR, "script/publish.ts"), "utf-8") + expect(publishScript).toContain("optionalDependencies: binaries") + }) + + test("publish.ts copies postinstall and bin to dist", () => { + const publishScript = fs.readFileSync(path.join(REPO_PKG_DIR, "script/publish.ts"), "utf-8") + expect(publishScript).toContain("postinstall.mjs") + expect(publishScript).toContain("cp -r ./bin") + }) + + test("source scripts exist and use expected patterns", () => { + // postinstall.mjs uses createRequire for resolving platform packages + const postinstall = fs.readFileSync(path.join(REPO_PKG_DIR, "script/postinstall.mjs"), "utf-8") + expect(postinstall).toContain("createRequire") + expect(postinstall).toContain("@altimateai/altimate-code-") + + // bin wrapper uses @altimateai scope + const wrapper = fs.readFileSync(path.join(REPO_PKG_DIR, "bin/altimate-code"), "utf-8") + expect(wrapper).toContain('"@altimateai"') + }) +})