Skip to content

Commit

Permalink
Fixed Raise and Find Intents Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
robmoffat committed Feb 21, 2024
1 parent b3c0691 commit 72daffc
Show file tree
Hide file tree
Showing 16 changed files with 231 additions and 91 deletions.
11 changes: 5 additions & 6 deletions packages/da/src/intents/DefaultIntentSupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,17 @@ export class DefaultIntentSupport implements IntentSupport {
return this.raiseSpecificIntent(intent, context, matched.apps[0])
} else {
// need to do the intent resolver
const chosentIntent = await this.intentResolver.resolveIntent([matched])
return this.raiseSpecificIntent(intent, context, chosentIntent.chosenApp)
const chosentIntent = await this.intentResolver.chooseIntent([matched])
return this.raiseSpecificIntent(chosentIntent.intent.name, context, chosentIntent.chosenApp)
}
}

async raiseIntentForContext(context: Context, app?: AppIdentifier | undefined): Promise<IntentResolution> {
var matched = await this.findIntentsByContext(context)

if (app) {
matched = matched
.map(m => this.filterApps(m, app))
.filter(m => m.apps.length == 0)
matched = matched.map(m => this.filterApps(m, app))
matched = matched.filter(m => m.apps.length > 0)
}

if (matched.length == 0) {
Expand All @@ -129,7 +128,7 @@ export class DefaultIntentSupport implements IntentSupport {
return this.raiseSpecificIntent(matched[0].intent.name, context, matched[0].apps[0])
} else {
// need to do the intent resolver
const chosentIntent = await this.intentResolver.resolveIntent(matched)
const chosentIntent = await this.intentResolver.chooseIntent(matched)
return this.raiseSpecificIntent(chosentIntent.intent.name, context, chosentIntent.chosenApp)
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/da/src/intents/IntentResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface SingleAppIntent {

export interface IntentResolver {

resolveIntent(appIntents: AppIntent[]) : SingleAppIntent
chooseIntent(appIntents: AppIntent[]) : SingleAppIntent

}

14 changes: 14 additions & 0 deletions packages/da/test/features/rasie-intent.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

Feature: Intent Listeners

Background: Desktop Agent API
Given A Desktop Agent in "api1"
Given "intentMessageOne" is a "intentRequest" message with intent "BuyStock" and context "fdc3.instrument"

Scenario:

Given "resultHandler" pipes intent to "intents"
When I call "api1" with "addIntentListener" with parameters "BuyStock" and "{resultHandler}"
And messaging receives "{intentMessageOne}"
Then "{intents}" is an array of objects with the following contents
| id.ticker | type | name |
8 changes: 4 additions & 4 deletions packages/da/test/step-definitions/generic.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SimpleIntentResolver implements IntentResolver {

cw: CustomWorld

resolveIntent(appIntents: AppIntent[]): SingleAppIntent {
chooseIntent(appIntents: AppIntent[]): SingleAppIntent {
const out = {
intent: appIntents[0].intent,
chosenApp :appIntents[0].apps[0]
Expand Down Expand Up @@ -111,11 +111,11 @@ Then('{string} is an object with the following contents', function (this: Custom
});

Then('{string} is null', function (this: CustomWorld, field: string) {
expect(this.props[field]).toBeNull()
expect(handleResolve(field, this)).toBeNull()
})

Then('{string} is empty', function (this: CustomWorld, field: string) {
expect(this.props[field]).toHaveLength(0)
expect(handleResolve(field, this)).toHaveLength(0)
})

Then('{string} is {string}', function (this: CustomWorld, field: string, expected: string) {
Expand All @@ -125,7 +125,7 @@ Then('{string} is {string}', function (this: CustomWorld, field: string, expecte
})

Then('{string} is an error with message {string}', function (this: CustomWorld, field: string, errorType: string) {
expect(this.props[field]['message']).toBe(errorType)
expect(handleResolve(field, this)['message']).toBe(errorType)
})

Given('{string} is a invocation counter into {string}', function(this: CustomWorld, handlerName: string, field: string) {
Expand Down
60 changes: 59 additions & 1 deletion packages/da/test/step-definitions/intents.steps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { Given } from '@cucumber/cucumber'
import { CustomWorld } from '../world/index';
import { handleResolve } from '../support/matching';

Given("app {string} resolves intent {string}", function (this: CustomWorld, appStr: string, intent: string) {
const [ appId, instanceId ] = appStr.split("/")
const app = { appId, instanceId }
this.messaging?.addAppIntentDetail({
app,
intent,
})
this.props[instanceId] = app
})

Given("app {string} resolves intent {string} with result type {string}", function (this: CustomWorld, appStr: string, intent: string, resultType: string) {
const [ appId, instanceId ] = appStr.split("/")
Expand Down Expand Up @@ -33,4 +44,51 @@ Given("app {string} resolves intent {string} with context {string} and result ty
resultType
})
this.props[instanceId] = app
})
})

Given("Raise Intent will return a context of {string}", function (this: CustomWorld, result: string) {
this.messaging?.setIntentResult({
context: handleResolve(result, this)
})
})

Given("Raise Intent will return an app channel", function (this: CustomWorld) {
this.messaging?.setIntentResult({
channel: {
type: 'app',
id: 'result-channel',
displayMetadata: {
color: "purple",
name: "Result Channel"
}
}
})
})

Given('Raise Intent will return a user channel', function (this: CustomWorld) {
this.messaging?.setIntentResult({
channel: {
type: 'user',
id: 'result-channel',
displayMetadata: {
color: "purple",
name: "Result Channel"
}
}
})
})

Given("Raise Intent will return a private channel", function (this: CustomWorld) {
this.messaging?.setIntentResult({
channel: {
type: 'private',
id: 'result-channel',
displayMetadata: {
color: "purple",
name: "Result Channel"
}
}
})
})

Given('{string} is a {string} message with intent {string} and context {string}', function (string, string2, string3, string4) {
29 changes: 23 additions & 6 deletions packages/da/test/support/TestMessaging.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AppIdentifier } from "@finos/fdc3";
import { AgentRequestMessage, AgentResponseMessage } from "@finos/fdc3/dist/bridging/BridgingTypes";
import { AgentRequestMessage, AgentResponseMessage, IntentResult } from "@finos/fdc3/dist/bridging/BridgingTypes";
import { v4 as uuidv4 } from 'uuid'
import { AbstractMessaging } from "../../src/messaging/AbstractMessaging";
import { RegisterableListener } from "../listeners/RegisterableListener";
import { FindIntent } from "./responses/FindIntent";
import { FIndIntentByContext } from "./responses/FindIntentByContext";
import { FindIntentByContext } from "./responses/FindIntentByContext";
import { ICreateLog } from "@cucumber/cucumber/lib/runtime/attachment_manager";
import { RaiseIntent } from "./responses/RaiseIntent";

Expand All @@ -23,13 +23,17 @@ export interface AutomaticResponse {
}

function matchStringOrUndefined(expected: string | undefined, actual: string | undefined) {
if (expected) {
if ((expected) && (actual)) {
return expected == actual
} else {
return true
}
}

function matchString(expected: string | undefined, actual: string | undefined) {
return expected == actual
}

function removeGenericType(t: string) {
const startOfGeneric = t.indexOf("<")
if (startOfGeneric > -1) {
Expand Down Expand Up @@ -57,11 +61,11 @@ function matchResultTypes(expected: string | undefined, actual: string | undefin
}
}

export function intentDetailMatches(instance: IntentDetail, template: IntentDetail) : boolean {
export function intentDetailMatches(instance: IntentDetail, template: IntentDetail, contextMustMatch: boolean) : boolean {
return matchStringOrUndefined(template.app?.appId, instance.app?.appId) &&
matchStringOrUndefined(template.app?.instanceId, instance.app?.instanceId) &&
matchStringOrUndefined(template.intent, instance.intent) &&
matchStringOrUndefined(template.context, instance.context) &&
(contextMustMatch ? matchString(template.context, instance.context) : matchStringOrUndefined(template.context, instance.context)) &&
matchResultTypes(template.resultType, instance.resultType)
}

Expand All @@ -70,9 +74,10 @@ export class TestMessaging extends AbstractMessaging {
readonly allPosts : AgentRequestMessage[] = []
readonly listeners : Map<string, RegisterableListener> = new Map()
readonly intentDetails : IntentDetail[] = []

readonly automaticResponses : AutomaticResponse[] = [
new FindIntent(),
new FIndIntentByContext(),
new FindIntentByContext(),
new RaiseIntent()
]

Expand Down Expand Up @@ -132,4 +137,16 @@ export class TestMessaging extends AbstractMessaging {
}
})
}

private ir: IntentResult = {

}

getIntentResult() {
return this.ir
}

setIntentResult(o : IntentResult) {
this.ir = o
}
}
2 changes: 1 addition & 1 deletion packages/da/test/support/responses/FindIntent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class FindIntent implements AutomaticResponse {
resultType
}

const relevant = m.intentDetails.filter(id => intentDetailMatches(id, template))
const relevant = m.intentDetails.filter(id => intentDetailMatches(id, template, false))
const request = this.createFindIntentResponseMessage(intentRequest, relevant)
setTimeout(() => { m.receive(request) },100)
return Promise.resolve()
Expand Down
4 changes: 2 additions & 2 deletions packages/da/test/support/responses/FindIntentByContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AgentRequestMessage, FindIntentsByContextAgentRequest, FindIntentsByCon
import { AutomaticResponse, IntentDetail, TestMessaging, intentDetailMatches } from "../TestMessaging";


export class FIndIntentByContext implements AutomaticResponse {
export class FindIntentByContext implements AutomaticResponse {

filter(t: string) {
return t == 'findIntentsByContextRequest'
Expand All @@ -16,7 +16,7 @@ export class FIndIntentByContext implements AutomaticResponse {
context
}

const relevant = m.intentDetails.filter(id => intentDetailMatches(id, template))
const relevant = m.intentDetails.filter(id => intentDetailMatches(id, template, true))
const request = this.createFindIntentsByContextResponseMessage(intentRequest, relevant)
setTimeout(() => { m.receive(request) }, 100)
return Promise.resolve()
Expand Down
35 changes: 5 additions & 30 deletions packages/da/test/support/responses/RaiseIntent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AgentRequestMessage, IntentResult, RaiseIntentAgentRequest, RaiseIntentAgentResponse, RaiseIntentResultAgentResponse } from "@finos/fdc3/dist/bridging/BridgingTypes";
import { AgentRequestMessage, RaiseIntentAgentRequest, RaiseIntentAgentResponse, RaiseIntentResultAgentResponse } from "@finos/fdc3/dist/bridging/BridgingTypes";
import { AutomaticResponse, IntentDetail, TestMessaging, intentDetailMatches } from "../TestMessaging";


Expand Down Expand Up @@ -26,39 +26,14 @@ export class RaiseIntent implements AutomaticResponse {
return out
}

createRaiseIntentResultResponseMesssage(intentRequest: RaiseIntentAgentRequest, using: IntentDetail, m: TestMessaging) : RaiseIntentResultAgentResponse {
var intentResult : IntentResult = {}

// here, we're just providing a few canned responses required for the tests
switch (using.resultType) {
case "channel":
intentResult.channel = {
type: 'app',
id: 'result-channel',
displayMetadata: {
color: "purple",
name: "Result Channel"
}
}
break;
case "fdc3.order":
intentResult.context = {
type: 'fdc3.order',
id: {
myOMS: "OMS-9",
},
name: "Big Order 9"
}
break;
}

createRaiseIntentResultResponseMesssage(intentRequest: RaiseIntentAgentRequest, m: TestMessaging) : RaiseIntentResultAgentResponse {
const out: RaiseIntentResultAgentResponse = {
meta: {
...intentRequest.meta,
responseUuid: m.createUUID()
},
payload: {
intentResult
intentResult:m.getIntentResult()
},
type: "raiseIntentResultResponse"
}
Expand All @@ -76,15 +51,15 @@ export class RaiseIntent implements AutomaticResponse {
context
}

const relevant = m.intentDetails.filter(id => intentDetailMatches(id, template))
const relevant = m.intentDetails.filter(id => intentDetailMatches(id, template, false))
const using = relevant[0]

// this sends out the intent resolution
const out1 = this.createRaiseIntentAgentResponseMessage(intentRequest, using, m)
setTimeout(() => { m.receive(out1) }, 100)

// next, send the result response
const out2 = this.createRaiseIntentResultResponseMesssage(intentRequest, using, m)
const out2 = this.createRaiseIntentResultResponseMesssage(intentRequest, m)
setTimeout(() => { m.receive(out2) }, 300)
return Promise.resolve()
}
Expand Down
6 changes: 3 additions & 3 deletions packages/da/test/unused-features/app-channels.feature
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Feature: Basic User Channels Support
And I call "channel1" with "addContextListener" with parameters "fdc3.instrument" and "{resultHandler}"
And I call "channel1" with "addContextListener" with parameters "fdc3.instrument" and "{resultHandler}"
And messaging receives "{instrumentMessageOne}"
Then "contexts" is an array of objects with the following contents
Then "{contexts}" is an array of objects with the following contents
| id.ticker | type | name |
| AAPL | fdc3.instrument | Apple |
| AAPL | fdc3.instrument | Apple |
Expand All @@ -39,7 +39,7 @@ Feature: Basic User Channels Support
And I call "channel1" with "addContextListener" with parameter "{resultHandler}"
And messaging receives "{instrumentMessageOne}"
And messaging receives "{countryMessageOne}"
Then "contexts" is an array of objects with the following contents
Then "{contexts}" is an array of objects with the following contents
| type | name |
| fdc3.instrument | Apple |
| fdc3.country | Sweden |
Expand All @@ -54,7 +54,7 @@ Feature: Basic User Channels Support
And I call "channel1" with "addContextListener" with parameters "{null}" and "{resultHandler}"
And messaging receives "{instrumentMessageOne}"
And messaging receives "{countryMessageOne}"
Then "contexts" is an array of objects with the following contents
Then "{contexts}" is an array of objects with the following contents
| type | name |
| fdc3.instrument | Apple |
| fdc3.country | Sweden |
Loading

0 comments on commit 72daffc

Please sign in to comment.