From ada7df792996a76392ee56e3f61ca396c21803ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 14 May 2026 17:30:30 +0200 Subject: [PATCH] backend-test-utils: add version field to mock credentials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mock credentials created by mockCredentials.none(), .user(), and .service() were missing the internal version: 'v1' field. This caused toInternalBackstageCredentials() to throw when used with mock credentials in tests. Signed-off-by: Fredrik Adelöw Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Fredrik Adelöw --- .changeset/mock-credentials-version.md | 5 ++ .../actionsRegistryServiceFactory.test.ts | 5 +- .../src/entrypoints/auth/helpers.test.ts | 47 +++++++++++++++++++ .../src/services/mockCredentials.test.ts | 8 ++++ .../src/services/mockCredentials.ts | 8 +++- 5 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 .changeset/mock-credentials-version.md diff --git a/.changeset/mock-credentials-version.md b/.changeset/mock-credentials-version.md new file mode 100644 index 0000000000..89cca1d4a6 --- /dev/null +++ b/.changeset/mock-credentials-version.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +Fixed `mockCredentials` to include the internal `version: 'v1'` field on all credential objects (`none()`, `user()`, `limitedUser()`, `service()`), and fixed `user()` to encode the user entity ref into the token (matching `user.token(ref)` behavior). This makes mock credentials compatible with `toInternalBackstageCredentials()`, which validates the version field, and ensures that credentials for different users produce different tokens. diff --git a/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.test.ts b/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.test.ts index 39392c101c..dd62a8d339 100644 --- a/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.test.ts +++ b/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.test.ts @@ -556,13 +556,14 @@ describe('actionsRegistryServiceFactory', () => { input: { name: 'test', }, - credentials: { + credentials: expect.objectContaining({ $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'service', subject: 'user:default/mock', }, - }, + }), logger: expect.anything(), }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/helpers.test.ts b/packages/backend-defaults/src/entrypoints/auth/helpers.test.ts index fdc6e26c3a..1b3a3a9687 100644 --- a/packages/backend-defaults/src/entrypoints/auth/helpers.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/helpers.test.ts @@ -14,10 +14,12 @@ * limitations under the License. */ +import { mockCredentials } from '@backstage/backend-test-utils'; import { createCredentialsWithNonePrincipal, createCredentialsWithServicePrincipal, createCredentialsWithUserPrincipal, + toInternalBackstageCredentials, } from './helpers'; describe('credentials', () => { @@ -82,6 +84,51 @@ describe('credentials', () => { ).not.toMatch(/my-token/); }); + it('should convert mock credentials to internal form', () => { + expect( + toInternalBackstageCredentials(mockCredentials.none()), + ).toMatchObject({ version: 'v1', principal: { type: 'none' } }); + + expect( + toInternalBackstageCredentials(mockCredentials.user()), + ).toMatchObject({ + version: 'v1', + token: mockCredentials.user.token(), + principal: { type: 'user', userEntityRef: 'user:default/mock' }, + }); + + expect( + toInternalBackstageCredentials( + mockCredentials.user('user:default/other'), + ), + ).toMatchObject({ + version: 'v1', + token: mockCredentials.user.token('user:default/other'), + principal: { type: 'user', userEntityRef: 'user:default/other' }, + }); + + expect( + toInternalBackstageCredentials(mockCredentials.limitedUser()), + ).toMatchObject({ + version: 'v1', + principal: { type: 'user', userEntityRef: 'user:default/mock' }, + }); + + expect( + toInternalBackstageCredentials(mockCredentials.service()), + ).toMatchObject({ + version: 'v1', + principal: { type: 'service', subject: 'external:test-service' }, + }); + + expect( + toInternalBackstageCredentials(mockCredentials.service('plugin:other')), + ).toMatchObject({ + version: 'v1', + principal: { type: 'service', subject: 'plugin:other' }, + }); + }); + it('should have a serializable form both as strings and as JSON', () => { const simpleService = createCredentialsWithServicePrincipal('my-service'); expect(String(simpleService)).toMatchInlineSnapshot( diff --git a/packages/backend-test-utils/src/services/mockCredentials.test.ts b/packages/backend-test-utils/src/services/mockCredentials.test.ts index 9248e3bde0..4620e4879d 100644 --- a/packages/backend-test-utils/src/services/mockCredentials.test.ts +++ b/packages/backend-test-utils/src/services/mockCredentials.test.ts @@ -20,6 +20,7 @@ describe('mockCredentials', () => { it('creates a mocked credentials object for a none principal', () => { expect(mockCredentials.none()).toEqual({ $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'none' }, }); }); @@ -27,11 +28,13 @@ describe('mockCredentials', () => { it('creates a mocked credentials object for a user principal', () => { expect(mockCredentials.user()).toEqual({ $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'user', userEntityRef: 'user:default/mock' }, }); expect(mockCredentials.user('user:default/other')).toEqual({ $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'user', userEntityRef: 'user:default/other' }, }); }); @@ -39,11 +42,13 @@ describe('mockCredentials', () => { it('creates a mocked credentials object for a limited user principal', () => { expect(mockCredentials.limitedUser()).toEqual({ $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'user', userEntityRef: 'user:default/mock' }, }); expect(mockCredentials.limitedUser('user:default/other')).toEqual({ $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'user', userEntityRef: 'user:default/other' }, }); }); @@ -51,11 +56,13 @@ describe('mockCredentials', () => { it('creates a mocked credentials object for a service principal', () => { expect(mockCredentials.service()).toEqual({ $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'service', subject: 'external:test-service' }, }); expect(mockCredentials.service('plugin:other')).toEqual({ $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'service', subject: 'plugin:other' }, }); }); @@ -138,6 +145,7 @@ describe('mockCredentials', () => { mockCredentials.service('test', { permissionNames: ['do.it'] }), ).toEqual({ $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'service', subject: 'test', diff --git a/packages/backend-test-utils/src/services/mockCredentials.ts b/packages/backend-test-utils/src/services/mockCredentials.ts index d749f7edb1..bbb1590748 100644 --- a/packages/backend-test-utils/src/services/mockCredentials.ts +++ b/packages/backend-test-utils/src/services/mockCredentials.ts @@ -78,6 +78,7 @@ export namespace mockCredentials { export function none(): BackstageCredentials { const result = { $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'none' }, } as const; Object.defineProperties(result, { @@ -122,6 +123,7 @@ export namespace mockCredentials { validateUserEntityRef(userEntityRef); const result = { $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'user', userEntityRef, @@ -142,7 +144,10 @@ export namespace mockCredentials { token: { enumerable: false, configurable: true, - value: user.token(), + value: + userEntityRef !== DEFAULT_MOCK_USER_ENTITY_REF || options?.actor + ? user.token(userEntityRef, options) + : user.token(), }, }); return result; @@ -250,6 +255,7 @@ export namespace mockCredentials { ): BackstageCredentials { const result = { $$type: '@backstage/BackstageCredentials', + version: 'v1', principal: { type: 'service', subject,