@@ -3,7 +3,7 @@ import {expect} from 'chai'
33import { got } from 'got'
44import nock from 'nock'
55import { existsSync } from 'node:fs'
6- import { mkdir , rm , symlink , writeFile } from 'node:fs/promises'
6+ import { mkdir , rm , symlink , utimes , writeFile } from 'node:fs/promises'
77import path from 'node:path'
88import zlib from 'node:zlib'
99import sinon from 'sinon'
@@ -42,6 +42,33 @@ function initUpdater(config: Config): Updater {
4242 return updater
4343}
4444
45+ const setOldMtime = async ( filePath : string ) : Promise < void > => {
46+ const oldDate = new Date ( )
47+ oldDate . setDate ( oldDate . getDate ( ) - 43 )
48+ await utimes ( filePath , oldDate , oldDate )
49+ }
50+
51+ const setupTidyClientRoot = async ( config : Interfaces . Config ) : Promise < string > => {
52+ const root = config . scopedEnvVar ( 'OCLIF_CLIENT_HOME' ) || path . join ( config . dataDir , 'client' )
53+ await mkdir ( root , { recursive : true } )
54+
55+ // Create current version directory (matches config.version = '2.0.0')
56+ const versionDir = path . join ( root , '2.0.0' )
57+ await mkdir ( path . join ( versionDir , 'bin' ) , { recursive : true } )
58+ await writeFile ( path . join ( versionDir , 'bin' , config . bin ) , 'binary' , 'utf8' )
59+
60+ // Create bin/ directory with launcher script
61+ await mkdir ( path . join ( root , 'bin' ) , { recursive : true } )
62+ await writeFile ( path . join ( root , 'bin' , config . bin ) , '../2.0.0/bin' , 'utf8' )
63+
64+ // Create current symlink
65+ if ( ! existsSync ( path . join ( root , 'current' ) ) ) {
66+ await symlink ( path . join ( root , '2.0.0' ) , path . join ( root , 'current' ) )
67+ }
68+
69+ return root
70+ }
71+
4572describe ( 'update plugin' , ( ) => {
4673 let config : Config
4774 let updater : Updater
@@ -298,4 +325,68 @@ describe('update plugin', () => {
298325 const stdout = stripAnsi ( collector . stdout . join ( ' ' ) )
299326 expect ( stdout ) . to . matches ( / U p d a t i n g t o a s p e c i f i c v e r s i o n w i l l n o t u p d a t e t h e c h a n n e l / )
300327 } )
328+
329+ describe ( 'tidy' , ( ) => {
330+ it ( 'should preserve bin, current, and active version directories during tidy' , async ( ) => {
331+ clientRoot = await setupTidyClientRoot ( config )
332+
333+ // Create old version directory that should be cleaned up
334+ const oldVersionDir = path . join ( clientRoot , '1.0.0-abc1234' )
335+ await mkdir ( path . join ( oldVersionDir , 'bin' ) , { recursive : true } )
336+ await writeFile ( path . join ( oldVersionDir , 'bin' , 'example-cli' ) , 'old version' , 'utf8' )
337+
338+ // Backdate the old version directory to be older than 42 days
339+ await setOldMtime ( oldVersionDir )
340+
341+ // Also backdate bin/ and current to verify they are preserved even when old
342+ await setOldMtime ( path . join ( clientRoot , 'bin' ) )
343+ await setOldMtime ( path . join ( clientRoot , 'current' ) )
344+
345+ // Backdate the active version directory too - it should still be preserved
346+ await setOldMtime ( path . join ( clientRoot , '2.0.0' ) )
347+
348+ // Trigger tidy via runUpdate (already on same version, but tidy still runs)
349+ const manifestRegex = new RegExp (
350+ `channels\\/stable\\/example-cli-${ config . platform } -${ config . arch } -buildmanifest` ,
351+ )
352+ nock ( / o c l i f - s t a g i n g .s 3 .a m a z o n a w s .c o m / )
353+ . get ( manifestRegex )
354+ . reply ( 200 , { version : '2.0.0' } )
355+
356+ updater = initUpdater ( config )
357+ await updater . runUpdate ( { autoUpdate : false } )
358+
359+ // Verify bin/ and current survived even though they are old
360+ expect ( existsSync ( path . join ( clientRoot , 'bin' ) ) ) . to . be . true
361+ expect ( existsSync ( path . join ( clientRoot , 'current' ) ) ) . to . be . true
362+
363+ // Verify active version directory survived even though it is old
364+ expect ( existsSync ( path . join ( clientRoot , '2.0.0' ) ) ) . to . be . true
365+
366+ // Verify old version was cleaned up
367+ expect ( existsSync ( oldVersionDir ) ) . to . be . false
368+ } )
369+
370+ it ( 'should not delete entries newer than 42 days' , async ( ) => {
371+ clientRoot = await setupTidyClientRoot ( config )
372+
373+ // Create a recent version directory (should survive)
374+ const recentVersionDir = path . join ( clientRoot , '1.9.0-def5678' )
375+ await mkdir ( path . join ( recentVersionDir , 'bin' ) , { recursive : true } )
376+ await writeFile ( path . join ( recentVersionDir , 'bin' , 'example-cli' ) , 'recent version' , 'utf8' )
377+
378+ const manifestRegex = new RegExp (
379+ `channels\\/stable\\/example-cli-${ config . platform } -${ config . arch } -buildmanifest` ,
380+ )
381+ nock ( / o c l i f - s t a g i n g .s 3 .a m a z o n a w s .c o m / )
382+ . get ( manifestRegex )
383+ . reply ( 200 , { version : '2.0.0' } )
384+
385+ updater = initUpdater ( config )
386+ await updater . runUpdate ( { autoUpdate : false } )
387+
388+ // Recent version should survive
389+ expect ( existsSync ( recentVersionDir ) ) . to . be . true
390+ } )
391+ } )
301392} )
0 commit comments