From c8de7a831dacebda05346468529f0c78fe783a8e Mon Sep 17 00:00:00 2001 From: joshuakrueger-dfx Date: Fri, 7 Aug 2026 14:08:09 +0200 Subject: [PATCH 1/2] feat(core): add the non-custodial wallet partner user role The API grants `NonCustodialWalletPartner` to employees of a wallet partner (DFXswiss/api#4587, `src/shared/auth/user-role.enum.ts`). Without the member, every consumer compares the role as a string literal, and a one-character drift silently hides a gated screen from everyone entitled to it. Value is character-identical to the API enum. --- packages/core/src/definitions/jwt.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/definitions/jwt.ts b/packages/core/src/definitions/jwt.ts index 0c071f14..40c39073 100644 --- a/packages/core/src/definitions/jwt.ts +++ b/packages/core/src/definitions/jwt.ts @@ -13,6 +13,7 @@ export enum UserRole { REALUNIT = 'RealUnit', MARKETING = 'Marketing', MONITORING = 'Monitoring', + NON_CUSTODIAL_WALLET_PARTNER = 'NonCustodialWalletPartner', } export interface Jwt { From 7ef2f7c1c272eadc935bd748166798a153c25aeb Mon Sep 17 00:00:00 2001 From: joshuakrueger-dfx Date: Fri, 7 Aug 2026 15:08:59 +0200 Subject: [PATCH 2/2] test(core): pin the UserRole contract strings The enum is a contract against the API: every value is a string the backend compares literally. A duplicate makes two roles indistinguishable, a stray space or hyphen breaks the comparison, and neither is caught by the compiler. Three invariants over all members rather than an assertion on any single one - no duplicates, anchored PascalCase, and a fixed member count so the other two cannot pass over an empty set. --- packages/core/src/__tests__/user-role.test.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 packages/core/src/__tests__/user-role.test.ts diff --git a/packages/core/src/__tests__/user-role.test.ts b/packages/core/src/__tests__/user-role.test.ts new file mode 100644 index 00000000..cd3d379d --- /dev/null +++ b/packages/core/src/__tests__/user-role.test.ts @@ -0,0 +1,29 @@ +import { UserRole } from '../definitions/jwt'; + +describe('UserRole', () => { + const values = Object.values(UserRole); + + it('has no duplicate values', () => { + const seen = new Set(); + const duplicates: string[] = []; + for (const value of values) { + if (seen.has(value)) { + duplicates.push(value); + } + seen.add(value); + } + expect(duplicates).toEqual([]); + }); + + it('uses contiguous PascalCase contract strings', () => { + // Anchored: rejects trailing/embedded junk (spaces, hyphens, underscores, digits, …). + // Allows VIP and KycClientCompany (single PascalCase / all-caps word). + const pattern = /^[A-Z][A-Za-z]*$/; + const invalid = values.filter((value) => !pattern.test(value)); + expect(invalid).toEqual([]); + }); + + it('has exactly 13 members', () => { + expect(values).toHaveLength(13); + }); +});