371 lines · 14.5 KB · Swift
RawDownload
import Foundation struct GeminiTestEnvironment {    enum GeminiCLILayout {        case npmNested        case nixShare        case fnmBundle        case homebrewBundle    }     let homeURL: URL    private let geminiDir: URL    private let antigravityDir: URL     init() throws {        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)        try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)        let geminiDir = root.appendingPathComponent(".gemini")        try FileManager.default.createDirectory(at: geminiDir, withIntermediateDirectories: true)        let antigravityDir = root            .appendingPathComponent(".codexbar")            .appendingPathComponent("antigravity")        try FileManager.default.createDirectory(at: antigravityDir, withIntermediateDirectories: true)        self.homeURL = root        self.geminiDir = geminiDir        self.antigravityDir = antigravityDir    }     func cleanup() {        try? FileManager.default.removeItem(at: self.homeURL)    }     func writeSettings(authType: String) throws {        let payload: [String: Any] = [            "security": [                "auth": [                    "selectedType": authType,                ],            ],        ]        let data = try JSONSerialization.data(withJSONObject: payload)        try data.write(to: self.geminiDir.appendingPathComponent("settings.json"), options: .atomic)    }     func writeCredentials(        accessToken: String?,        refreshToken: String?,        expiry: Date,        idToken: String?) throws    {        var payload: [String: Any] = [            "expiry_date": expiry.timeIntervalSince1970 * 1000,        ]        if let accessToken { payload["access_token"] = accessToken }        if let refreshToken { payload["refresh_token"] = refreshToken }        if let idToken { payload["id_token"] = idToken }        let data = try JSONSerialization.data(withJSONObject: payload)        try data.write(to: self.geminiDir.appendingPathComponent("oauth_creds.json"), options: .atomic)    }     func readCredentials() throws -> [String: Any] {        let url = self.geminiDir.appendingPathComponent("oauth_creds.json")        let data = try Data(contentsOf: url)        let object = try JSONSerialization.jsonObject(with: data)        return object as? [String: Any] ?? [:]    }     func writeAntigravityCredentials(        accessToken: String,        refreshToken: String?,        expiry: Date,        idToken: String? = nil,        email: String? = nil,        projectID: String? = nil,        clientID: String? = nil,        clientSecret: String? = nil) throws    {        var payload: [String: Any] = [            "access_token": accessToken,            "expiry_date": expiry.timeIntervalSince1970 * 1000,        ]        if let refreshToken { payload["refresh_token"] = refreshToken }        if let idToken { payload["id_token"] = idToken }        if let email { payload["email"] = email }        if let projectID { payload["project_id"] = projectID }        if let clientID { payload["client_id"] = clientID }        if let clientSecret { payload["client_secret"] = clientSecret }        let data = try JSONSerialization.data(withJSONObject: payload)        try data.write(to: self.antigravityDir.appendingPathComponent("oauth_creds.json"), options: .atomic)    }     func readAntigravityCredentials() throws -> [String: Any] {        let url = self.antigravityDir.appendingPathComponent("oauth_creds.json")        let data = try Data(contentsOf: url)        let object = try JSONSerialization.jsonObject(with: data)        return object as? [String: Any] ?? [:]    }     func writeFakeGeminiCLI(includeOAuth: Bool = true, layout: GeminiCLILayout = .npmNested) throws -> URL {        let base = self.homeURL.appendingPathComponent("gemini-cli")        let binDir = base.appendingPathComponent("bin")        try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true)         switch layout {        case .npmNested:            let oauthPath = base                .appendingPathComponent("lib")                .appendingPathComponent("node_modules")                .appendingPathComponent("@google")                .appendingPathComponent("gemini-cli")                .appendingPathComponent("node_modules")                .appendingPathComponent("@google")                .appendingPathComponent("gemini-cli-core")                .appendingPathComponent("dist")                .appendingPathComponent("src")                .appendingPathComponent("code_assist")                .appendingPathComponent("oauth2.js")             if includeOAuth {                try FileManager.default.createDirectory(                    at: oauthPath.deletingLastPathComponent(),                    withIntermediateDirectories: true)                 let oauthContent = """                const OAUTH_CLIENT_ID = 'test-client-id';                const OAUTH_CLIENT_SECRET = 'test-client-secret';                """                try oauthContent.write(to: oauthPath, atomically: true, encoding: .utf8)            }             let geminiBinary = binDir.appendingPathComponent("gemini")            try "#!/bin/bash\nexit 0\n".write(to: geminiBinary, atomically: true, encoding: .utf8)            try FileManager.default.setAttributes(                [.posixPermissions: 0o755],                ofItemAtPath: geminiBinary.path)            return geminiBinary         case .nixShare:            let oauthPath = base                .appendingPathComponent("share")                .appendingPathComponent("gemini-cli")                .appendingPathComponent("node_modules")                .appendingPathComponent("@google")                .appendingPathComponent("gemini-cli-core")                .appendingPathComponent("dist")                .appendingPathComponent("src")                .appendingPathComponent("code_assist")                .appendingPathComponent("oauth2.js")             if includeOAuth {                try FileManager.default.createDirectory(                    at: oauthPath.deletingLastPathComponent(),                    withIntermediateDirectories: true)                 let oauthContent = """                const OAUTH_CLIENT_ID = 'test-client-id';                const OAUTH_CLIENT_SECRET = 'test-client-secret';                """                try oauthContent.write(to: oauthPath, atomically: true, encoding: .utf8)            }             let geminiBinary = binDir.appendingPathComponent("gemini")            try "#!/bin/bash\nexit 0\n".write(to: geminiBinary, atomically: true, encoding: .utf8)            try FileManager.default.setAttributes(                [.posixPermissions: 0o755],                ofItemAtPath: geminiBinary.path)            return geminiBinary         case .fnmBundle:            // Mirror a real fnm multishell layout: bin/gemini is a single relative            // symlink into the same multishell's lib/node_modules/@google/gemini-cli,            // which is a plain directory with the real package.json + bundle/*.js.            let multishellRoot = self.homeURL                .appendingPathComponent("Library")                .appendingPathComponent("Caches")                .appendingPathComponent("fnm_multishells")                .appendingPathComponent("12345_67890")            let binDir = multishellRoot.appendingPathComponent("bin")            let packageRoot = multishellRoot                .appendingPathComponent("lib")                .appendingPathComponent("node_modules")                .appendingPathComponent("@google")                .appendingPathComponent("gemini-cli")            let bundleDir = packageRoot.appendingPathComponent("bundle")            try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true)            try FileManager.default.createDirectory(at: bundleDir, withIntermediateDirectories: true)             let packageJSON = """            {              "name": "@google/gemini-cli"            }            """            try packageJSON.write(                to: packageRoot.appendingPathComponent("package.json"),                atomically: true,                encoding: .utf8)             let chunkName = "chunk-TEST123.js"            let geminiEntry = bundleDir.appendingPathComponent("gemini.js")            let geminiContent = """            #!/usr/bin/env node            import { start } from "./\(chunkName)";            start();            """            try geminiContent.write(to: geminiEntry, atomically: true, encoding: .utf8)            try FileManager.default.setAttributes(                [.posixPermissions: 0o755],                ofItemAtPath: geminiEntry.path)             let chunkContent = if includeOAuth {                """                export const start = () => {};                const OAUTH_CLIENT_ID = 'test-client-id';                const OAUTH_CLIENT_SECRET = 'test-client-secret';                """            } else {                "export const start = () => {};\n"            }            try chunkContent.write(                to: bundleDir.appendingPathComponent(chunkName),                atomically: true,                encoding: .utf8)             // Relative symlink matching what fnm actually creates:            //   fnm_multishells/XXX/bin/gemini -> ../lib/node_modules/@google/gemini-cli/bundle/gemini.js            // Use the path-based API so the target is stored as a literal relative            // string; the URL-based API resolves URL(fileURLWithPath: "../...") against            // the process CWD, which produces a bogus absolute target.            let geminiBinary = binDir.appendingPathComponent("gemini")            try FileManager.default.createSymbolicLink(                atPath: geminiBinary.path,                withDestinationPath: "../lib/node_modules/@google/gemini-cli/bundle/gemini.js")             return geminiBinary         case .homebrewBundle:            return try self.writeFakeHomebrewGeminiCLI(base: base, includeOAuth: includeOAuth)        }    }     private func writeFakeHomebrewGeminiCLI(base: URL, includeOAuth: Bool) throws -> URL {        let cellarRoot = base            .appendingPathComponent("Cellar")            .appendingPathComponent("gemini-cli")            .appendingPathComponent("0.41.2")        let binDir = cellarRoot.appendingPathComponent("bin")        let packageRoot = cellarRoot            .appendingPathComponent("libexec")            .appendingPathComponent("lib")            .appendingPathComponent("node_modules")            .appendingPathComponent("@google")            .appendingPathComponent("gemini-cli")        let bundleDir = packageRoot.appendingPathComponent("bundle")        try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true)        try FileManager.default.createDirectory(at: bundleDir, withIntermediateDirectories: true)         let packageJSON = """        {          "name": "@google/gemini-cli"        }        """        try packageJSON.write(            to: packageRoot.appendingPathComponent("package.json"),            atomically: true,            encoding: .utf8)         let entry = bundleDir.appendingPathComponent("gemini.js")        try "#!/usr/bin/env node\nawait import('./gemini-HASH.js');\n".write(            to: entry,            atomically: true,            encoding: .utf8)        try FileManager.default.setAttributes(            [.posixPermissions: 0o755],            ofItemAtPath: entry.path)        try "export const run = () => {};\n".write(            to: bundleDir.appendingPathComponent("gemini-HASH.js"),            atomically: true,            encoding: .utf8)         let chunkContent = if includeOAuth {            """            var OAUTH_CLIENT_ID = "test-client-id";            var OAUTH_CLIENT_SECRET = "test-client-secret";            """        } else {            "export const unrelated = true;\n"        }        try chunkContent.write(            to: bundleDir.appendingPathComponent("chunk-OAUTH.js"),            atomically: true,            encoding: .utf8)         let geminiBinary = binDir.appendingPathComponent("gemini")        try FileManager.default.createSymbolicLink(            atPath: geminiBinary.path,            withDestinationPath: "../libexec/lib/node_modules/@google/gemini-cli/bundle/gemini.js")        return geminiBinary    }     func writeFakeFnm(        currentVersion: String = "v24.6.0",        npmRoot: String? = nil,        geminiPackageJSONPath: String,        holdNpmRootStdoutOpen: Bool = false) throws -> URL    {        let binDir = self.homeURL.appendingPathComponent("bin")        try FileManager.default.createDirectory(at: binDir, withIntermediateDirectories: true)         let fnmPath = binDir.appendingPathComponent("fnm")        let stdoutHolder = holdNpmRootStdoutOpen            ? #"""            python3 - <<'PY'            import os            import subprocess            import sys             child = subprocess.Popen(                [sys.executable, "-c", "import time; time.sleep(5)"],                stdin=subprocess.DEVNULL,                stderr=subprocess.DEVNULL,                start_new_session=True,            )            with open(os.environ["CODEXBAR_TEST_CHILD_PID_FILE"], "w") as handle:                handle.write(str(child.pid))            PY            """#            : ":"        let script = if let npmRoot {            """            #!/bin/bash            if [ "$1" = "current" ]; then              printf '%s\n' "\(currentVersion)"              exit 0            fi             if [ "$1" = "exec" ] && [ "$4" = "npm" ] && [ "$5" = "root" ] && [ "$6" = "-g" ]; then              \(stdoutHolder)              printf '%s\n' "\(npmRoot)"              exit 0            fi             if [ "$1" = "exec" ] && [ "$4" = "node" ]; then              printf '%s\n' "\(geminiPackageJSONPath)"              exit 0            fi             exit 1            """        } else {            """            #!/bin/bash            if [ "$1" = "current" ]; then              printf '%s\n' "\(currentVersion)"              exit 0            fi             if [ "$1" = "exec" ]; then              printf '%s\n' "\(geminiPackageJSONPath)"              exit 0            fi             exit 1            """        }        try script.write(to: fnmPath, atomically: true, encoding: .utf8)        try FileManager.default.setAttributes(            [.posixPermissions: 0o755],            ofItemAtPath: fnmPath.path)        return fnmPath    }}