diff --git a/lib/permissions/minimally-encoded-thread-permissions.test.js b/lib/permissions/minimally-encoded-thread-permissions.test.js --- a/lib/permissions/minimally-encoded-thread-permissions.test.js +++ b/lib/permissions/minimally-encoded-thread-permissions.test.js @@ -23,8 +23,10 @@ import { minimallyEncodeRawThreadInfo, decodeMinimallyEncodedRawThreadInfo, + minimallyEncodeThreadCurrentUserInfo, } from '../types/minimally-encoded-thread-permissions-types.js'; import type { ThreadRolePermissionsBlob } from '../types/thread-permission-types.js'; +import type { ThreadCurrentUserInfo } from '../types/thread-types.js'; const permissions = { know_of: { value: true, source: '1' }, @@ -480,3 +482,127 @@ ).toStrictEqual(expectedDecodedExampleRawThreadInfoA); }); }); + +const threadCurrentUserInfo: ThreadCurrentUserInfo = { + role: '256|83795', + permissions: { + know_of: { + value: true, + source: '256|1', + }, + visible: { + value: true, + source: '256|1', + }, + voiced: { + value: false, + source: null, + }, + edit_entries: { + value: false, + source: null, + }, + edit_thread: { + value: false, + source: null, + }, + edit_thread_description: { + value: false, + source: null, + }, + edit_thread_color: { + value: false, + source: null, + }, + delete_thread: { + value: false, + source: null, + }, + create_subthreads: { + value: false, + source: null, + }, + create_sidebars: { + value: false, + source: null, + }, + join_thread: { + value: false, + source: null, + }, + edit_permissions: { + value: false, + source: null, + }, + add_members: { + value: false, + source: null, + }, + remove_members: { + value: false, + source: null, + }, + change_role: { + value: false, + source: null, + }, + leave_thread: { + value: false, + source: null, + }, + react_to_message: { + value: false, + source: null, + }, + edit_message: { + value: false, + source: null, + }, + edit_thread_avatar: { + value: false, + source: null, + }, + manage_pins: { + value: false, + source: null, + }, + manage_invite_links: { + value: false, + source: null, + }, + voiced_in_announcement_channels: { + value: false, + source: null, + }, + }, + subscription: { + home: true, + pushNotifs: true, + }, + unread: true, +}; +describe('minimallyEncodeThreadCurrentUserInfo', () => { + it('should correctly encode threadCurrentUserInfo ONCE', () => { + const minimallyEncoded = minimallyEncodeThreadCurrentUserInfo( + threadCurrentUserInfo, + ); + expect(minimallyEncoded.permissions).toBe('3'); + }); + + it('should throw when attempting to minimally encode threadCurrentUserInfo twice', () => { + const minimallyEncoded = minimallyEncodeThreadCurrentUserInfo( + threadCurrentUserInfo, + ); + expect(minimallyEncoded.permissions).toBe('3'); + expect(() => + // `MinimallyEncodedThreadCurrentUser` should never be passed + // to `minimallyEncodeThreadCurrentUserInfo`. We're intentionally + // bypassing Flow to simulate a scenario where malformed input is + // passed to minimallyEncodeThreadCurrentUserInfo to ensure that the + // `invariant` throws the expected error. + + // $FlowExpectedError + minimallyEncodeThreadCurrentUserInfo(minimallyEncoded), + ).toThrow('threadCurrentUserInfo is already minimally encoded.'); + }); +}); diff --git a/lib/types/minimally-encoded-thread-permissions-types.js b/lib/types/minimally-encoded-thread-permissions-types.js --- a/lib/types/minimally-encoded-thread-permissions-types.js +++ b/lib/types/minimally-encoded-thread-permissions-types.js @@ -1,5 +1,6 @@ // @flow +import invariant from 'invariant'; import _mapValues from 'lodash/fp/mapValues.js'; import type { @@ -51,11 +52,17 @@ const minimallyEncodeThreadCurrentUserInfo = ( threadCurrentUserInfo: ThreadCurrentUserInfo, -): MinimallyEncodedThreadCurrentUserInfo => ({ - ...threadCurrentUserInfo, - minimallyEncoded: true, - permissions: permissionsToBitmaskHex(threadCurrentUserInfo.permissions), -}); +): MinimallyEncodedThreadCurrentUserInfo => { + invariant( + !('minimallyEncoded' in threadCurrentUserInfo), + 'threadCurrentUserInfo is already minimally encoded.', + ); + return { + ...threadCurrentUserInfo, + minimallyEncoded: true, + permissions: permissionsToBitmaskHex(threadCurrentUserInfo.permissions), + }; +}; const decodeMinimallyEncodedThreadCurrentUserInfo = ( minimallyEncodedThreadCurrentUserInfo: MinimallyEncodedThreadCurrentUserInfo,