@@ -190,6 +190,15 @@ func TestEnsureBinary(t *testing.T) {
190190 defer srv .Close ()
191191
192192 home := t .TempDir ()
193+ // Older installs: an unversioned one from a CLI before versioned installs, and a
194+ // previous version. Neither counts as the pinned version being installed.
195+ depsRoot := filepath .Join (home , agentDepsDir )
196+ for _ , dir := range []string {"node" , "node-v1" } {
197+ writeFakeExec (t , filepath .Join (depsRoot , dir , "bin" ), "npm" , ":" )
198+ }
199+ for _ , dir := range []string {"uv" , "uv-1" } {
200+ writeFakeExec (t , filepath .Join (depsRoot , dir ), "uv" , ":" )
201+ }
193202 for _ , c := range []struct {
194203 spec archiveSpec
195204 wantBinDir string
@@ -200,16 +209,18 @@ func TestEnsureBinary(t *testing.T) {
200209 binaryName : "npm" ,
201210 checksum : hex .EncodeToString (nodeSum [:]),
202211 dirName : "node" ,
212+ version : "v2" ,
203213 binDirName : "bin" ,
204- }, wantBinDir : filepath .Join (home , agentDepsDir , "node" , "bin" ),
214+ }, wantBinDir : filepath .Join (home , agentDepsDir , "node-v2 " , "bin" ),
205215 },
206216 {
207217 spec : archiveSpec {
208218 archiveURL : srv .URL + "/uv.tar.gz" ,
209219 binaryName : "uv" ,
210220 checksum : hex .EncodeToString (uvSum [:]),
211221 dirName : "uv" ,
212- }, wantBinDir : filepath .Join (home , agentDepsDir , "uv" ),
222+ version : "2" ,
223+ }, wantBinDir : filepath .Join (home , agentDepsDir , "uv-2" ),
213224 },
214225 } {
215226 t .Run ("downloads, extracts, and returns the bin dir (" + c .spec .binaryName + ")" , func (t * testing.T ) {
@@ -255,8 +266,8 @@ func TestEnsureToolchain(t *testing.T) {
255266 t .Run (fmt .Sprintf ("no-op when the toolchain is already present (staleLock=%v)" , staleLock ), func (t * testing.T ) {
256267 home := t .TempDir ()
257268 writeFakeExec (t , filepath .Join (home , uvToolBinDir ), ugExecutable , ":" )
258- writeFakeExec (t , filepath . Join ( home , agentDepsDir , "node" , "bin" ), "npm" , ":" )
259- writeFakeExec (t , filepath . Join ( home , agentDepsDir , "uv" ), "uv" , fakeUvRecorder )
269+ writeFakeExec (t , nodeArchiveSpec ( "amd64" ). binDir ( home ), "npm" , ":" )
270+ writeFakeExec (t , uvArchiveSpec ( "amd64" ). binDir ( home ), "uv" , fakeUvRecorder )
260271 if staleLock {
261272 // left by a session killed mid-install
262273 writeFakeExec (t , filepath .Join (home , agentRootDir ), setupLockName , "" )
@@ -274,8 +285,8 @@ func TestEnsureToolchain(t *testing.T) {
274285 home := t .TempDir ()
275286 // uv and npm are present; ug is not, so only ug gets installed and no
276287 // download happens
277- writeFakeExec (t , filepath . Join ( home , agentDepsDir , "uv" ), "uv" , fakeUvRecorder )
278- writeFakeExec (t , filepath . Join ( home , agentDepsDir , "node" , "bin" ), "npm" , ":" )
288+ writeFakeExec (t , uvArchiveSpec ( "amd64" ). binDir ( home ), "uv" , fakeUvRecorder )
289+ writeFakeExec (t , nodeArchiveSpec ( "amd64" ). binDir ( home ), "npm" , ":" )
279290 marker := filepath .Join (t .TempDir (), "uv.log" )
280291 t .Setenv ("FAKE_UV_LOG" , marker )
281292 t .Setenv ("PATH" , t .TempDir ())
@@ -290,11 +301,33 @@ func TestEnsureToolchain(t *testing.T) {
290301 assert .NoFileExists (t , filepath .Join (home , agentRootDir , setupLockName ), "the setup lock is released once setup finishes" )
291302 })
292303
304+ t .Run ("replaces a Unity Gateway CLI from another commit with the pinned one" , func (t * testing.T ) {
305+ home := t .TempDir ()
306+ writeFakeExec (t , uvArchiveSpec ("amd64" ).binDir (home ), "uv" , fakeUvRecorder )
307+ writeFakeExec (t , nodeArchiveSpec ("amd64" ).binDir (home ), "npm" , ":" )
308+ // Installs from an older pin and from a CLI before installs were versioned. Both
309+ // put a ug on PATH, but neither is the pinned commit.
310+ depsRoot := filepath .Join (home , agentDepsDir )
311+ oldBinDirs := []string {filepath .Join (depsRoot , "uv_tools_bin" ), filepath .Join (depsRoot , "uv_tools_bin-oldcommit" )}
312+ for _ , dir := range oldBinDirs {
313+ writeFakeExec (t , dir , ugExecutable , ":" )
314+ }
315+ marker := filepath .Join (t .TempDir (), "uv.log" )
316+ t .Setenv ("FAKE_UV_LOG" , marker )
317+ t .Setenv ("PATH" , strings .Join (oldBinDirs , string (os .PathListSeparator )))
318+
319+ require .NoError (t , ensureToolchain (cmdio .MockDiscard (t .Context ()), home , "amd64" ))
320+
321+ out , err := os .ReadFile (marker )
322+ require .NoError (t , err )
323+ assert .Contains (t , string (out ), "tool install git+https://github.com/" + ugRepo + "@" + ugCommit )
324+ })
325+
293326 t .Run ("recheck after acquiring the lock skips a redundant install" , func (t * testing.T ) {
294327 home := t .TempDir ()
295328 // uv is present and records if it ever installs; it must not be called once a
296329 // concurrent client finishes the toolchain while we wait for the lock.
297- writeFakeExec (t , filepath . Join ( home , agentDepsDir , "uv" ), "uv" , fakeUvRecorder )
330+ writeFakeExec (t , uvArchiveSpec ( "amd64" ). binDir ( home ), "uv" , fakeUvRecorder )
298331 marker := filepath .Join (t .TempDir (), "uv.log" )
299332 t .Setenv ("FAKE_UV_LOG" , marker )
300333 t .Setenv ("PATH" , t .TempDir ())
@@ -310,7 +343,7 @@ func TestEnsureToolchain(t *testing.T) {
310343 // The other client finishes: publish ug+npm into the PATH dir, then release
311344 // the lock so the waiting ensureToolchain can proceed.
312345 writeFakeExec (t , filepath .Join (home , uvToolBinDir ), ugExecutable , ":" )
313- writeFakeExec (t , filepath . Join ( home , agentDepsDir , "node" , "bin" ), "npm" , ":" )
346+ writeFakeExec (t , nodeArchiveSpec ( "amd64" ). binDir ( home ), "npm" , ":" )
314347 unlock ()
315348
316349 require .NoError (t , <- done )
@@ -635,7 +668,7 @@ func TestBootstrapToolchain(t *testing.T) {
635668 t .Run ("returns the path to the Unity Gateway CLI" , func (t * testing.T ) {
636669 home := t .TempDir ()
637670 writeFakeExec (t , filepath .Join (home , uvToolBinDir ), ugExecutable , ":" )
638- writeFakeExec (t , filepath . Join ( home , agentDepsDir , "node" , "bin" ), npmExecutable , ":" )
671+ writeFakeExec (t , nodeArchiveSpec ( "amd64" ). binDir ( home ), npmExecutable , ":" )
639672 t .Setenv ("PATH" , t .TempDir ())
640673
641674 ugPath , err := bootstrapToolchain (cmdio .MockDiscard (t .Context ()), home )
@@ -646,8 +679,8 @@ func TestBootstrapToolchain(t *testing.T) {
646679 t .Run ("reports UNITY_GATEWAY_CLI_NOT_FOUND when the install leaves no CLI on PATH" , func (t * testing.T ) {
647680 home := t .TempDir ()
648681 // uv "succeeds" without installing anything, so the post-setup lookup fails.
649- writeFakeExec (t , filepath . Join ( home , agentDepsDir , "uv" ), "uv" , ":" )
650- writeFakeExec (t , filepath . Join ( home , agentDepsDir , "node" , "bin" ), npmExecutable , ":" )
682+ writeFakeExec (t , uvArchiveSpec ( "amd64" ). binDir ( home ), "uv" , ":" )
683+ writeFakeExec (t , nodeArchiveSpec ( "amd64" ). binDir ( home ), npmExecutable , ":" )
651684 t .Setenv ("PATH" , t .TempDir ())
652685
653686 _ , err := bootstrapToolchain (cmdio .MockDiscard (t .Context ()), home )
0 commit comments