Guard cache eviction on identity and use createDeferred in test

Only delete the cache entry on error if the map still holds the same
promise, preventing a stale rejection from evicting a newer entry.
Also switch the test to createDeferred for readability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2026-05-19 15:26:58 +02:00
parent 6eacdc036e
commit 4b4a614c5c
2 changed files with 7 additions and 6 deletions
@@ -19,6 +19,7 @@ import {
UserInfoService,
} from '@backstage/backend-plugin-api';
import { mockCredentials } from '@backstage/backend-test-utils';
import { createDeferred } from '@backstage/types';
import {
CachedUserInfoService,
UserInfoCacheEntry,
@@ -129,10 +130,8 @@ describe('CachedUserInfoService', () => {
});
it('evicts eagerly so concurrent waiters see the rejection and the next call retries', async () => {
let rejectFirst: (error: Error) => void;
const firstCall = new Promise<BackstageUserInfo>((_resolve, reject) => {
rejectFirst = reject;
});
const firstCall = createDeferred<BackstageUserInfo>();
firstCall.catch(() => {});
const delegate: UserInfoService = {
getUserInfo: jest
@@ -146,7 +145,7 @@ describe('CachedUserInfoService', () => {
const p1 = service.getUserInfo(creds);
const p2 = service.getUserInfo(creds);
rejectFirst!(new Error('boom'));
firstCall.reject(new Error('boom'));
await expect(p1).rejects.toThrow('boom');
await expect(p2).rejects.toThrow('boom');
@@ -73,7 +73,9 @@ export class CachedUserInfoService implements UserInfoService {
}
const promise = this.#delegate.getUserInfo(credentials).catch(error => {
this.#entries.delete(token);
if (this.#entries.get(token)?.promise === promise) {
this.#entries.delete(token);
}
throw error;
});