diff --git a/keyserver/src/socket/socket.js b/keyserver/src/socket/socket.js --- a/keyserver/src/socket/socket.js +++ b/keyserver/src/socket/socket.js @@ -37,6 +37,7 @@ clientSocketMessageTypes, stateSyncPayloadTypes, serverSocketMessageTypes, + serverServerSocketMessageValidator, } from 'lib/types/socket-types.js'; import { ServerError } from 'lib/utils/errors.js'; import { values } from 'lib/utils/objects.js'; @@ -86,6 +87,7 @@ checkInputValidator, checkClientSupported, policiesValidator, + validateOutput, } from '../utils/validation-utils.js'; const clientSocketMessageInputValidator = t.union([ @@ -375,7 +377,13 @@ "shouldn't send message until connection established", ); if (this.ws.readyState === 1) { - this.ws.send(JSON.stringify(message)); + const { viewer } = this; + const validatedMessage = validateOutput( + viewer, + serverServerSocketMessageValidator, + message, + ); + this.ws.send(JSON.stringify(validatedMessage)); } }; diff --git a/keyserver/src/utils/validation-utils.js b/keyserver/src/utils/validation-utils.js --- a/keyserver/src/utils/validation-utils.js +++ b/keyserver/src/utils/validation-utils.js @@ -50,7 +50,7 @@ } function validateOutput( - viewer: Viewer, + viewer: ?Viewer, outputValidator: TType, data: T, ): T { @@ -63,8 +63,8 @@ } if ( - hasMinCodeVersion(viewer.platformDetails, 1000) && - !isWebPlatform(viewer.platformDetails?.platform) && + hasMinCodeVersion(viewer?.platformDetails, 1000) && + !isWebPlatform(viewer?.platformDetails?.platform) && convertToNewIDSchema ) { return convertServerIDsToClientIDs( diff --git a/lib/types/message-types.js b/lib/types/message-types.js --- a/lib/types/message-types.js +++ b/lib/types/message-types.js @@ -588,6 +588,12 @@ +truncationStatuses: MessageTruncationStatuses, +currentAsOf: number, }; +export const messagesResponseValidator: TInterface = + tShape({ + rawMessageInfos: t.list(rawMessageInfoValidator), + truncationStatuses: messageTruncationStatusesValidator, + currentAsOf: t.Number, + }); export type SimpleMessagesPayload = { +rawMessageInfos: $ReadOnlyArray, +truncationStatuses: MessageTruncationStatuses, @@ -673,6 +679,10 @@ export type NewMessagesPayload = { +messagesResult: MessagesResponse, }; +export const newMessagesPayloadValidator: TInterface = + tShape({ + messagesResult: messagesResponseValidator, + }); export type MessageStorePrunePayload = { +threadIDs: $ReadOnlyArray, diff --git a/lib/types/request-types.js b/lib/types/request-types.js --- a/lib/types/request-types.js +++ b/lib/types/request-types.js @@ -1,24 +1,33 @@ // @flow import invariant from 'invariant'; +import t, { type TUnion } from 'tcomb'; import { type ActivityUpdate } from './activity-types.js'; import type { Shape } from './core.js'; import type { SignedIdentityKeysBlob } from './crypto-types.js'; import type { Platform, PlatformDetails } from './device-types.js'; -import type { RawEntryInfo, CalendarQuery } from './entry-types.js'; +import { + type RawEntryInfo, + type CalendarQuery, + rawEntryInfoValidator, +} from './entry-types.js'; import type { ThreadInconsistencyReportShape, EntryInconsistencyReportShape, ClientThreadInconsistencyReportShape, ClientEntryInconsistencyReportShape, } from './report-types.js'; -import type { RawThreadInfo } from './thread-types.js'; -import type { - CurrentUserInfo, - OldCurrentUserInfo, - AccountUserInfo, +import { type RawThreadInfo, rawThreadInfoValidator } from './thread-types.js'; +import { + type CurrentUserInfo, + currentUserInfoValidator, + type OldCurrentUserInfo, + oldCurrentUserInfoValidator, + type AccountUserInfo, + accountUserInfoValidator, } from './user-types.js'; +import { tNumber, tShape, tID } from '../utils/validation-utils.js'; // "Server requests" are requests for information that the server delivers to // clients. Clients then respond to those requests with a "client response". @@ -55,6 +64,9 @@ type PlatformServerRequest = { +type: 0, }; +const platformServerRequestValidator = tShape({ + type: tNumber(serverRequestTypes.PLATFORM), +}); type PlatformClientResponse = { +type: 0, +platform: Platform, @@ -68,6 +80,11 @@ type PlatformDetailsServerRequest = { type: 3, }; +const platformDetailsServerRequestValidator = + tShape({ + type: tNumber(serverRequestTypes.PLATFORM_DETAILS), + }); + type PlatformDetailsClientResponse = { type: 3, platformDetails: PlatformDetails, @@ -96,6 +113,32 @@ +deleteUserInfoIDs: string[], }>, }; +const serverCheckStateServerRequestValidator = + tShape({ + type: tNumber(serverRequestTypes.CHECK_STATE), + hashesToCheck: t.dict(t.String, t.Number), + failUnmentioned: t.maybe( + tShape({ + threadInfos: t.maybe(t.Boolean), + entryInfos: t.maybe(t.Boolean), + userInfos: t.maybe(t.Boolean), + }), + ), + stateChanges: t.maybe( + tShape({ + rawThreadInfos: t.maybe(t.list(rawThreadInfoValidator)), + rawEntryInfos: t.maybe(t.list(rawEntryInfoValidator)), + currentUserInfo: t.maybe( + t.union([currentUserInfoValidator, oldCurrentUserInfoValidator]), + ), + userInfos: t.maybe(t.list(accountUserInfoValidator)), + deleteThreadIDs: t.maybe(t.list(tID)), + deleteEntryIDs: t.maybe(t.list(tID)), + deleteUserInfoIDs: t.maybe(t.list(t.String)), + }), + ), + }); + type CheckStateClientResponse = { +type: 6, +hashResults: { +[key: string]: boolean }, @@ -109,6 +152,11 @@ type MoreOneTimeKeysServerRequest = { +type: 8, }; +const moreOneTimeKeysServerRequestValidator = + tShape({ + type: tNumber(serverRequestTypes.MORE_ONE_TIME_KEYS), + }); + type MoreOneTimeKeysClientResponse = { +type: 8, +keys: $ReadOnlyArray, @@ -117,6 +165,11 @@ type SignedIdentityKeysBlobServerRequest = { +type: 9, }; +const signedIdentityKeysBlobServerRequestValidator = + tShape({ + type: tNumber(serverRequestTypes.SIGNED_IDENTITY_KEYS_BLOB), + }); + type SignedIdentityKeysBlobClientResponse = { +type: 9, +signedIdentityKeysBlob: SignedIdentityKeysBlob, @@ -128,6 +181,15 @@ | ServerCheckStateServerRequest | MoreOneTimeKeysServerRequest | SignedIdentityKeysBlobServerRequest; +export const serverServerRequestValidator: TUnion = + t.union([ + platformServerRequestValidator, + platformDetailsServerRequestValidator, + serverCheckStateServerRequestValidator, + moreOneTimeKeysServerRequestValidator, + signedIdentityKeysBlobServerRequestValidator, + ]); + export type ClientResponse = | PlatformClientResponse | ThreadInconsistencyClientResponse diff --git a/lib/types/socket-types.js b/lib/types/socket-types.js --- a/lib/types/socket-types.js +++ b/lib/types/socket-types.js @@ -1,39 +1,55 @@ // @flow import invariant from 'invariant'; +import t, { type TInterface, type TUnion } from 'tcomb'; import { type ActivityUpdate, type UpdateActivityResult, + updateActivityResultValidator, } from './activity-types.js'; import type { Platform } from './device-types.js'; import type { APIRequest } from './endpoints.js'; import { type RawEntryInfo, + rawEntryInfoValidator, type CalendarQuery, defaultCalendarQuery, } from './entry-types.js'; -import type { MessagesResponse, NewMessagesPayload } from './message-types.js'; -import type { - ServerServerRequest, - ClientServerRequest, - ClientResponse, - ClientClientResponse, +import { + type MessagesResponse, + messagesResponseValidator, + type NewMessagesPayload, + newMessagesPayloadValidator, +} from './message-types.js'; +import { + type ServerServerRequest, + serverServerRequestValidator, + type ClientServerRequest, + type ClientResponse, + type ClientClientResponse, } from './request-types.js'; import type { SessionState, SessionIdentification } from './session-types.js'; -import type { RawThreadInfo } from './thread-types.js'; -import type { - ClientUpdatesResult, - ClientUpdatesResultWithUserInfos, - ServerUpdatesResult, - ServerUpdatesResultWithUserInfos, +import { type RawThreadInfo, rawThreadInfoValidator } from './thread-types.js'; +import { + type ClientUpdatesResult, + type ClientUpdatesResultWithUserInfos, + type ServerUpdatesResult, + serverUpdatesResultValidator, + type ServerUpdatesResultWithUserInfos, + serverUpdatesResultWithUserInfosValidator, } from './update-types.js'; -import type { - UserInfo, - CurrentUserInfo, - OldCurrentUserInfo, - LoggedOutUserInfo, +import { + type UserInfo, + userInfoValidator, + type CurrentUserInfo, + currentUserInfoValidator, + type OldCurrentUserInfo, + oldCurrentUserInfoValidator, + type LoggedOutUserInfo, + loggedOutUserInfoValidator, } from './user-types.js'; +import { tShape, tNumber, tID } from '../utils/validation-utils.js'; // The types of messages that the client sends across the socket export const clientSocketMessageTypes = Object.freeze({ @@ -169,6 +185,13 @@ +userInfos: $ReadOnlyArray, +updatesCurrentAsOf: number, }; +const baseFullStateSyncValidator = tShape({ + messagesResult: messagesResponseValidator, + threadInfos: t.dict(tID, rawThreadInfoValidator), + rawEntryInfos: t.list(rawEntryInfoValidator), + userInfos: t.list(userInfoValidator), + updatesCurrentAsOf: t.Number, +}); export type ClientFullStateSync = { ...BaseFullStateSync, @@ -189,12 +212,26 @@ ...BaseFullStateSync, +currentUserInfo: CurrentUserInfo | OldCurrentUserInfo, }; +const serverFullStateSyncValidator = tShape({ + ...baseFullStateSyncValidator.meta.props, + currentUserInfo: t.union([ + currentUserInfoValidator, + oldCurrentUserInfoValidator, + ]), +}); + export type ServerStateSyncFullSocketPayload = { ...ServerFullStateSync, +type: 0, // Included iff client is using sessionIdentifierTypes.BODY_SESSION_ID +sessionID?: string, }; +const serverStateSyncFullSocketPayloadValidator = + tShape({ + ...serverFullStateSyncValidator.meta.props, + type: tNumber(stateSyncPayloadTypes.FULL), + sessionID: t.maybe(t.String), + }); export const incrementalStateSyncActionType = 'INCREMENTAL_STATE_SYNC'; export type BaseIncrementalStateSync = { @@ -203,6 +240,12 @@ +deletedEntryIDs: $ReadOnlyArray, +userInfos: $ReadOnlyArray, }; +const baseIncrementalStateSyncValidator = tShape({ + messagesResult: messagesResponseValidator, + deltaEntryInfos: t.list(rawEntryInfoValidator), + deletedEntryIDs: t.list(tID), + userInfos: t.list(userInfoValidator), +}); export type ClientIncrementalStateSync = { ...BaseIncrementalStateSync, @@ -221,10 +264,20 @@ ...BaseIncrementalStateSync, +updatesResult: ServerUpdatesResult, }; +const serverIncrementalStateSyncValidator = tShape({ + ...baseIncrementalStateSyncValidator.meta.props, + updatesResult: serverUpdatesResultValidator, +}); + type ServerStateSyncIncrementalSocketPayload = { +type: 1, ...ServerIncrementalStateSync, }; +const serverStateSyncIncrementalSocketPayloadValidator = + tShape({ + type: tNumber(stateSyncPayloadTypes.INCREMENTAL), + ...serverIncrementalStateSyncValidator.meta.props, + }); export type ClientStateSyncSocketPayload = | ClientStateSyncFullSocketPayload @@ -232,12 +285,23 @@ export type ServerStateSyncSocketPayload = | ServerStateSyncFullSocketPayload | ServerStateSyncIncrementalSocketPayload; +const serverStateSyncSocketPayloadValidator = t.union([ + serverStateSyncFullSocketPayloadValidator, + serverStateSyncIncrementalSocketPayloadValidator, +]); export type ServerStateSyncServerSocketMessage = { +type: 0, +responseTo: number, +payload: ServerStateSyncSocketPayload, }; +export const serverStateSyncServerSocketMessageValidator: TInterface = + tShape({ + type: tNumber(serverSocketMessageTypes.STATE_SYNC), + responseTo: t.Number, + payload: serverStateSyncSocketPayloadValidator, + }); + export type ServerRequestsServerSocketMessage = { +type: 1, +responseTo?: number, @@ -245,12 +309,29 @@ +serverRequests: $ReadOnlyArray, }, }; +export const serverRequestsServerSocketMessageValidator: TInterface = + tShape({ + type: tNumber(serverSocketMessageTypes.REQUESTS), + responseTo: t.maybe(t.Number), + payload: tShape({ + serverRequests: t.list(serverServerRequestValidator), + }), + }); + export type ErrorServerSocketMessage = { type: 2, responseTo?: number, message: string, payload?: Object, }; +export const errorServerSocketMessageValidator: TInterface = + tShape({ + type: tNumber(serverSocketMessageTypes.ERROR), + responseTo: t.maybe(t.Number), + message: t.String, + payload: t.maybe(t.Object), + }); + export type AuthErrorServerSocketMessage = { type: 3, responseTo: number, @@ -262,28 +343,70 @@ currentUserInfo: LoggedOutUserInfo, }, }; +export const authErrorServerSocketMessageValidator: TInterface = + tShape({ + type: tNumber(serverSocketMessageTypes.AUTH_ERROR), + responseTo: t.Number, + message: t.String, + sessionChange: t.maybe( + tShape({ cookie: t.String, currentUserInfo: loggedOutUserInfoValidator }), + ), + }); + export type ActivityUpdateResponseServerSocketMessage = { +type: 4, +responseTo: number, +payload: UpdateActivityResult, }; +export const activityUpdateResponseServerSocketMessageValidator: TInterface = + tShape({ + type: tNumber(serverSocketMessageTypes.ACTIVITY_UPDATE_RESPONSE), + responseTo: t.Number, + payload: updateActivityResultValidator, + }); + export type PongServerSocketMessage = { +type: 5, +responseTo: number, }; +export const pongServerSocketMessageValidator: TInterface = + tShape({ + type: tNumber(serverSocketMessageTypes.PONG), + responseTo: t.Number, + }); + export type ServerUpdatesServerSocketMessage = { +type: 6, +payload: ServerUpdatesResultWithUserInfos, }; +export const serverUpdatesServerSocketMessageValidator: TInterface = + tShape({ + type: tNumber(serverSocketMessageTypes.UPDATES), + payload: serverUpdatesResultWithUserInfosValidator, + }); + export type MessagesServerSocketMessage = { +type: 7, +payload: NewMessagesPayload, }; +export const messagesServerSocketMessageValidator: TInterface = + tShape({ + type: tNumber(serverSocketMessageTypes.MESSAGES), + payload: newMessagesPayloadValidator, + }); + export type APIResponseServerSocketMessage = { +type: 8, +responseTo: number, - +payload: Object, -}; + +payload?: Object, +}; +export const apiResponseServerSocketMessageValidator: TInterface = + tShape({ + type: tNumber(serverSocketMessageTypes.API_RESPONSE), + responseTo: t.Number, + payload: t.maybe(t.Object), + }); + export type ServerServerSocketMessage = | ServerStateSyncServerSocketMessage | ServerRequestsServerSocketMessage @@ -294,6 +417,18 @@ | ServerUpdatesServerSocketMessage | MessagesServerSocketMessage | APIResponseServerSocketMessage; +export const serverServerSocketMessageValidator: TUnion = + t.union([ + serverStateSyncServerSocketMessageValidator, + serverRequestsServerSocketMessageValidator, + errorServerSocketMessageValidator, + authErrorServerSocketMessageValidator, + activityUpdateResponseServerSocketMessageValidator, + pongServerSocketMessageValidator, + serverUpdatesServerSocketMessageValidator, + messagesServerSocketMessageValidator, + apiResponseServerSocketMessageValidator, + ]); export type ClientRequestsServerSocketMessage = { +type: 1, diff --git a/lib/types/update-types.js b/lib/types/update-types.js --- a/lib/types/update-types.js +++ b/lib/types/update-types.js @@ -378,10 +378,21 @@ +currentAsOf: number, +newUpdates: $ReadOnlyArray, }; +export const serverUpdatesResultValidator: TInterface = + tShape({ + currentAsOf: t.Number, + newUpdates: t.list(serverUpdateInfoValidator), + }); + export type ServerUpdatesResultWithUserInfos = { +updatesResult: ServerUpdatesResult, +userInfos: $ReadOnlyArray, }; +export const serverUpdatesResultWithUserInfosValidator: TInterface = + tShape({ + updatesResult: serverUpdatesResultValidator, + userInfos: t.list(userInfoValidator), + }); export type ClientUpdatesResult = { +currentAsOf: number, diff --git a/lib/types/user-types.js b/lib/types/user-types.js --- a/lib/types/user-types.js +++ b/lib/types/user-types.js @@ -1,6 +1,6 @@ // @flow -import t, { type TInterface, type TDict } from 'tcomb'; +import t, { type TInterface, type TDict, type TUnion } from 'tcomb'; import { type DefaultNotificationPayload, @@ -112,7 +112,16 @@ tShape({ id: t.String, anonymous: tBool(true) }); export type OldCurrentUserInfo = OldLoggedInUserInfo | LoggedOutUserInfo; +export const oldCurrentUserInfoValidator: TUnion = t.union([ + oldLoggedInUserInfoValidator, + loggedOutUserInfoValidator, +]); + export type CurrentUserInfo = LoggedInUserInfo | LoggedOutUserInfo; +export const currentUserInfoValidator: TUnion = t.union([ + loggedInUserInfoValidator, + loggedOutUserInfoValidator, +]); export type PasswordUpdate = { +updatedFields: { diff --git a/lib/types/validation.test.js b/lib/types/validation.test.js --- a/lib/types/validation.test.js +++ b/lib/types/validation.test.js @@ -9,6 +9,18 @@ mediaValidator, } from './media-types.js'; import { messageTypes } from './message-types-enum.js'; +import { + activityUpdateResponseServerSocketMessageValidator, + apiResponseServerSocketMessageValidator, + authErrorServerSocketMessageValidator, + errorServerSocketMessageValidator, + messagesServerSocketMessageValidator, + pongServerSocketMessageValidator, + serverRequestsServerSocketMessageValidator, + serverSocketMessageTypes, + serverStateSyncServerSocketMessageValidator, + serverUpdatesServerSocketMessageValidator, +} from './socket-types.js'; import { threadTypes, rawThreadInfoValidator } from './thread-types.js'; import { updateTypes, @@ -667,71 +679,71 @@ }); }); -describe('server update validation', () => { - const updates = [ - { - type: updateTypes.DELETE_ACCOUNT, - id: '98424', - time: 1640870111106, - deletedUserID: '98262', - }, - { - type: updateTypes.UPDATE_THREAD, - id: '97948', - time: 1640868525494, - threadInfo: thread, - }, - { - type: updateTypes.UPDATE_THREAD_READ_STATUS, - id: '98002', - time: 1640869373326, - threadID: '83794', - unread: true, - }, - { - type: updateTypes.DELETE_THREAD, - id: '98208', - time: 1640869773339, - threadID: '97852', - }, - { - type: updateTypes.JOIN_THREAD, - id: '98126', - time: 1640869494461, - threadInfo: thread, - rawMessageInfos: messages, - truncationStatus: 'exhaustive', - rawEntryInfos: [entry], - }, - { - type: updateTypes.BAD_DEVICE_TOKEN, - id: '98208', - time: 1640869773495, - deviceToken: 'some-device-token', - }, - { - type: updateTypes.UPDATE_ENTRY, - id: '98233', - time: 1640869844908, - entryInfo: entry, - }, - { - type: updateTypes.UPDATE_CURRENT_USER, - id: '98237', - time: 1640869934058, - currentUserInfo: { - id: '256', - username: 'ashoat', - }, - }, - { - type: updateTypes.UPDATE_USER, - id: '97988', - time: 1640869211822, - updatedUserID: '86565', +const updates = [ + { + type: updateTypes.DELETE_ACCOUNT, + id: '98424', + time: 1640870111106, + deletedUserID: '98262', + }, + { + type: updateTypes.UPDATE_THREAD, + id: '97948', + time: 1640868525494, + threadInfo: thread, + }, + { + type: updateTypes.UPDATE_THREAD_READ_STATUS, + id: '98002', + time: 1640869373326, + threadID: '83794', + unread: true, + }, + { + type: updateTypes.DELETE_THREAD, + id: '98208', + time: 1640869773339, + threadID: '97852', + }, + { + type: updateTypes.JOIN_THREAD, + id: '98126', + time: 1640869494461, + threadInfo: thread, + rawMessageInfos: messages, + truncationStatus: 'exhaustive', + rawEntryInfos: [entry], + }, + { + type: updateTypes.BAD_DEVICE_TOKEN, + id: '98208', + time: 1640869773495, + deviceToken: 'some-device-token', + }, + { + type: updateTypes.UPDATE_ENTRY, + id: '98233', + time: 1640869844908, + entryInfo: entry, + }, + { + type: updateTypes.UPDATE_CURRENT_USER, + id: '98237', + time: 1640869934058, + currentUserInfo: { + id: '256', + username: 'ashoat', }, - ]; + }, + { + type: updateTypes.UPDATE_USER, + id: '97988', + time: 1640869211822, + updatedUserID: '86565', + }, +]; +describe('server update validation', () => { const validatorByUpdateType = { [updateTypes.DELETE_ACCOUNT]: accountDeletionUpdateInfoValidator, [updateTypes.UPDATE_THREAD]: threadUpdateInfoValidator, @@ -766,3 +778,126 @@ } } }); + +describe('socket message validation', () => { + const socketMessages = [ + { + type: serverSocketMessageTypes.STATE_SYNC, + responseTo: 0, + payload: { + type: 1, + messagesResult: { + rawMessageInfos: messages, + truncationStatuses: { '86033': 'unchanged' }, + currentAsOf: 1683296863468, + }, + updatesResult: { + newUpdates: updates, + currentAsOf: 1683296863489, + }, + deltaEntryInfos: [], + deletedEntryIDs: [], + userInfos: [], + }, + }, + { + type: serverSocketMessageTypes.REQUESTS, + payload: { + serverRequests: [ + { + type: 6, + hashesToCheck: { + threadInfos: 3311950643, + entryInfos: 3191324567, + currentUserInfo: 820850779, + userInfos: 707653884, + }, + }, + ], + }, + }, + { + type: serverSocketMessageTypes.ACTIVITY_UPDATE_RESPONSE, + responseTo: 194, + payload: { unfocusedToUnread: [] }, + }, + { type: serverSocketMessageTypes.PONG, responseTo: 190 }, + { + type: 6, + payload: { + updatesResult: { + currentAsOf: 1683298141720, + newUpdates: [ + { + type: 1, + id: '94428', + time: 1683298141720, + threadInfo: thread, + }, + ], + }, + userInfos: [], + }, + }, + { + type: serverSocketMessageTypes.MESSAGES, + payload: { + messagesResult: { + rawMessageInfos: messages, + truncationStatuses: { '86033': 'unchanged' }, + currentAsOf: 1683298141707, + }, + }, + }, + { + type: serverSocketMessageTypes.API_RESPONSE, + responseTo: 209, + payload: { + rawMessageInfos: messages, + truncationStatuses: { '1': 'exhaustive' }, + userInfos: {}, + }, + }, + ]; + + const validatorByMessageType = { + [serverSocketMessageTypes.STATE_SYNC]: + serverStateSyncServerSocketMessageValidator, + [serverSocketMessageTypes.REQUESTS]: + serverRequestsServerSocketMessageValidator, + [serverSocketMessageTypes.ERROR]: errorServerSocketMessageValidator, + [serverSocketMessageTypes.AUTH_ERROR]: + authErrorServerSocketMessageValidator, + [serverSocketMessageTypes.ACTIVITY_UPDATE_RESPONSE]: + activityUpdateResponseServerSocketMessageValidator, + [serverSocketMessageTypes.PONG]: pongServerSocketMessageValidator, + [serverSocketMessageTypes.UPDATES]: + serverUpdatesServerSocketMessageValidator, + [serverSocketMessageTypes.MESSAGES]: messagesServerSocketMessageValidator, + [serverSocketMessageTypes.API_RESPONSE]: + apiResponseServerSocketMessageValidator, + }; + + for (const validatorMessageType in validatorByMessageType) { + const validator = validatorByMessageType[validatorMessageType]; + const validatorMessageTypeName = _findKey( + e => e === Number(validatorMessageType), + )(serverSocketMessageTypes); + + for (const message of socketMessages) { + const messageTypeName = _findKey(e => e === message.type)( + serverSocketMessageTypes, + ); + + if (Number(validatorMessageType) === message.type) { + it(`${validatorMessageTypeName} should validate ${messageTypeName}`, () => { + expect(validator.is(message)).toBe(true); + }); + } else { + it(`${validatorMessageTypeName} shouldn't validate ${messageTypeName}`, () => { + expect(validator.is(message)).toBe(false); + }); + } + } + } +});