@@ -1038,6 +1038,72 @@ describe("RetrieveLatestApp handler", () => {
10381038 await expect ( RetrieveLatestApp ( req , res ) ) . rejects . toThrow ( "is not available for version" ) ;
10391039 } ) ;
10401040 } ) ;
1041+
1042+ describe ( "cache behavior" , ( ) => {
1043+ it ( "should return cached redirect on second call with same parameters" , async ( ) => {
1044+ const req1 = createMockRequest ( { } ) ;
1045+ const res1 = createMockResponse ( ) ;
1046+
1047+ s3Mock . on ( ListObjectsV2Command , { Prefix : "app/" } ) . resolves ( {
1048+ CommonPrefixes : [ { Prefix : "app/1.0.0/" } ] ,
1049+ } ) ;
1050+
1051+ const content = "cached-app-content" ;
1052+ const crypto = await import ( "crypto" ) ;
1053+ const hash = crypto . createHash ( "sha256" ) . update ( content ) . digest ( "hex" ) ;
1054+
1055+ mockS3LegacyVersionWithContent ( "app" , "1.0.0" , "jetkvm_app" , content , hash ) ;
1056+
1057+ await RetrieveLatestApp ( req1 , res1 ) ;
1058+ expect ( res1 . _redirectUrl ) . toBe ( "https://cdn.test.com/app/1.0.0/jetkvm_app" ) ;
1059+
1060+ // Reset S3 mock to return different data
1061+ s3Mock . reset ( ) ;
1062+ s3Mock . on ( ListObjectsV2Command , { Prefix : "app/" } ) . resolves ( {
1063+ CommonPrefixes : [ { Prefix : "app/2.0.0/" } ] ,
1064+ } ) ;
1065+ mockS3LegacyVersionWithContent ( "app" , "2.0.0" , "jetkvm_app" , "new-content" , "new-hash" ) ;
1066+
1067+ // Second call should return cached result (1.0.0), not new S3 data (2.0.0)
1068+ const req2 = createMockRequest ( { } ) ;
1069+ const res2 = createMockResponse ( ) ;
1070+
1071+ await RetrieveLatestApp ( req2 , res2 ) ;
1072+ expect ( res2 . _redirectUrl ) . toBe ( "https://cdn.test.com/app/1.0.0/jetkvm_app" ) ;
1073+ } ) ;
1074+
1075+ it ( "should use different cache keys for different SKUs" , async ( ) => {
1076+ // First call with default SKU
1077+ const req1 = createMockRequest ( { } ) ;
1078+ const res1 = createMockResponse ( ) ;
1079+
1080+ s3Mock . on ( ListObjectsV2Command , { Prefix : "app/" } ) . resolves ( {
1081+ CommonPrefixes : [ { Prefix : "app/1.0.0/" } ] ,
1082+ } ) ;
1083+
1084+ const content = "sku-cache-test" ;
1085+ const crypto = await import ( "crypto" ) ;
1086+ const hash = crypto . createHash ( "sha256" ) . update ( content ) . digest ( "hex" ) ;
1087+
1088+ mockS3LegacyVersionWithContent ( "app" , "1.0.0" , "jetkvm_app" , content , hash ) ;
1089+
1090+ await RetrieveLatestApp ( req1 , res1 ) ;
1091+ expect ( res1 . _redirectUrl ) . toBe ( "https://cdn.test.com/app/1.0.0/jetkvm_app" ) ;
1092+
1093+ // Second call with different SKU should NOT use cached result
1094+ s3Mock . reset ( ) ;
1095+ s3Mock . on ( ListObjectsV2Command , { Prefix : "app/" } ) . resolves ( {
1096+ CommonPrefixes : [ { Prefix : "app/2.0.0/" } ] ,
1097+ } ) ;
1098+ mockS3SkuVersionWithContent ( "app" , "2.0.0" , "jetkvm-2" , "jetkvm_app" , content , hash ) ;
1099+
1100+ const req2 = createMockRequest ( { sku : "jetkvm-2" } ) ;
1101+ const res2 = createMockResponse ( ) ;
1102+
1103+ await RetrieveLatestApp ( req2 , res2 ) ;
1104+ expect ( res2 . _redirectUrl ) . toBe ( "https://cdn.test.com/app/2.0.0/skus/jetkvm-2/jetkvm_app" ) ;
1105+ } ) ;
1106+ } ) ;
10411107} ) ;
10421108
10431109describe ( "RetrieveLatestSystemRecovery handler" , ( ) => {
@@ -1276,4 +1342,70 @@ describe("RetrieveLatestSystemRecovery handler", () => {
12761342 await expect ( RetrieveLatestSystemRecovery ( req , res ) ) . rejects . toThrow ( "is not available for version" ) ;
12771343 } ) ;
12781344 } ) ;
1345+
1346+ describe ( "cache behavior" , ( ) => {
1347+ it ( "should return cached redirect on second call with same parameters" , async ( ) => {
1348+ const req1 = createMockRequest ( { } ) ;
1349+ const res1 = createMockResponse ( ) ;
1350+
1351+ s3Mock . on ( ListObjectsV2Command , { Prefix : "system/" } ) . resolves ( {
1352+ CommonPrefixes : [ { Prefix : "system/1.0.0/" } ] ,
1353+ } ) ;
1354+
1355+ const content = "cached-system-recovery-content" ;
1356+ const crypto = await import ( "crypto" ) ;
1357+ const hash = crypto . createHash ( "sha256" ) . update ( content ) . digest ( "hex" ) ;
1358+
1359+ mockS3LegacyVersionWithContent ( "system" , "1.0.0" , "update.img" , content , hash ) ;
1360+
1361+ await RetrieveLatestSystemRecovery ( req1 , res1 ) ;
1362+ expect ( res1 . _redirectUrl ) . toBe ( "https://cdn.test.com/system/1.0.0/update.img" ) ;
1363+
1364+ // Reset S3 mock to return different data
1365+ s3Mock . reset ( ) ;
1366+ s3Mock . on ( ListObjectsV2Command , { Prefix : "system/" } ) . resolves ( {
1367+ CommonPrefixes : [ { Prefix : "system/2.0.0/" } ] ,
1368+ } ) ;
1369+ mockS3LegacyVersionWithContent ( "system" , "2.0.0" , "update.img" , "new-content" , "new-hash" ) ;
1370+
1371+ // Second call should return cached result (1.0.0), not new S3 data (2.0.0)
1372+ const req2 = createMockRequest ( { } ) ;
1373+ const res2 = createMockResponse ( ) ;
1374+
1375+ await RetrieveLatestSystemRecovery ( req2 , res2 ) ;
1376+ expect ( res2 . _redirectUrl ) . toBe ( "https://cdn.test.com/system/1.0.0/update.img" ) ;
1377+ } ) ;
1378+
1379+ it ( "should use different cache keys for different SKUs" , async ( ) => {
1380+ // First call with default SKU
1381+ const req1 = createMockRequest ( { } ) ;
1382+ const res1 = createMockResponse ( ) ;
1383+
1384+ s3Mock . on ( ListObjectsV2Command , { Prefix : "system/" } ) . resolves ( {
1385+ CommonPrefixes : [ { Prefix : "system/1.0.0/" } ] ,
1386+ } ) ;
1387+
1388+ const content = "sku-cache-test-recovery" ;
1389+ const crypto = await import ( "crypto" ) ;
1390+ const hash = crypto . createHash ( "sha256" ) . update ( content ) . digest ( "hex" ) ;
1391+
1392+ mockS3LegacyVersionWithContent ( "system" , "1.0.0" , "update.img" , content , hash ) ;
1393+
1394+ await RetrieveLatestSystemRecovery ( req1 , res1 ) ;
1395+ expect ( res1 . _redirectUrl ) . toBe ( "https://cdn.test.com/system/1.0.0/update.img" ) ;
1396+
1397+ // Second call with different SKU should NOT use cached result
1398+ s3Mock . reset ( ) ;
1399+ s3Mock . on ( ListObjectsV2Command , { Prefix : "system/" } ) . resolves ( {
1400+ CommonPrefixes : [ { Prefix : "system/2.0.0/" } ] ,
1401+ } ) ;
1402+ mockS3SkuVersionWithContent ( "system" , "2.0.0" , "jetkvm-2" , "update.img" , content , hash ) ;
1403+
1404+ const req2 = createMockRequest ( { sku : "jetkvm-2" } ) ;
1405+ const res2 = createMockResponse ( ) ;
1406+
1407+ await RetrieveLatestSystemRecovery ( req2 , res2 ) ;
1408+ expect ( res2 . _redirectUrl ) . toBe ( "https://cdn.test.com/system/2.0.0/skus/jetkvm-2/update.img" ) ;
1409+ } ) ;
1410+ } ) ;
12791411} ) ;
0 commit comments