|
1 | 1 | import * as assert from "assert"; |
| 2 | +import * as fs from "fs"; |
2 | 3 | import * as path from "path"; |
3 | 4 | import { EOL } from "os"; |
4 | 5 | import { SysInfo } from "../src/sys-info"; |
@@ -910,4 +911,134 @@ Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)`), |
910 | 911 | }); |
911 | 912 | }); |
912 | 913 | }); |
| 914 | + |
| 915 | + describe("isCocoaPodsWorkingCorrectly", () => { |
| 916 | + interface ICocoaPodsMockOptions { |
| 917 | + // Mimics the ChildProcess result for `asdf current ruby`. |
| 918 | + // When omitted, asdf is treated as not installed. |
| 919 | + asdfResult?: { |
| 920 | + stdout?: string; |
| 921 | + stderr?: string; |
| 922 | + exitCode?: number | string; |
| 923 | + }; |
| 924 | + podExitCode?: number; |
| 925 | + } |
| 926 | + |
| 927 | + const createCocoaPodsSysInfo = (options: ICocoaPodsMockOptions) => { |
| 928 | + const appendedFiles: { filePath: string; text: string }[] = []; |
| 929 | + const spawnCalls: { |
| 930 | + command: string; |
| 931 | + options?: ISpawnFromEventOptions; |
| 932 | + }[] = []; |
| 933 | + |
| 934 | + const childProcess: any = { |
| 935 | + spawnFromEvent: async ( |
| 936 | + command: string, |
| 937 | + args: string[], |
| 938 | + event: string, |
| 939 | + spawnFromEventOptions?: ISpawnFromEventOptions, |
| 940 | + ) => { |
| 941 | + const fullCommand = `${command} ${args.join(" ")}`; |
| 942 | + spawnCalls.push({ |
| 943 | + command: fullCommand, |
| 944 | + options: spawnFromEventOptions, |
| 945 | + }); |
| 946 | + |
| 947 | + if (fullCommand === "asdf current ruby") { |
| 948 | + // Mirror the ChildProcess wrapper: with `ignoreError` it always |
| 949 | + // resolves, surfacing a non-zero exitCode instead of throwing when |
| 950 | + // asdf is missing/misconfigured. |
| 951 | + return ( |
| 952 | + options.asdfResult || { |
| 953 | + stdout: "", |
| 954 | + stderr: "spawn asdf ENOENT", |
| 955 | + exitCode: "ENOENT", |
| 956 | + } |
| 957 | + ); |
| 958 | + } |
| 959 | + |
| 960 | + return { |
| 961 | + stdout: "", |
| 962 | + stderr: "", |
| 963 | + exitCode: options.podExitCode ?? 0, |
| 964 | + }; |
| 965 | + }, |
| 966 | + exec: async () => ({ stdout: "", stderr: "" }), |
| 967 | + execFile: async (): Promise<any> => undefined, |
| 968 | + execSync: (): string => null, |
| 969 | + }; |
| 970 | + |
| 971 | + const fileSystem: any = { |
| 972 | + exists: () => true, |
| 973 | + extractZip: () => Promise.resolve(), |
| 974 | + readDirectory: () => [], |
| 975 | + appendFile: (filePath: string, text: string) => { |
| 976 | + appendedFiles.push({ filePath, text }); |
| 977 | + return true; |
| 978 | + }, |
| 979 | + deleteEntry: (filePath: string) => |
| 980 | + fs.rmSync(filePath, { recursive: true, force: true }), |
| 981 | + }; |
| 982 | + |
| 983 | + const hostInfo: any = { |
| 984 | + isDarwin: true, |
| 985 | + isWindows: false, |
| 986 | + isLinux: false, |
| 987 | + }; |
| 988 | + |
| 989 | + const helpers = new Helpers(hostInfo); |
| 990 | + const sysInfo = new SysInfo( |
| 991 | + childProcess, |
| 992 | + fileSystem, |
| 993 | + helpers, |
| 994 | + hostInfo, |
| 995 | + null, |
| 996 | + androidToolsInfo, |
| 997 | + ); |
| 998 | + |
| 999 | + return { sysInfo, appendedFiles, spawnCalls }; |
| 1000 | + }; |
| 1001 | + |
| 1002 | + it("writes the active Ruby version to .tool-versions when asdf is available", async () => { |
| 1003 | + const { sysInfo, appendedFiles, spawnCalls } = createCocoaPodsSysInfo({ |
| 1004 | + asdfResult: { |
| 1005 | + stdout: |
| 1006 | + "ruby 3.2.1 /Users/user/app/.tool-versions", |
| 1007 | + exitCode: 0, |
| 1008 | + }, |
| 1009 | + }); |
| 1010 | + |
| 1011 | + const result = await sysInfo.isCocoaPodsWorkingCorrectly(); |
| 1012 | + |
| 1013 | + assert.deepEqual(result, true); |
| 1014 | + assert.deepEqual(appendedFiles.length, 1); |
| 1015 | + assert.ok( |
| 1016 | + appendedFiles[0].filePath.endsWith( |
| 1017 | + path.join("cocoapods", ".tool-versions"), |
| 1018 | + ), |
| 1019 | + ); |
| 1020 | + // The entry must be newline-terminated so it does not merge with existing content. |
| 1021 | + assert.deepEqual(appendedFiles[0].text, "ruby 3.2.1\n"); |
| 1022 | + |
| 1023 | + const asdfCall = spawnCalls.find( |
| 1024 | + (c) => c.command === "asdf current ruby", |
| 1025 | + ); |
| 1026 | + assert.ok(asdfCall, "expected asdf to be probed"); |
| 1027 | + // The probe must not throw when asdf is missing/misconfigured... |
| 1028 | + assert.deepEqual(asdfCall.options.ignoreError, true); |
| 1029 | + // ...and it must resolve the version relative to the invocation directory. |
| 1030 | + assert.deepEqual(asdfCall.options.spawnOptions.cwd, process.cwd()); |
| 1031 | + }); |
| 1032 | + |
| 1033 | + it("does not write .tool-versions and stays healthy when asdf is not installed", async () => { |
| 1034 | + const { sysInfo, appendedFiles } = createCocoaPodsSysInfo({ |
| 1035 | + // asdf missing -> wrapper resolves with a non-zero (ENOENT) exit code. |
| 1036 | + }); |
| 1037 | + |
| 1038 | + const result = await sysInfo.isCocoaPodsWorkingCorrectly(); |
| 1039 | + |
| 1040 | + assert.deepEqual(result, true); |
| 1041 | + assert.deepEqual(appendedFiles.length, 0); |
| 1042 | + }); |
| 1043 | + }); |
913 | 1044 | }); |
0 commit comments