@@ -1189,6 +1189,246 @@ func Test_determineUpgradeVersionDirectories_Downgrade_PathContainsFromVersion(t
11891189 require .Equal (t , expectedToDir , toDir )
11901190}
11911191
1192+ func Test_rehydrateMrSeqFiles_OpenStatusDirFails (t * testing.T ) {
1193+ tempDir , _ := os .MkdirTemp ("" , "OpenStatusDirFails" )
1194+ defer os .RemoveAll (tempDir )
1195+ handlerEnvironment := handlerenv.HandlerEnvironment {
1196+ EventsFolder : tempDir ,
1197+ }
1198+
1199+ ctx := log .NewContext (log .NewNopLogger ())
1200+ extensionLogger := logging .New (nil )
1201+ events := extensionevents .New (extensionLogger , & handlerEnvironment )
1202+
1203+ from := t .TempDir () // does not contain the status subdir
1204+ to := t .TempDir ()
1205+
1206+ err := rehydrateMrSeqFilesForProblematicUpgrades (ctx , from , to , events )
1207+ require .NotNil (t , err , "expected error when opening missing status dir" )
1208+ require .True (t , strings .Contains (err .Error (), "Failed to open status directory" ), "unexpected error message: %s" , err .Error ())
1209+ }
1210+
1211+ func Test_rehydrateMrSeqFiles_ReadDirFails (t * testing.T ) {
1212+ tempDir , _ := os .MkdirTemp ("" , "ReadDirFails" )
1213+ defer os .RemoveAll (tempDir )
1214+ handlerEnvironment := handlerenv.HandlerEnvironment {
1215+ EventsFolder : tempDir ,
1216+ }
1217+
1218+ ctx := log .NewContext (log .NewNopLogger ())
1219+ extensionLogger := logging .New (nil )
1220+ events := extensionevents .New (extensionLogger , & handlerEnvironment )
1221+
1222+ from := filepath .Join (tempDir , "from" )
1223+ require .NoError (t , os .Mkdir (from , 0o755 ))
1224+ to := filepath .Join (tempDir , "to" )
1225+ require .NoError (t , os .Mkdir (to , 0o755 ))
1226+
1227+ // Create a *file* at "<from>/status" so os.Open succeeds but ReadDir fails.
1228+ statusPath := filepath .Join (from , constants .StatusFileDirectory )
1229+ require .NoError (t , os .WriteFile (statusPath , []byte ("not a directory" ), 0o600 ))
1230+
1231+ err := rehydrateMrSeqFilesForProblematicUpgrades (ctx , from , to , events )
1232+ require .NotNil (t , err , "expected error when ReadDir fails" )
1233+ require .True (t , strings .Contains (err .Error (), "could not read directory entries" ), "unexpected error message: %s" , err .Error ())
1234+ }
1235+
1236+ func Test_rehydrateMrSeqFiles_IgnoresInvalidStatusFilename (t * testing.T ) {
1237+ tempDir , _ := os .MkdirTemp ("" , "IgnoresInvalidStatusFilename" )
1238+ defer os .RemoveAll (tempDir )
1239+ handlerEnvironment := handlerenv.HandlerEnvironment {
1240+ EventsFolder : tempDir ,
1241+ }
1242+
1243+ ctx := log .NewContext (log .NewNopLogger ())
1244+ extensionLogger := logging .New (nil )
1245+ events := extensionevents .New (extensionLogger , & handlerEnvironment )
1246+
1247+ from := filepath .Join (tempDir , "from" )
1248+ require .NoError (t , os .Mkdir (from , 0o755 ))
1249+ to := filepath .Join (tempDir , "to" )
1250+ require .NoError (t , os .Mkdir (to , 0o755 ))
1251+
1252+ // Proper status directory
1253+ require .NoError (t , os .MkdirAll (filepath .Join (from , constants .StatusFileDirectory ), 0o755 ))
1254+
1255+ // Invalid filename (only two parts, missing seqNo). Should be ignored.
1256+ invalid := filepath .Join (from , constants .StatusFileDirectory , "alpha" + constants .StatusFileExtension )
1257+ require .NoError (t , os .WriteFile (invalid , []byte ("" ), 0o600 ))
1258+
1259+ err := rehydrateMrSeqFilesForProblematicUpgrades (ctx , from , to , events )
1260+ require .Nil (t , err , "unexpected error: %v" , err )
1261+
1262+ // No mrseq should be created for invalid filenames.
1263+ _ , err = os .Stat (filepath .Join (to , "alpha" + constants .MrSeqFileExtension ))
1264+ require .True (t , os .IsNotExist (err ), "alpha%s should not have been created" , constants .MrSeqFileExtension )
1265+ }
1266+
1267+ func Test_rehydrateMrSeqFiles_RehydrateMissingMrseq (t * testing.T ) {
1268+ tempDir , _ := os .MkdirTemp ("" , "RehydrateMissingMrseq" )
1269+ defer os .RemoveAll (tempDir )
1270+ handlerEnvironment := handlerenv.HandlerEnvironment {
1271+ EventsFolder : tempDir ,
1272+ }
1273+
1274+ ctx := log .NewContext (log .NewNopLogger ())
1275+ extensionLogger := logging .New (nil )
1276+ events := extensionevents .New (extensionLogger , & handlerEnvironment )
1277+
1278+ from := filepath .Join (tempDir , "from" )
1279+ require .NoError (t , os .Mkdir (from , 0o755 ))
1280+ to := filepath .Join (tempDir , "to" )
1281+ require .NoError (t , os .Mkdir (to , 0o755 ))
1282+
1283+ statusDir := filepath .Join (from , constants .StatusFileDirectory )
1284+ if err := os .MkdirAll (statusDir , 0o755 ); err != nil {
1285+ t .Fatalf ("mkdir status: %v" , err )
1286+ }
1287+
1288+ // alpha.5.status → should create to/alpha.mrseq with "5"
1289+ alphaStatus := filepath .Join (statusDir , "alpha.5" + constants .StatusFileExtension )
1290+ require .NoError (t , os .WriteFile (alphaStatus , []byte ("" ), 0o600 ))
1291+
1292+ err := rehydrateMrSeqFilesForProblematicUpgrades (ctx , from , to , events )
1293+ require .Nil (t , err , "unexpected error: %v" , err )
1294+
1295+ mrseqPath := filepath .Join (to , "alpha" + constants .MrSeqFileExtension )
1296+ got := mustReadFile (t , mrseqPath )
1297+ require .Equal (t , "5" , got , "mrseq content = %q, want %q" , got , "5" )
1298+ }
1299+
1300+ func Test_rehydrateMrSeqFiles_UpdateExistingMrseqWhenHigherSeqFound (t * testing.T ) {
1301+ tempDir , _ := os .MkdirTemp ("" , "UpdateExistingMrseqWhenHigherSeqFound" )
1302+ defer os .RemoveAll (tempDir )
1303+ handlerEnvironment := handlerenv.HandlerEnvironment {
1304+ EventsFolder : tempDir ,
1305+ }
1306+
1307+ ctx := log .NewContext (log .NewNopLogger ())
1308+ extensionLogger := logging .New (nil )
1309+ events := extensionevents .New (extensionLogger , & handlerEnvironment )
1310+
1311+ from := filepath .Join (tempDir , "from" )
1312+ require .NoError (t , os .Mkdir (from , 0o755 ))
1313+ to := filepath .Join (tempDir , "to" )
1314+ require .NoError (t , os .Mkdir (to , 0o755 ))
1315+
1316+ statusDir := filepath .Join (from , constants .StatusFileDirectory )
1317+ require .NoError (t , os .MkdirAll (statusDir , 0o755 ))
1318+
1319+ // Existing mrseq=3
1320+ mrseqPath := filepath .Join (to , "alpha" + constants .MrSeqFileExtension )
1321+ require .NoError (t , os .WriteFile (mrseqPath , []byte ("3" ), 0o600 ))
1322+
1323+ // Status reports seq=5 → should overwrite to "5"
1324+ alphaStatus := filepath .Join (statusDir , "alpha.5" + constants .StatusFileExtension )
1325+ require .NoError (t , os .WriteFile (alphaStatus , []byte ("" ), 0o600 ))
1326+
1327+ err := rehydrateMrSeqFilesForProblematicUpgrades (ctx , from , to , events )
1328+ require .Nil (t , err , "unexpected error: %v" , err )
1329+
1330+ got := mustReadFile (t , mrseqPath )
1331+ require .Equal (t , "5" , got , "mrseq content = %q, want %q" , got , "5" )
1332+ }
1333+
1334+ func Test_rehydrateMrSeqFiles_NoUpdateWhenExistingIsHigher (t * testing.T ) {
1335+ tempDir , _ := os .MkdirTemp ("" , "NoUpdateWhenExistingIsHigher" )
1336+ defer os .RemoveAll (tempDir )
1337+ handlerEnvironment := handlerenv.HandlerEnvironment {
1338+ EventsFolder : tempDir ,
1339+ }
1340+
1341+ ctx := log .NewContext (log .NewNopLogger ())
1342+ extensionLogger := logging .New (nil )
1343+ events := extensionevents .New (extensionLogger , & handlerEnvironment )
1344+
1345+ from := filepath .Join (tempDir , "from" )
1346+ require .NoError (t , os .Mkdir (from , 0o755 ))
1347+ to := filepath .Join (tempDir , "to" )
1348+ require .NoError (t , os .Mkdir (to , 0o755 ))
1349+
1350+ statusDir := filepath .Join (from , constants .StatusFileDirectory )
1351+ require .NoError (t , os .MkdirAll (statusDir , 0o755 ))
1352+
1353+ // Existing mrseq=7
1354+ mrseqPath := filepath .Join (to , "alpha " + constants .MrSeqFileExtension )
1355+ require .NoError (t , os .WriteFile (mrseqPath , []byte ("7" ), 0o600 ))
1356+
1357+ // Status reports seq=5 → should NOT overwrite
1358+ alphaStatus := filepath .Join (statusDir , "alpha.5" + constants .StatusFileExtension )
1359+ require .NoError (t , os .WriteFile (alphaStatus , []byte ("" ), 0o600 ))
1360+
1361+ err := rehydrateMrSeqFilesForProblematicUpgrades (ctx , from , to , events )
1362+ require .Nil (t , err , "unexpected error: %v" , err )
1363+
1364+ got := mustReadFile (t , mrseqPath )
1365+ require .Equal (t , "7" , got , "mrseq content = %q, want %q" , got , "7" )
1366+ }
1367+
1368+ func Test_rehydrateMrSeqFiles_MultipleStatusFiles_TakesMax (t * testing.T ) {
1369+ tempDir , _ := os .MkdirTemp ("" , "TakesMax" )
1370+ defer os .RemoveAll (tempDir )
1371+ handlerEnvironment := handlerenv.HandlerEnvironment {
1372+ EventsFolder : tempDir ,
1373+ }
1374+
1375+ ctx := log .NewContext (log .NewNopLogger ())
1376+ extensionLogger := logging .New (nil )
1377+ events := extensionevents .New (extensionLogger , & handlerEnvironment )
1378+
1379+ from := filepath .Join (tempDir , "from" )
1380+ require .NoError (t , os .Mkdir (from , 0o755 ))
1381+ to := filepath .Join (tempDir , "to" )
1382+ require .NoError (t , os .Mkdir (to , 0o755 ))
1383+
1384+ statusDir := filepath .Join (from , constants .StatusFileDirectory )
1385+ require .NoError (t , os .MkdirAll (statusDir , 0o755 ))
1386+
1387+ // Both alpha.3.status and alpha.7.status — final mrseq should be "7"
1388+ for _ , seq := range []int {3 , 7 } {
1389+ p := filepath .Join (statusDir , "alpha." + strconv .Itoa (seq )+ constants .StatusFileExtension )
1390+ require .NoError (t , os .WriteFile (p , []byte ("" ), 0o600 ))
1391+ }
1392+
1393+ err := rehydrateMrSeqFilesForProblematicUpgrades (ctx , from , to , events )
1394+ require .Nil (t , err , "unexpected error: %v" , err )
1395+
1396+ got := mustReadFile (t , filepath .Join (to , "alpha" + constants .MrSeqFileExtension ))
1397+ require .Equal (t , "7" , got , "mrseq content = %q, want %q" , got , "7" )
1398+ }
1399+
1400+ func Test_rehydrateMrSeqFiles_ReadExistingMrseqFails (t * testing.T ) {
1401+ tempDir , _ := os .MkdirTemp ("" , "ReadExistingMrseqFails" )
1402+ defer os .RemoveAll (tempDir )
1403+ handlerEnvironment := handlerenv.HandlerEnvironment {
1404+ EventsFolder : tempDir ,
1405+ }
1406+
1407+ ctx := log .NewContext (log .NewNopLogger ())
1408+ extensionLogger := logging .New (nil )
1409+ events := extensionevents .New (extensionLogger , & handlerEnvironment )
1410+
1411+ from := filepath .Join (tempDir , "from" )
1412+ require .NoError (t , os .Mkdir (from , 0o755 ))
1413+ to := filepath .Join (tempDir , "to" )
1414+ require .NoError (t , os .Mkdir (to , 0o755 ))
1415+
1416+ statusDir := filepath .Join (from , constants .StatusFileDirectory )
1417+ require .NoError (t , os .MkdirAll (statusDir , 0o755 ))
1418+
1419+ // Make a directory at the mrseq path so ReadFile fails
1420+ mrseqPath := filepath .Join (to , "alpha" + constants .MrSeqFileExtension )
1421+ require .NoError (t , os .MkdirAll (mrseqPath , 0o755 ))
1422+
1423+ // Now create a status that would try to read/compare
1424+ alphaStatus := filepath .Join (statusDir , "alpha.5" + constants .StatusFileExtension )
1425+ require .NoError (t , os .WriteFile (alphaStatus , []byte ("" ), 0o600 ))
1426+
1427+ err := rehydrateMrSeqFilesForProblematicUpgrades (ctx , from , to , events )
1428+ require .NotNil (t , err , "expected error due to unreadable mrseq" )
1429+ require .True (t , strings .Contains (err .Error (), "Could not read file" ), "Unexpected error: %v" , err .Error ())
1430+ }
1431+
11921432// setEnvs is a helper to seed the env for each scenario.
11931433func setEnvs (t * testing.T , to , curr , from , dir string ) {
11941434 t .Helper ()
@@ -1197,3 +1437,11 @@ func setEnvs(t *testing.T, to, curr, from, dir string) {
11971437 t .Setenv (constants .ExtensionVersionUpdatingFromEnvName , from )
11981438 t .Setenv (constants .ExtensionPathEnvName , dir )
11991439}
1440+
1441+ func mustReadFile (t * testing.T , p string ) string {
1442+ b , err := os .ReadFile (p )
1443+ if err != nil {
1444+ t .Fatalf ("read %s: %v" , p , err )
1445+ }
1446+ return string (b )
1447+ }
0 commit comments