From e811b23df586198dcba3d1d46a7987c7d7ed36bb Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Thu, 2 Jan 2025 17:39:43 +0100
Subject: [PATCH] fix(platform): ensure order for cached pr's on gitea and
 bitbucket (#33373)

---
 .../platform/bitbucket/pr-cache.spec.ts       |  2 +-
 lib/modules/platform/bitbucket/pr-cache.ts    | 15 +++++++++++++-
 lib/modules/platform/gitea/index.spec.ts      | 20 +++++++++----------
 lib/modules/platform/gitea/pr-cache.ts        | 15 +++++++++++++-
 4 files changed, 39 insertions(+), 13 deletions(-)

diff --git a/lib/modules/platform/bitbucket/pr-cache.spec.ts b/lib/modules/platform/bitbucket/pr-cache.spec.ts
index ac16cc38a2..5bf1d30da0 100644
--- a/lib/modules/platform/bitbucket/pr-cache.spec.ts
+++ b/lib/modules/platform/bitbucket/pr-cache.spec.ts
@@ -166,8 +166,8 @@ describe('modules/platform/bitbucket/pr-cache', () => {
     );
 
     expect(res).toMatchObject([
-      { number: 1, title: 'title' },
       { number: 2, title: 'title' },
+      { number: 1, title: 'title' },
     ]);
     expect(cache).toEqual({
       httpCache: {},
diff --git a/lib/modules/platform/bitbucket/pr-cache.ts b/lib/modules/platform/bitbucket/pr-cache.ts
index 45ac62b30f..b8e9000e3c 100644
--- a/lib/modules/platform/bitbucket/pr-cache.ts
+++ b/lib/modules/platform/bitbucket/pr-cache.ts
@@ -11,6 +11,7 @@ import type { BitbucketPrCacheData, PagedResult, PrResponse } from './types';
 import { prFieldsFilter, prInfo, prStates } from './utils';
 
 export class BitbucketPrCache {
+  private items: Pr[] = [];
   private cache: BitbucketPrCacheData;
 
   private constructor(
@@ -41,6 +42,7 @@ export class BitbucketPrCache {
     }
     repoCache.platform.bitbucket.pullRequestsCache = pullRequestCache;
     this.cache = pullRequestCache;
+    this.updateItems();
   }
 
   private static async init(
@@ -62,7 +64,7 @@ export class BitbucketPrCache {
   }
 
   private getPrs(): Pr[] {
-    return Object.values(this.cache.items);
+    return this.items;
   }
 
   static async getPrs(
@@ -77,6 +79,7 @@ export class BitbucketPrCache {
   private setPr(pr: Pr): void {
     logger.debug(`Adding PR #${pr.number} to the PR cache`);
     this.cache.items[pr.number] = pr;
+    this.updateItems();
   }
 
   static async setPr(
@@ -161,6 +164,16 @@ export class BitbucketPrCache {
       },
       `PR cache sync finished`,
     );
+
+    this.updateItems();
     return this;
   }
+
+  /**
+   * Ensure the pr cache starts with the most recent PRs.
+   * JavaScript ensures that the cache is sorted by PR number.
+   */
+  private updateItems(): void {
+    this.items = Object.values(this.cache.items).reverse();
+  }
 }
diff --git a/lib/modules/platform/gitea/index.spec.ts b/lib/modules/platform/gitea/index.spec.ts
index 198ea0962a..84031f8697 100644
--- a/lib/modules/platform/gitea/index.spec.ts
+++ b/lib/modules/platform/gitea/index.spec.ts
@@ -1166,10 +1166,10 @@ describe('modules/platform/gitea/index', () => {
 
       const res = await gitea.getPrList();
       expect(res).toMatchObject([
-        { number: 1, title: 'Some PR' },
-        { number: 2, title: 'Other PR' },
-        { number: 3, title: 'Draft PR' },
         { number: 4, title: 'Merged PR' },
+        { number: 3, title: 'Draft PR' },
+        { number: 2, title: 'Other PR' },
+        { number: 1, title: 'Some PR' },
       ]);
     });
 
@@ -1209,10 +1209,10 @@ describe('modules/platform/gitea/index', () => {
       const res = await gitea.getPrList();
 
       expect(res).toMatchObject([
-        { number: 1, title: 'Some PR' },
-        { number: 2, title: 'Other PR' },
-        { number: 3, title: 'Draft PR' },
         { number: 4, title: 'Merged PR' },
+        { number: 3, title: 'Draft PR' },
+        { number: 2, title: 'Other PR' },
+        { number: 1, title: 'Some PR' },
       ]);
     });
 
@@ -1244,16 +1244,16 @@ describe('modules/platform/gitea/index', () => {
       await initFakeRepo(scope);
 
       const res1 = await gitea.getPrList();
-      expect(res1).toMatchObject([{ number: 1 }, { number: 2 }]);
+      expect(res1).toMatchObject([{ number: 2 }, { number: 1 }]);
 
       memCache.set('gitea-pr-cache-synced', false);
 
       const res2 = await gitea.getPrList();
       expect(res2).toMatchObject([
-        { number: 1 },
-        { number: 2 },
-        { number: 3 },
         { number: 4 },
+        { number: 3 },
+        { number: 2 },
+        { number: 1 },
       ]);
     });
   });
diff --git a/lib/modules/platform/gitea/pr-cache.ts b/lib/modules/platform/gitea/pr-cache.ts
index b26085d97a..e6f363500d 100644
--- a/lib/modules/platform/gitea/pr-cache.ts
+++ b/lib/modules/platform/gitea/pr-cache.ts
@@ -11,6 +11,7 @@ import { API_PATH, toRenovatePR } from './utils';
 
 export class GiteaPrCache {
   private cache: GiteaPrCacheData;
+  private items: Pr[] = [];
 
   private constructor(
     private repo: string,
@@ -31,6 +32,7 @@ export class GiteaPrCache {
     }
     repoCache.platform.gitea.pullRequestsCache = pullRequestCache;
     this.cache = pullRequestCache;
+    this.updateItems();
   }
 
   static forceSync(): void {
@@ -54,7 +56,7 @@ export class GiteaPrCache {
   }
 
   private getPrs(): Pr[] {
-    return Object.values(this.cache.items);
+    return this.items;
   }
 
   static async getPrs(
@@ -68,6 +70,7 @@ export class GiteaPrCache {
 
   private setPr(item: Pr): void {
     this.cache.items[item.number] = item;
+    this.updateItems();
   }
 
   static async setPr(
@@ -137,6 +140,16 @@ export class GiteaPrCache {
       url = parseLinkHeader(res.headers.link)?.next?.url;
     }
 
+    this.updateItems();
+
     return this;
   }
+
+  /**
+   * Ensure the pr cache starts with the most recent PRs.
+   * JavaScript ensures that the cache is sorted by PR number.
+   */
+  private updateItems(): void {
+    this.items = Object.values(this.cache.items).reverse();
+  }
 }
-- 
GitLab