@@ -93,6 +93,62 @@ public async Task TryResolvePathExecutable_Unix_ResolvesGeminiBinary()
9393 }
9494 }
9595
96+ [ Test ]
97+ public async Task TryResolveNpmInstalledBinary_ResolvesPrimaryVendoredBinary ( )
98+ {
99+ var targetTriple = GeminiCliLocator . GetCurrentTargetTriple ( ) ;
100+ if ( targetTriple is null )
101+ {
102+ return ;
103+ }
104+
105+ var sandboxDirectory = CreateSandboxDirectory ( ) ;
106+
107+ try
108+ {
109+ var binaryPath = CreateVendoredBinaryPath ( sandboxDirectory , targetTriple , nested : false ) ;
110+ Directory . CreateDirectory ( Path . GetDirectoryName ( binaryPath ) ! ) ;
111+ await File . WriteAllTextAsync ( binaryPath , "binary" ) ;
112+
113+ var resolved = GeminiCliLocator . TryResolveNpmInstalledBinary ( [ sandboxDirectory ] , targetTriple , OperatingSystem . IsWindows ( ) , out var executablePath ) ;
114+
115+ await Assert . That ( resolved ) . IsTrue ( ) ;
116+ await Assert . That ( executablePath ) . IsEqualTo ( binaryPath ) ;
117+ }
118+ finally
119+ {
120+ Directory . Delete ( sandboxDirectory , recursive : true ) ;
121+ }
122+ }
123+
124+ [ Test ]
125+ public async Task TryResolveNpmInstalledBinary_ResolvesNestedVendoredBinary ( )
126+ {
127+ var targetTriple = GeminiCliLocator . GetCurrentTargetTriple ( ) ;
128+ if ( targetTriple is null )
129+ {
130+ return ;
131+ }
132+
133+ var sandboxDirectory = CreateSandboxDirectory ( ) ;
134+
135+ try
136+ {
137+ var binaryPath = CreateVendoredBinaryPath ( sandboxDirectory , targetTriple , nested : true ) ;
138+ Directory . CreateDirectory ( Path . GetDirectoryName ( binaryPath ) ! ) ;
139+ await File . WriteAllTextAsync ( binaryPath , "binary" ) ;
140+
141+ var resolved = GeminiCliLocator . TryResolveNpmInstalledBinary ( [ sandboxDirectory ] , targetTriple , OperatingSystem . IsWindows ( ) , out var executablePath ) ;
142+
143+ await Assert . That ( resolved ) . IsTrue ( ) ;
144+ await Assert . That ( executablePath ) . IsEqualTo ( binaryPath ) ;
145+ }
146+ finally
147+ {
148+ Directory . Delete ( sandboxDirectory , recursive : true ) ;
149+ }
150+ }
151+
96152 private static string CreateSandboxDirectory ( )
97153 {
98154 var sandboxDirectory = Path . Combine (
@@ -103,4 +159,48 @@ private static string CreateSandboxDirectory()
103159 Directory . CreateDirectory ( sandboxDirectory ) ;
104160 return sandboxDirectory ;
105161 }
162+
163+ private static string CreateVendoredBinaryPath ( string sandboxDirectory , string targetTriple , bool nested )
164+ {
165+ var packageDirectory = GetPackageDirectory ( targetTriple ) ;
166+ var executableName = OperatingSystem . IsWindows ( )
167+ ? GeminiCliLocator . GeminiWindowsExecutableName
168+ : GeminiCliLocator . GeminiExecutableName ;
169+
170+ var segments = new List < string >
171+ {
172+ sandboxDirectory ,
173+ "node_modules" ,
174+ "@google" ,
175+ } ;
176+
177+ if ( nested )
178+ {
179+ segments . Add ( "gemini" ) ;
180+ segments . Add ( "node_modules" ) ;
181+ segments . Add ( "@google" ) ;
182+ }
183+
184+ segments . Add ( packageDirectory ) ;
185+ segments . Add ( "vendor" ) ;
186+ segments . Add ( targetTriple ) ;
187+ segments . Add ( "gemini" ) ;
188+ segments . Add ( executableName ) ;
189+
190+ return Path . Combine ( [ .. segments ] ) ;
191+ }
192+
193+ private static string GetPackageDirectory ( string targetTriple )
194+ {
195+ return targetTriple switch
196+ {
197+ "x86_64-unknown-linux-musl" => "gemini-cli-linux-x64" ,
198+ "aarch64-unknown-linux-musl" => "gemini-cli-linux-arm64" ,
199+ "x86_64-apple-darwin" => "gemini-cli-darwin-x64" ,
200+ "aarch64-apple-darwin" => "gemini-cli-darwin-arm64" ,
201+ "x86_64-pc-windows-msvc" => "gemini-cli-win32-x64" ,
202+ "aarch64-pc-windows-msvc" => "gemini-cli-win32-arm64" ,
203+ _ => throw new InvalidOperationException ( $ "Unsupported test target triple: { targetTriple } ") ,
204+ } ;
205+ }
106206}
0 commit comments