diff --git a/packages/connector/src/MosDevice.ts b/packages/connector/src/MosDevice.ts index bc6619ec..4670a966 100644 --- a/packages/connector/src/MosDevice.ts +++ b/packages/connector/src/MosDevice.ts @@ -355,14 +355,41 @@ export class MosDevice implements IMOSDevice { }, } } else if (data.roStoryMove) { + // From documentation: + // **Note**: If the second tag is blank move the story to the bottom of the Running Order. + + let storyIDs: string[] + + if (Array.isArray(data.roStoryMove.storyID)) { + storyIDs = data.roStoryMove.storyID + } else { + if (this.strict) { + // The storyID is xml-converted to a string if the second tag is missing. + // The spec says that there must be two storyID tags, so we'll throw an error here: + return new MosModel.ROAck( + { + ID: this.mosTypes.mosString128.create(data.roStoryMove.roID), + Status: this.mosTypes.mosString128.create( + `The second tag is missing in .` + ), + Stories: [], + }, + this.strict + ) + } else { + // Non strict mode: This is technically out of spec, but it's a common mistake, so we'll handle it like so: + storyIDs = [data.roStoryMove.storyID as string, ''] + } + } + data.roElementAction = { roID: data.roStoryMove.roID, operation: 'MOVE', element_target: { - storyID: data.roStoryMove.storyID[1], + storyID: storyIDs[1], }, element_source: { - storyID: data.roStoryMove.storyID[0], + storyID: storyIDs[0], }, } } else if (data.roStorySwap) { @@ -384,11 +411,53 @@ export class MosDevice implements IMOSDevice { storyID: data.roStoryDelete.storyID, }, } - } else if (data.roStoryMoveMultiple && data.roStoryMoveMultiple.storyID.length > 1) { - const l = data.roStoryMoveMultiple.storyID.length + } else if (data.roStoryMoveMultiple && data.roStoryMoveMultiple.storyID) { + const stories: string[] = Array.isArray(data.roStoryMoveMultiple.storyID) + ? (data.roStoryMoveMultiple.storyID as string[]) + : [data.roStoryMoveMultiple.storyID as string] + + { + // From documentation: + // Validation: Duplicate storyIDs are not permitted with in the storyID list. + // This prevents the move from being ambiguous; if two IDs are the same, it is unclear + // where in the playlist the story with duplicate ID must be placed. + const uniqueStoryIds = new Set() + for (const storyId of stories) { + if (uniqueStoryIds.has(storyId)) + return new MosModel.ROAck( + { + ID: this.mosTypes.mosString128.create(data.roStoryMoveMultiple.roID), + Status: this.mosTypes.mosString128.create( + `Duplicate storyIDs are not permitted with in the storyID list.` + ), + Stories: [], + }, + this.strict + ) + uniqueStoryIds.add(storyId) + } + } + + let target: string + let sources: string[] - const target = data.roStoryMoveMultiple.storyID[l - 1] - const sources = data.roStoryMoveMultiple.storyID.slice(0, l - 1) + if (stories.length > 1) { + target = stories[stories.length - 1] + sources = stories.slice(0, stories.length - 1) + } else { + if (this.strict) { + // Technically a no-op: + target = stories[0] + sources = [] + } else { + // Handling of edge-case: + // If there is only a single storyId, we assume that the single mentioned story should be moved to the end of the playlist + // (ie that there is supposed to be a second, blank storyId that was just omitted by the sender) + + target = '' + sources = stories + } + } data.roElementAction = { roID: data.roStoryMoveMultiple.roID, @@ -435,21 +504,26 @@ export class MosDevice implements IMOSDevice { itemID: data.roItemDelete.itemID, }, } - } else if (data.roItemMoveMultiple && data.roItemMoveMultiple.itemID.length > 1) { - const l = data.roItemMoveMultiple.itemID.length + } else if (data.roItemMoveMultiple && data.roItemMoveMultiple.itemID && data.roItemMoveMultiple.storyID) { + const items: string[] = Array.isArray(data.roItemMoveMultiple.itemID) + ? (data.roItemMoveMultiple.itemID as string[]) + : [data.roItemMoveMultiple.itemID as string] + + // An aditional validation checking the length of items can be added + const l = items.length - const target = data.roItemMoveMultiple.itemID[l - 1] - const sources = data.roItemMoveMultiple.itemID.slice(0, l - 1) + const target = items[l - 1] + const sources = items.slice(0, l - 1) data.roElementAction = { roID: data.roItemMoveMultiple.roID, operation: 'MOVE', element_target: { storyID: data.roItemMoveMultiple.storyID, - itemID: target, + itemID: l === 1 ? undefined : target, }, element_source: { - itemID: sources, + itemID: l === 1 ? items : sources, }, } } diff --git a/packages/connector/src/__mocks__/testData.ts b/packages/connector/src/__mocks__/testData.ts index 4374d382..2d5205ae 100644 --- a/packages/connector/src/__mocks__/testData.ts +++ b/packages/connector/src/__mocks__/testData.ts @@ -112,9 +112,12 @@ const xmlData = { roStoryInsert: `5PM HOTEL FIRE V: BRIDGE COLLAPSE Bridge Collapse B7 30848 M000627 testmos.enps.com\\\\server\\media\\clip392028cd2320s0d.mxfhttp://server/proxy/clipe.wmv 0 815 310 c01/l04/dve07 r00 PLAYLIST http://MOSA4.com/mos/supported_schemas/MOSAXML2.08 SHOLMES 2 463 a b PLAYLIST http://MOSA4.com/mos/supported_schemas/MOSBXML2.08 52 2 463 30849 M000628 testmos 0 815 310 c01/l04/dve07 r00 PLAYLIST http://MOSA4.com/mos/supported_schemas/MOSAXML2.08 SHOLMES 2 463 a b `, roStoryReplace: `5PM P: PHILLIPS INTERVIEW V: HOTEL FIRE Hotel Fire C1 30848 Hotel Fire vo M000702 testmos\\\\server\\media\\clip392028cd2320s0d.mxfhttp://server/proxy/clipe.wmv 0 900 800 c01/l04/dve07 r00 PLAYLIST http://MOSA4.com/mos/supported_schemas/MOSAXML2.08 SHOLMES 2 463 a b V: DORMITORY FIRE Dormitory Fire C2 1 Dormitory Fire vo M000705 testmos 0 800 310 c01/l04/dve07 r00 PLAYLIST http://MOSA4.com/mos/supported_schemas/MOSAXML2.08 SHOLMES 2 463 a b `, roStoryMove: `5PM V: BRIDGE COLLAPSE P: PHILLIPS INTERVIEW `, + roStoryMove_blank: `5PM V: BRIDGE COLLAPSE `, + roStoryMove_offspec_missing: `5PM V: BRIDGE COLLAPSE`, roStorySwap: `5PM V: BRIDGE COLLAPSE P: PHILLIPS INTERVIEW `, roStoryDelete: `5PM V: BRIDGE COLLAPSE P: PHILLIPS INTERVIEW `, roStoryMoveMultiple: `5PM 2 3 5 6 1 `, + roStoryMoveMultiple_single_storyId: `5PM 2 `, roItemInsert: ` 5PM 2597609 5 30848 Hotel Fire vo M00702 testmos \\\\server\\media\\clip392028cd2320s0d.mxfhttp://server/proxy/clipe.wmv 0 900 310 1 Dormitory Fire vo M00705 testmos 0 800 310 `, roItemReplace: ` 5PM 2597609 5 30848 Hotel Fire vo M00702 testmos\\\\server\\media\\clip392028cd2320s0d.mxfhttp://server/proxy/clipe.wmv 0 900 810 1 Dormitory Fire vo M00705 testmos 0 800 610 `, roItemMoveMultiple: `5PM Barn Fire 2 3 5 6 1 `, @@ -1523,6 +1526,10 @@ const xmlApiData = { StoryID: mosTypes.mosString128.create('P: PHILLIPS INTERVIEW'), }), roElementAction_roStoryMove_stories: [mosTypes.mosString128.create('V: BRIDGE COLLAPSE')], + roElementAction_roStoryMove_blank_action: literal({ + RunningOrderID: mosTypes.mosString128.create('5PM'), + StoryID: mosTypes.mosString128.create(''), + }), roElementAction_roStorySwap_action: literal({ RunningOrderID: mosTypes.mosString128.create('5PM'), }), @@ -1546,6 +1553,18 @@ const xmlApiData = { mosTypes.mosString128.create('5'), mosTypes.mosString128.create('6'), ], + // Thechnically a no-op, but if reading the docs literally...: + roElementAction_roStoryMoveMultiple_single_storyId_action: literal({ + RunningOrderID: mosTypes.mosString128.create('5PM'), + StoryID: mosTypes.mosString128.create('2'), + }), + roElementAction_roStoryMoveMultiple_single_storyId_stories: [], + // Assuming that the single storyId is the story to be moved: + roElementAction_roStoryMoveMultiple_single_storyId_offspec_action: literal({ + RunningOrderID: mosTypes.mosString128.create('5PM'), + StoryID: mosTypes.mosString128.create(''), + }), + roElementAction_roStoryMoveMultiple_single_storyId_offspec_stories: [mosTypes.mosString128.create('2')], roElementAction_roItemInsert_action: literal({ RunningOrderID: mosTypes.mosString128.create('5PM'), StoryID: mosTypes.mosString128.create('2597609'), diff --git a/packages/connector/src/__tests__/Profile2-non-strict.spec.ts b/packages/connector/src/__tests__/Profile2-non-strict.spec.ts new file mode 100644 index 00000000..376e3924 --- /dev/null +++ b/packages/connector/src/__tests__/Profile2-non-strict.spec.ts @@ -0,0 +1,297 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +import { + checkReplyToServer, + clearMocks, + decode, + doBeforeAll, + encode, + fakeIncomingMessage, + fixSnapshot, + getMessageId, + getMosConnection, + getMosDevice, + getXMLReply, + mosTypes, + setupMocks, +} from './lib' +import { + MosConnection, + MosDevice, + IMOSObject, + IMOSItem, + IMOSItemAction, + IMOSItemStatus, + IMOSROAck, + IMOSROAction, + IMOSROReadyToAir, + IMOSROStory, + IMOSRunningOrder, + IMOSRunningOrderBase, + IMOSRunningOrderStatus, + IMOSStoryAction, + IMOSStoryStatus, + IMOSListMachInfo, + IMOSString128, +} from '..' +import { SocketMock } from '../__mocks__/socket' +import { ServerMock } from '../__mocks__/server' +import { xmlData, xmlApiData } from '../__mocks__/testData' + +/* eslint-disable @typescript-eslint/no-unused-vars */ +// @ts-ignore imports are unused +import { Socket } from 'net' +/* eslint-enable @typescript-eslint/no-unused-vars */ + +beforeAll(() => { + setupMocks() +}) +beforeEach(() => { + clearMocks() +}) +describe('Profile 2 - non strict', () => { + let mosDevice: MosDevice + let mosConnection: MosConnection + let socketMockLower: SocketMock + let socketMockUpper: SocketMock + + let serverSocketMockLower: SocketMock + let serverSocketMockUpper: SocketMock + let serverSocketMockQuery: SocketMock + + let onRequestMachineInfo: jest.Mock + let onRequestMOSObject: jest.Mock + let onRequestAllMOSObjects: jest.Mock + let onCreateRunningOrder: jest.Mock + let onReplaceRunningOrder: jest.Mock + let onDeleteRunningOrder: jest.Mock + let onRequestRunningOrder: jest.Mock + let onMetadataReplace: jest.Mock + let onRunningOrderStatus: jest.Mock + let onStoryStatus: jest.Mock + let onItemStatus: jest.Mock + let onReadyToAir: jest.Mock + let onROInsertStories: jest.Mock + let onROInsertItems: jest.Mock + let onROReplaceStories: jest.Mock + let onROReplaceItems: jest.Mock + let onROMoveStories: jest.Mock + let onROMoveItems: jest.Mock + let onRODeleteStories: jest.Mock + let onRODeleteItems: jest.Mock + let onROSwapStories: jest.Mock + let onROSwapItems: jest.Mock + + const mockReplyRoAck = jest.fn((data) => { + const str = decode(data) + const messageID = getMessageId(str) + return encode(getXMLReply(messageID, xmlData.roAck)) + }) + + beforeAll(async () => { + SocketMock.mockClear() + ServerMock.mockClear() + + mosConnection = await getMosConnection( + { + '0': true, + '1': true, + '2': true, + }, + false + ) + mosDevice = await getMosDevice(mosConnection) + + // Profile 0: + onRequestMachineInfo = jest.fn(async () => { + return xmlApiData.machineInfo + }) + mosDevice.onRequestMachineInfo(async (): Promise => { + return onRequestMachineInfo() + }) + // Profile 1: + onRequestMOSObject = jest.fn() + onRequestAllMOSObjects = jest.fn() + mosDevice.onRequestMOSObject(async (objId: string): Promise => { + return onRequestMOSObject(objId) + }) + mosDevice.onRequestAllMOSObjects(async (): Promise> => { + return onRequestAllMOSObjects() + }) + + // Profile 2: + const roAckReply = async () => { + const ack: IMOSROAck = { + ID: mosTypes.mosString128.create('runningOrderId'), + Status: mosTypes.mosString128.create('OK'), + Stories: [], + } + return ack + } + onCreateRunningOrder = jest.fn(roAckReply) + onReplaceRunningOrder = jest.fn(roAckReply) + onDeleteRunningOrder = jest.fn(roAckReply) + onRequestRunningOrder = jest.fn(async () => { + return xmlApiData.roCreate + }) + onMetadataReplace = jest.fn(roAckReply) + onRunningOrderStatus = jest.fn(roAckReply) + onStoryStatus = jest.fn(roAckReply) + onItemStatus = jest.fn(roAckReply) + onReadyToAir = jest.fn(roAckReply) + onROInsertStories = jest.fn(roAckReply) + onROInsertItems = jest.fn(roAckReply) + onROReplaceStories = jest.fn(roAckReply) + onROReplaceItems = jest.fn(roAckReply) + onROMoveStories = jest.fn(roAckReply) + onROMoveItems = jest.fn(roAckReply) + onRODeleteStories = jest.fn(roAckReply) + onRODeleteItems = jest.fn(roAckReply) + onROSwapStories = jest.fn(roAckReply) + onROSwapItems = jest.fn(roAckReply) + + mosDevice.onCreateRunningOrder(async (ro: IMOSRunningOrder): Promise => { + return onCreateRunningOrder(ro) + }) + mosDevice.onReplaceRunningOrder(async (ro: IMOSRunningOrder): Promise => { + return onReplaceRunningOrder(ro) + }) + mosDevice.onDeleteRunningOrder(async (runningOrderId: IMOSString128): Promise => { + return onDeleteRunningOrder(runningOrderId) + }) + mosDevice.onRequestRunningOrder(async (runningOrderId: IMOSString128): Promise => { + return onRequestRunningOrder(runningOrderId) + }) + mosDevice.onMetadataReplace(async (metadata: IMOSRunningOrderBase): Promise => { + return onMetadataReplace(metadata) + }) + mosDevice.onRunningOrderStatus(async (status: IMOSRunningOrderStatus): Promise => { + return onRunningOrderStatus(status) + }) + mosDevice.onStoryStatus(async (status: IMOSStoryStatus): Promise => { + return onStoryStatus(status) + }) + mosDevice.onItemStatus(async (status: IMOSItemStatus): Promise => { + return onItemStatus(status) + }) + mosDevice.onReadyToAir(async (Action: IMOSROReadyToAir): Promise => { + return onReadyToAir(Action) + }) + mosDevice.onROInsertStories( + async (Action: IMOSStoryAction, Stories: Array): Promise => { + return onROInsertStories(Action, Stories) + } + ) + mosDevice.onROInsertItems(async (Action: IMOSItemAction, Items: Array): Promise => { + return onROInsertItems(Action, Items) + }) + mosDevice.onROReplaceStories( + async (Action: IMOSStoryAction, Stories: Array): Promise => { + return onROReplaceStories(Action, Stories) + } + ) + mosDevice.onROReplaceItems(async (Action: IMOSItemAction, Items: Array): Promise => { + return onROReplaceItems(Action, Items) + }) + mosDevice.onROMoveStories( + async (Action: IMOSStoryAction, Stories: Array): Promise => { + return onROMoveStories(Action, Stories) + } + ) + mosDevice.onROMoveItems(async (Action: IMOSItemAction, Items: Array): Promise => { + return onROMoveItems(Action, Items) + }) + mosDevice.onRODeleteStories(async (Action: IMOSROAction, Stories: Array): Promise => { + return onRODeleteStories(Action, Stories) + }) + mosDevice.onRODeleteItems(async (Action: IMOSStoryAction, Items: Array): Promise => { + return onRODeleteItems(Action, Items) + }) + mosDevice.onROSwapStories( + async (Action: IMOSROAction, StoryID0: IMOSString128, StoryID1: IMOSString128): Promise => { + return onROSwapStories(Action, StoryID0, StoryID1) + } + ) + mosDevice.onROSwapItems( + async (Action: IMOSStoryAction, ItemID0: IMOSString128, ItemID1: IMOSString128): Promise => { + return onROSwapItems(Action, ItemID0, ItemID1) + } + ) + const b = doBeforeAll() + socketMockLower = b.socketMockLower + socketMockUpper = b.socketMockUpper + serverSocketMockLower = b.serverSocketMockLower + serverSocketMockUpper = b.serverSocketMockUpper + serverSocketMockQuery = b.serverSocketMockQuery + + mosConnection.checkProfileValidness() + mosDevice.checkProfileValidness() + }) + beforeEach(() => { + onRequestMOSObject.mockClear() + onRequestAllMOSObjects.mockClear() + + onCreateRunningOrder.mockClear() + onReplaceRunningOrder.mockClear() + onDeleteRunningOrder.mockClear() + onRequestRunningOrder.mockClear() + onMetadataReplace.mockClear() + onRunningOrderStatus.mockClear() + onStoryStatus.mockClear() + onItemStatus.mockClear() + onReadyToAir.mockClear() + onROInsertStories.mockClear() + onROInsertItems.mockClear() + onROReplaceStories.mockClear() + onROReplaceItems.mockClear() + onROMoveStories.mockClear() + onROMoveItems.mockClear() + onRODeleteStories.mockClear() + onRODeleteItems.mockClear() + onROSwapStories.mockClear() + onROSwapItems.mockClear() + + socketMockLower.mockClear() + socketMockUpper.mockClear() + serverSocketMockLower.mockClear() + serverSocketMockUpper.mockClear() + serverSocketMockQuery.mockClear() + + mockReplyRoAck.mockClear() + }) + afterAll(async () => { + await mosDevice.dispose() + await mosConnection.dispose() + }) + describe('deprecated messages', () => { + // These methods are still supported, but will be removed in future versions of the mos protocol + test('roStoryMove - missing second ', async () => { + // Note: from documentation: + // https://mosprotocol.com/wp-content/MOS-Protocol-Documents/MOS_Protocol_Version_2.8.5_Final.htm#roStoryMove + // **Note**: If the second tag is blank move the story to the bottom of the Running Order. + + // Fake incoming message on socket: + const messageId = await fakeIncomingMessage(serverSocketMockLower, xmlData.roStoryMove_offspec_missing) + expect(onROMoveStories).toHaveBeenCalledTimes(1) + expect(onROMoveStories.mock.calls[0][0]).toEqual(xmlApiData.roElementAction_roStoryMove_blank_action) + expect(onROMoveStories.mock.calls[0][1]).toEqual(xmlApiData.roElementAction_roStoryMove_stories) + expect(fixSnapshot(onROMoveStories.mock.calls)).toMatchSnapshot() + await checkReplyToServer(serverSocketMockLower, messageId, '') + }) + test('roStoryMoveMultiple with single storyId', async () => { + // Fake incoming message on socket: + const messageId = await fakeIncomingMessage( + serverSocketMockLower, + xmlData.roStoryMoveMultiple_single_storyId + ) + expect(onROMoveStories).toHaveBeenCalledTimes(1) + expect(onROMoveStories.mock.calls[0][0]).toEqual( + xmlApiData.roElementAction_roStoryMoveMultiple_single_storyId_offspec_action + ) + expect(onROMoveStories.mock.calls[0][1]).toEqual( + xmlApiData.roElementAction_roStoryMoveMultiple_single_storyId_offspec_stories + ) + expect(fixSnapshot(onROMoveStories.mock.calls)).toMatchSnapshot() + await checkReplyToServer(serverSocketMockLower, messageId, '') + }) + }) +}) diff --git a/packages/connector/src/__tests__/Profile2.spec.ts b/packages/connector/src/__tests__/Profile2.spec.ts index 5bede02f..903fd430 100644 --- a/packages/connector/src/__tests__/Profile2.spec.ts +++ b/packages/connector/src/__tests__/Profile2.spec.ts @@ -1187,6 +1187,27 @@ describe('Profile 2', () => { expect(fixSnapshot(onROMoveStories.mock.calls)).toMatchSnapshot() await checkReplyToServer(serverSocketMockLower, messageId, '') }) + test('roStoryMove with blank', async () => { + // Fake incoming message on socket: + const messageId = await fakeIncomingMessage(serverSocketMockLower, xmlData.roStoryMove_blank) + expect(onROMoveStories).toHaveBeenCalledTimes(1) + expect(onROMoveStories.mock.calls[0][0]).toEqual(xmlApiData.roElementAction_roStoryMove_blank_action) + expect(onROMoveStories.mock.calls[0][1]).toEqual(xmlApiData.roElementAction_roStoryMove_stories) + expect(fixSnapshot(onROMoveStories.mock.calls)).toMatchSnapshot() + await checkReplyToServer(serverSocketMockLower, messageId, '') + }) + test('roStoryMove - missing second ', async () => { + // Note: from documentation: + // https://mosprotocol.com/wp-content/MOS-Protocol-Documents/MOS_Protocol_Version_2.8.5_Final.htm#roStoryMove + // **Note**: If the second tag is blank move the story to the bottom of the Running Order. + + // Fake incoming message on socket: + const messageId = await fakeIncomingMessage(serverSocketMockLower, xmlData.roStoryMove_offspec_missing) + expect(onROMoveStories).toHaveBeenCalledTimes(0) + + // This is out of spec: + await checkReplyToServer(serverSocketMockLower, messageId, '', 'The second', 'tag is missing') + }) test('roStorySwap', async () => { // Fake incoming message on socket: const messageId = await fakeIncomingMessage(serverSocketMockLower, xmlData.roStorySwap) @@ -1215,6 +1236,22 @@ describe('Profile 2', () => { expect(fixSnapshot(onROMoveStories.mock.calls)).toMatchSnapshot() await checkReplyToServer(serverSocketMockLower, messageId, '') }) + test('roStoryMoveMultiple with single storyId', async () => { + // Fake incoming message on socket: + const messageId = await fakeIncomingMessage( + serverSocketMockLower, + xmlData.roStoryMoveMultiple_single_storyId + ) + expect(onROMoveStories).toHaveBeenCalledTimes(1) + expect(onROMoveStories.mock.calls[0][0]).toEqual( + xmlApiData.roElementAction_roStoryMoveMultiple_single_storyId_action + ) + expect(onROMoveStories.mock.calls[0][1]).toEqual( + xmlApiData.roElementAction_roStoryMoveMultiple_single_storyId_stories + ) + expect(fixSnapshot(onROMoveStories.mock.calls)).toMatchSnapshot() + await checkReplyToServer(serverSocketMockLower, messageId, '') + }) test('roItemInsert', async () => { // Fake incoming message on socket: const messageId = await fakeIncomingMessage(serverSocketMockLower, xmlData.roItemInsert) diff --git a/packages/connector/src/__tests__/__snapshots__/Profile2-non-strict.spec.ts.snap b/packages/connector/src/__tests__/__snapshots__/Profile2-non-strict.spec.ts.snap new file mode 100644 index 00000000..2e0af120 --- /dev/null +++ b/packages/connector/src/__tests__/__snapshots__/Profile2-non-strict.spec.ts.snap @@ -0,0 +1,81 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Profile 2 - non strict deprecated messages roStoryMove - missing second 1`] = ` +[ + [ + { + "RunningOrderID": { + "_mosString128": "5PM", + }, + "StoryID": { + "_mosString128": "", + }, + }, + [ + { + "_mosString128": "V: BRIDGE COLLAPSE", + }, + ], + ], +] +`; + +exports[`Profile 2 - non strict deprecated messages roStoryMove - missing second : + their.mos.id + our.mos.id + 1633 + + runningOrderId + OK + + 1`] = ` +" + their.mos.id + our.mos.id + 1633 + + runningOrderId + OK + +" +`; + +exports[`Profile 2 - non strict deprecated messages roStoryMoveMultiple with single storyId 1`] = ` +[ + [ + { + "RunningOrderID": { + "_mosString128": "5PM", + }, + "StoryID": { + "_mosString128": "", + }, + }, + [ + { + "_mosString128": "2", + }, + ], + ], +] +`; + +exports[`Profile 2 - non strict deprecated messages roStoryMoveMultiple with single storyId: + their.mos.id + our.mos.id + 1634 + + runningOrderId + OK + + 1`] = ` +" + their.mos.id + our.mos.id + 1634 + + runningOrderId + OK + +" +`; diff --git a/packages/connector/src/__tests__/__snapshots__/Profile2.spec.ts.snap b/packages/connector/src/__tests__/__snapshots__/Profile2.spec.ts.snap index 1c4aeafd..fe19e9a1 100644 --- a/packages/connector/src/__tests__/__snapshots__/Profile2.spec.ts.snap +++ b/packages/connector/src/__tests__/__snapshots__/Profile2.spec.ts.snap @@ -32,7 +32,7 @@ exports[`Profile 2 deprecated messages roItemDelete 1`] = ` exports[`Profile 2 deprecated messages roItemDelete: their.mos.id our.mos.id - 1667 + 1670 runningOrderId OK @@ -41,7 +41,7 @@ exports[`Profile 2 deprecated messages roItemDelete: " their.mos.id our.mos.id - 1667 + 1670 runningOrderId OK @@ -114,7 +114,7 @@ exports[`Profile 2 deprecated messages roItemInsert 1`] = ` exports[`Profile 2 deprecated messages roItemInsert: their.mos.id our.mos.id - 1664 + 1667 runningOrderId OK @@ -123,7 +123,7 @@ exports[`Profile 2 deprecated messages roItemInsert: " their.mos.id our.mos.id - 1664 + 1667 runningOrderId OK @@ -166,7 +166,7 @@ exports[`Profile 2 deprecated messages roItemMoveMultiple 1`] = ` exports[`Profile 2 deprecated messages roItemMoveMultiple: their.mos.id our.mos.id - 1666 + 1669 runningOrderId OK @@ -175,7 +175,7 @@ exports[`Profile 2 deprecated messages roItemMoveMultiple: " their.mos.id our.mos.id - 1666 + 1669 runningOrderId OK @@ -248,7 +248,7 @@ exports[`Profile 2 deprecated messages roItemReplace 1`] = ` exports[`Profile 2 deprecated messages roItemReplace: their.mos.id our.mos.id - 1665 + 1668 runningOrderId OK @@ -257,7 +257,7 @@ exports[`Profile 2 deprecated messages roItemReplace: " their.mos.id our.mos.id - 1665 + 1668 runningOrderId OK @@ -421,7 +421,7 @@ exports[`Profile 2 deprecated messages roStoryDelete 1`] = ` exports[`Profile 2 deprecated messages roStoryDelete: their.mos.id our.mos.id - 1662 + 1664 runningOrderId OK @@ -430,7 +430,7 @@ exports[`Profile 2 deprecated messages roStoryDelete: " their.mos.id our.mos.id - 1662 + 1664 runningOrderId OK @@ -571,6 +571,26 @@ exports[`Profile 2 deprecated messages roStoryInsert: " `; +exports[`Profile 2 deprecated messages roStoryMove - missing second : + their.mos.id + our.mos.id + 1662 + + 5PM + The second <storyID> tag is missing in <roStoryMove>. + + 1`] = ` +" + their.mos.id + our.mos.id + 1662 + + 5PM + The second <storyID> tag is missing in <roStoryMove>. + +" +`; + exports[`Profile 2 deprecated messages roStoryMove 1`] = ` [ [ @@ -591,6 +611,46 @@ exports[`Profile 2 deprecated messages roStoryMove 1`] = ` ] `; +exports[`Profile 2 deprecated messages roStoryMove with blank 1`] = ` +[ + [ + { + "RunningOrderID": { + "_mosString128": "5PM", + }, + "StoryID": { + "_mosString128": "", + }, + }, + [ + { + "_mosString128": "V: BRIDGE COLLAPSE", + }, + ], + ], +] +`; + +exports[`Profile 2 deprecated messages roStoryMove with blank: + their.mos.id + our.mos.id + 1661 + + runningOrderId + OK + + 1`] = ` +" + their.mos.id + our.mos.id + 1661 + + runningOrderId + OK + +" +`; + exports[`Profile 2 deprecated messages roStoryMove: their.mos.id our.mos.id @@ -640,10 +700,46 @@ exports[`Profile 2 deprecated messages roStoryMoveMultiple 1`] = ` ] `; +exports[`Profile 2 deprecated messages roStoryMoveMultiple with single storyId 1`] = ` +[ + [ + { + "RunningOrderID": { + "_mosString128": "5PM", + }, + "StoryID": { + "_mosString128": "2", + }, + }, + [], + ], +] +`; + +exports[`Profile 2 deprecated messages roStoryMoveMultiple with single storyId: + their.mos.id + our.mos.id + 1666 + + runningOrderId + OK + + 1`] = ` +" + their.mos.id + our.mos.id + 1666 + + runningOrderId + OK + +" +`; + exports[`Profile 2 deprecated messages roStoryMoveMultiple: their.mos.id our.mos.id - 1663 + 1665 runningOrderId OK @@ -652,7 +748,7 @@ exports[`Profile 2 deprecated messages roStoryMoveMultiple: " their.mos.id our.mos.id - 1663 + 1665 runningOrderId OK @@ -824,7 +920,7 @@ exports[`Profile 2 deprecated messages roStorySwap 1`] = ` exports[`Profile 2 deprecated messages roStorySwap: their.mos.id our.mos.id - 1661 + 1663 runningOrderId OK @@ -833,7 +929,7 @@ exports[`Profile 2 deprecated messages roStorySwap: " their.mos.id our.mos.id - 1661 + 1663 runningOrderId OK diff --git a/packages/connector/src/__tests__/lib.ts b/packages/connector/src/__tests__/lib.ts index 91ed3b74..ab19b029 100644 --- a/packages/connector/src/__tests__/lib.ts +++ b/packages/connector/src/__tests__/lib.ts @@ -141,7 +141,11 @@ export function decode(data: Buffer): string { export function encode(str: string): Buffer { return iconv.encode(str, 'utf16-be') } -export async function checkReplyToServer(socket: SocketMock, messageId: number, replyString: string): Promise { +export async function checkReplyToServer( + socket: SocketMock, + messageId: number, + ...replyStrings: string[] +): Promise { // check reply to server: await socket.mockWaitForSentMessages() @@ -149,7 +153,9 @@ export async function checkReplyToServer(socket: SocketMock, messageId: number, // @ts-ignore mock const reply = decode(socket.mockSentMessage.mock.calls[0][0]) expect(reply).toContain('' + messageId + '') - expect(reply).toContain(replyString) + for (const replyString of Array.isArray(replyStrings) ? replyStrings : [replyStrings]) { + expect(reply).toContain(replyString) + } expect(reply).toMatchSnapshot(reply) } export async function getReplyToServer(socket: SocketMock, messageId: number): Promise {