|
| 1 | +import { assert, expect } from 'chai'; |
| 2 | +import { Context, ContextMetadata, ContextWithMetadata } from '@finos/fdc3'; |
| 3 | +import { ChannelControlImpl } from '../support/channels-support'; |
| 4 | +import { ContextMetadataValidator } from '../support/context-metadata-support'; |
| 5 | +import { |
| 6 | + JOIN_AND_BROADCAST, |
| 7 | + JOIN_AND_BROADCAST_WITH_TRACE_ID, |
| 8 | + JOIN_AND_BROADCAST_WITH_SIGNATURE_CUSTOM, |
| 9 | + APP_CHANNEL_AND_BROADCAST, |
| 10 | +} from '../support/channel-control'; |
| 11 | +import constants from '../../constants'; |
| 12 | +import { wait } from '../../utils'; |
| 13 | +import { getAgent } from '@finos/fdc3'; |
| 14 | +import { APIDocumentation } from '../support/apiDocuments'; |
| 15 | + |
| 16 | +const documentation = '\r\nDocumentation: ' + APIDocumentation.desktopAgent + '\r\nCause:'; |
| 17 | +const validator = new ContextMetadataValidator(); |
| 18 | + |
| 19 | +export default async () => { |
| 20 | + const fdc3 = await getAgent(); |
| 21 | + const cc = new ChannelControlImpl(fdc3); |
| 22 | + |
| 23 | + return describe('fdc3.contextMetadata', () => { |
| 24 | + beforeEach(cc.leaveChannel); |
| 25 | + |
| 26 | + afterEach(async function afterEach() { |
| 27 | + await cc.closeMockApp(this.currentTest?.title ?? 'Some-Test-Title'); |
| 28 | + }); |
| 29 | + |
| 30 | + // --- User Channel Tests --- |
| 31 | + |
| 32 | + const ucMetadataBroadcast = |
| 33 | + '(3.0-UCContextMetadataOnBroadcast) Should receive ContextMetadata with source and timestamp when context is broadcast on a user channel'; |
| 34 | + it(ucMetadataBroadcast, async () => { |
| 35 | + const errorMessage = `\r\nSteps:\r\n- App A adds fdc3.instrument context listener\r\n- App A joins channel\r\n- App B joins channel and broadcasts fdc3.instrument${documentation}`; |
| 36 | + |
| 37 | + const resolveExecutionCompleteListener = cc.initCompleteListener(ucMetadataBroadcast); |
| 38 | + let receivedMetadata: ContextMetadata | undefined; |
| 39 | + let receivedContext = false; |
| 40 | + |
| 41 | + const listener = await cc.setupAndValidateListener( |
| 42 | + null, |
| 43 | + 'fdc3.instrument', |
| 44 | + 'fdc3.instrument', |
| 45 | + errorMessage, |
| 46 | + (_ctx: Context, metadata?: ContextMetadata) => { |
| 47 | + receivedMetadata = metadata; |
| 48 | + receivedContext = true; |
| 49 | + } |
| 50 | + ); |
| 51 | + |
| 52 | + const channel = await cc.getNonGlobalUserChannel(); |
| 53 | + await cc.joinChannel(channel); |
| 54 | + await cc.openChannelApp(ucMetadataBroadcast, channel.id, JOIN_AND_BROADCAST); |
| 55 | + await resolveExecutionCompleteListener; |
| 56 | + |
| 57 | + try { |
| 58 | + if (!receivedContext) { |
| 59 | + await wait(constants.ShortWait); |
| 60 | + } |
| 61 | + assert.isTrue(receivedContext, `No context received!${errorMessage}`); |
| 62 | + assert.isDefined(receivedMetadata, `No metadata received with context${errorMessage}`); |
| 63 | + validator.validateRequiredFields(receivedMetadata!, 'ChannelsAppId'); |
| 64 | + } finally { |
| 65 | + cc.unsubscribeListeners([listener]); |
| 66 | + } |
| 67 | + }); |
| 68 | + |
| 69 | + const ucMetadataTraceId = |
| 70 | + '(3.0-UCContextMetadataTraceId) Should receive app-provided traceId in ContextMetadata on user channel broadcast'; |
| 71 | + it(ucMetadataTraceId, async () => { |
| 72 | + const errorMessage = `\r\nSteps:\r\n- App A adds listener\r\n- App B broadcasts with traceId metadata${documentation}`; |
| 73 | + |
| 74 | + const resolveExecutionCompleteListener = cc.initCompleteListener(ucMetadataTraceId); |
| 75 | + let receivedMetadata: ContextMetadata | undefined; |
| 76 | + let receivedContext = false; |
| 77 | + |
| 78 | + const listener = await cc.setupAndValidateListener( |
| 79 | + null, |
| 80 | + 'fdc3.instrument', |
| 81 | + 'fdc3.instrument', |
| 82 | + errorMessage, |
| 83 | + (_ctx: Context, metadata?: ContextMetadata) => { |
| 84 | + receivedMetadata = metadata; |
| 85 | + receivedContext = true; |
| 86 | + } |
| 87 | + ); |
| 88 | + |
| 89 | + const channel = await cc.getNonGlobalUserChannel(); |
| 90 | + await cc.joinChannel(channel); |
| 91 | + await cc.openChannelApp(ucMetadataTraceId, channel.id, JOIN_AND_BROADCAST_WITH_TRACE_ID); |
| 92 | + await resolveExecutionCompleteListener; |
| 93 | + |
| 94 | + try { |
| 95 | + if (!receivedContext) { |
| 96 | + await wait(constants.ShortWait); |
| 97 | + } |
| 98 | + assert.isTrue(receivedContext, `No context received!${errorMessage}`); |
| 99 | + assert.isDefined(receivedMetadata, `No metadata received${errorMessage}`); |
| 100 | + validator.validateRequiredFields(receivedMetadata!); |
| 101 | + validator.validateTraceId(receivedMetadata!, 'test-trace-123'); |
| 102 | + } finally { |
| 103 | + cc.unsubscribeListeners([listener]); |
| 104 | + } |
| 105 | + }); |
| 106 | + |
| 107 | + const ucMetadataSignatureCustom = |
| 108 | + '(3.0-UCContextMetadataSignatureCustom) Should receive app-provided signature and custom fields in ContextMetadata on user channel broadcast'; |
| 109 | + it(ucMetadataSignatureCustom, async () => { |
| 110 | + const errorMessage = `\r\nSteps:\r\n- App A adds listener\r\n- App B broadcasts with signature and custom metadata${documentation}`; |
| 111 | + |
| 112 | + const resolveExecutionCompleteListener = cc.initCompleteListener(ucMetadataSignatureCustom); |
| 113 | + let receivedMetadata: ContextMetadata | undefined; |
| 114 | + let receivedContext = false; |
| 115 | + |
| 116 | + const listener = await cc.setupAndValidateListener( |
| 117 | + null, |
| 118 | + 'fdc3.instrument', |
| 119 | + 'fdc3.instrument', |
| 120 | + errorMessage, |
| 121 | + (_ctx: Context, metadata?: ContextMetadata) => { |
| 122 | + receivedMetadata = metadata; |
| 123 | + receivedContext = true; |
| 124 | + } |
| 125 | + ); |
| 126 | + |
| 127 | + const channel = await cc.getNonGlobalUserChannel(); |
| 128 | + await cc.joinChannel(channel); |
| 129 | + await cc.openChannelApp(ucMetadataSignatureCustom, channel.id, JOIN_AND_BROADCAST_WITH_SIGNATURE_CUSTOM); |
| 130 | + await resolveExecutionCompleteListener; |
| 131 | + |
| 132 | + try { |
| 133 | + if (!receivedContext) { |
| 134 | + await wait(constants.ShortWait); |
| 135 | + } |
| 136 | + assert.isTrue(receivedContext, `No context received!${errorMessage}`); |
| 137 | + assert.isDefined(receivedMetadata, `No metadata received${errorMessage}`); |
| 138 | + validator.validateRequiredFields(receivedMetadata!); |
| 139 | + validator.validateSignature(receivedMetadata!, 'sig-abc'); |
| 140 | + validator.validateCustom(receivedMetadata!, 'region', 'EMEA'); |
| 141 | + } finally { |
| 142 | + cc.unsubscribeListeners([listener]); |
| 143 | + } |
| 144 | + }); |
| 145 | + |
| 146 | + // --- App Channel Tests --- |
| 147 | + |
| 148 | + const acMetadataBroadcast = |
| 149 | + '(3.0-ACContextMetadataOnBroadcast) Should receive ContextMetadata with source and timestamp when context is broadcast on an app channel'; |
| 150 | + it(acMetadataBroadcast, async () => { |
| 151 | + const errorMessage = `\r\nSteps:\r\n- App A gets app channel and adds listener\r\n- App B gets same channel and broadcasts${documentation}`; |
| 152 | + |
| 153 | + const resolveExecutionCompleteListener = cc.initCompleteListener(acMetadataBroadcast); |
| 154 | + const testChannel = await fdc3.getOrCreateChannel('test-channel'); |
| 155 | + let receivedMetadata: ContextMetadata | undefined; |
| 156 | + let receivedContext = false; |
| 157 | + |
| 158 | + const listener = await testChannel.addContextListener( |
| 159 | + 'fdc3.instrument', |
| 160 | + (context: Context, metadata?: ContextMetadata) => { |
| 161 | + expect(context.type).to.be.equals('fdc3.instrument', errorMessage); |
| 162 | + receivedMetadata = metadata; |
| 163 | + receivedContext = true; |
| 164 | + } |
| 165 | + ); |
| 166 | + |
| 167 | + await cc.openChannelApp(acMetadataBroadcast, 'test-channel', APP_CHANNEL_AND_BROADCAST); |
| 168 | + await resolveExecutionCompleteListener; |
| 169 | + |
| 170 | + try { |
| 171 | + if (!receivedContext) { |
| 172 | + await wait(constants.ShortWait); |
| 173 | + } |
| 174 | + assert.isTrue(receivedContext, `No context received!${errorMessage}`); |
| 175 | + assert.isDefined(receivedMetadata, `No metadata received${errorMessage}`); |
| 176 | + validator.validateRequiredFields(receivedMetadata!, 'ChannelsAppId'); |
| 177 | + } finally { |
| 178 | + listener.unsubscribe(); |
| 179 | + } |
| 180 | + }); |
| 181 | + |
| 182 | + // --- getCurrentContextWithMetadata Tests --- |
| 183 | + |
| 184 | + const acGetCurrentContextWithMetadata = |
| 185 | + '(3.0-ACGetCurrentContextWithMetadata) getCurrentContextWithMetadata should return context and metadata from an app channel'; |
| 186 | + it(acGetCurrentContextWithMetadata, async () => { |
| 187 | + const errorMessage = `\r\nSteps:\r\n- App B broadcasts to app channel\r\n- App A calls getCurrentContextWithMetadata${documentation}`; |
| 188 | + |
| 189 | + const resolveExecutionCompleteListener = cc.initCompleteListener(acGetCurrentContextWithMetadata); |
| 190 | + const testChannel = await fdc3.getOrCreateChannel('test-channel'); |
| 191 | + |
| 192 | + await cc.openChannelApp(acGetCurrentContextWithMetadata, 'test-channel', APP_CHANNEL_AND_BROADCAST); |
| 193 | + await resolveExecutionCompleteListener; |
| 194 | + |
| 195 | + // Allow time for the broadcast to be stored |
| 196 | + await wait(constants.ShortWait); |
| 197 | + |
| 198 | + const result: ContextWithMetadata | null = await testChannel.getCurrentContextWithMetadata('fdc3.instrument'); |
| 199 | + |
| 200 | + assert.isNotNull(result, `getCurrentContextWithMetadata returned null${errorMessage}`); |
| 201 | + expect(result!.context.type).to.be.equal('fdc3.instrument'); |
| 202 | + assert.isDefined(result!.metadata, `metadata was not returned${errorMessage}`); |
| 203 | + validator.validateRequiredFields(result!.metadata, 'ChannelsAppId'); |
| 204 | + }); |
| 205 | + |
| 206 | + const acGetCurrentContextWithMetadataNull = |
| 207 | + '(3.0-ACGetCurrentContextWithMetadataNull) getCurrentContextWithMetadata should return null on an empty channel'; |
| 208 | + it(acGetCurrentContextWithMetadataNull, async () => { |
| 209 | + const testChannel = await fdc3.getOrCreateChannel('test-channel-empty-' + cc.getRandomId()); |
| 210 | + const result: ContextWithMetadata | null = await testChannel.getCurrentContextWithMetadata('fdc3.instrument'); |
| 211 | + assert.isNull(result, 'getCurrentContextWithMetadata should return null on an empty channel'); |
| 212 | + }); |
| 213 | + }); |
| 214 | +}; |
0 commit comments