Skip to content
This repository has been archived by the owner on Dec 2, 2020. It is now read-only.

Commit

Permalink
mocks
Browse files Browse the repository at this point in the history
  • Loading branch information
kengoldfarb committed May 22, 2020
1 parent b06cdae commit f7c2f23
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 5 deletions.
14 changes: 10 additions & 4 deletions src/Mercury.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import BaseTest, { test, assert } from '@sprucelabs/test'
import MercuryMock from './MercuryMock'
import { IMercuryEventContract } from './types/mercuryEvents'
import { MercurySubscriptionScope } from './types/subscriptions'
// import { MercurySubscriptionScope } from './types/subscriptions'

export const SpruceEvents = {
core: {
Expand Down Expand Up @@ -59,13 +60,15 @@ export default class MercuryTest extends BaseTest {
// console.log(body)
// }
// )
this.mercury.setEmitResponse({
this.mercury.setMockResponse({
eventName: SpruceEvents.core.DidEnter,
payload: {
somethingElse: 'blah'
}
})

let numCallbacks = 0

this.mercury.on(
{
// eventName: 'did-enter',
Expand All @@ -75,17 +78,20 @@ export default class MercuryTest extends BaseTest {
},
options => {
console.log(options.payload.somethingElse)
numCallbacks += 1
}
)

const result = await this.mercury.emit({
eventName: 'did-leave',
eventName: 'did-enter',
payload: {
somethingElse: ''
userId: 'asdf',
enteredAt: 'asdf'
}
})

assert.isOk(result.responses[0].payload.userId)
assert.isOk(result.responses[0].payload.somethingElse)
assert.equal(numCallbacks, 1)

// mercury.emit(SpruceEvents.core.didEnter)

Expand Down
1 change: 0 additions & 1 deletion src/Mercury.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ export default class Mercury<EventContract extends IMercuryEventContract> {
options: IMercuryEmitOptions<EventContract, EventName, EventSpace>,
handler?: OnHandler<EventContract, EventName, EventSpace>
): Promise<{
// TODO: Better typing on this response
responses: IOnData<EventContract, EventName, EventSpace>[]
}> {
await this.awaitConnection()
Expand Down
194 changes: 194 additions & 0 deletions src/MercuryMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { faker } from '@sprucelabs/test'
import MercuryAdapterMock from './adapters/MercuryAdapterMock'
import log from './lib/log'
import Mercury, { MercuryAdapterKind } from './Mercury'
import {
IMercuryEventContract,
IOnData,
ISkillOnData,
IMercuryEmitOptions,
OnHandler
} from './types/mercuryEvents'
import { MercurySubscriptionScope } from './types/subscriptions'

interface IMockEmitResponses {
[eventName: string]: {
[skillId: string]: Omit<IOnData<any, any, any>, 'eventId'>
}
}

export default class MercuryMock<
EventContract extends IMercuryEventContract
> extends Mercury<EventContract> {
private mockEmitResponses: IMockEmitResponses = {}

/** Emit an event and set handler for responses */
public async emit<
EventName extends keyof EventContract,
EventSpace extends EventContract[EventName]
>(
options: IMercuryEmitOptions<EventContract, EventName, EventSpace>,
handler?: OnHandler<EventContract, EventName, EventSpace>
): Promise<{
responses: IOnData<EventContract, EventName, EventSpace>[]
}> {
const { eventName } = options

await this.awaitConnection()

if (!this.adapter) {
log.warn('Mercury: Unable to emit because adapter is not set.')
// @ts-ignore
return
}

const eventId = options.eventId ?? this.uuid()
if (!this.eventHandlers[eventId]) {
this.eventHandlers[eventId] = {
onFinished: [],
onError: [],
onResponse: []
}
}
if (handler) {
this.eventHandlers[eventId].onResponse = [handler]
}
this.adapter.emit({
...options,
eventId,
credentials: this.credentials
})

process.nextTick(async () => {
try {
const responses = this.getMockResponsesForEvent({
eventName,
eventId: eventId ?? this.uuid()
})

if (responses.length > 0) {
const promises = responses.map(r => {
return this.handleEvent({
...r,
// TODO: set / control subscription scope
scope: MercurySubscriptionScope.Location,
eventId,
responses
})
})

// Execute individual event handlers for each
await Promise.all(promises)

// Execute the final callback
await this.handleEvent({
eventName,
// TODO: set / control subscription scope
scope: MercurySubscriptionScope.Location,
eventId,
organizationId: responses[0].organizationId,
locationId: responses[0].locationId,
userId: responses[0].userId,
payload: responses[0].payload,
responses
})
}
} catch (e) {
log.warn(e)
}
})

const finishedHandler = this.emitOnFinishedCallback(eventId)

return finishedHandler
}

/** Sets the response to an emit */
public setMockResponse<
EventName extends keyof EventContract,
ResponsePayload extends EventContract[EventName]['responsePayload']
>(options: {
eventName: EventName
payload: ResponsePayload
skill?: ISkillOnData
}) {
const { eventName, payload } = options
const eventNameStr = eventName as string

if (!this.mockEmitResponses[eventNameStr]) {
this.mockEmitResponses[eventNameStr] = {}
}

const skillData = options.skill ?? {
id: this.uuid(),
name: faker.name.title(),
slug: faker.name.title()
}

this.mockEmitResponses[eventNameStr][skillData.id] = {
eventName,
skill: skillData,
payload
}
}

/** Clear the mock data for an event */
public clearMockResponse<
EventName extends keyof EventContract,
ResponsePayload extends EventContract[EventName]['responsePayload']
>(options: { eventName?: EventName }) {
const { eventName } = options
const eventNameStr = eventName as string
delete this.mockEmitResponses[eventNameStr]
}

/** Clear all mock data */
public clearMocks() {
this.mockEmitResponses = {}
}

protected setAdapter(options: {
adapter: MercuryAdapterKind
connectionOptions: Record<string, any>
}): boolean {
const { connectionOptions } = options
log.debug('setAdapter', { options })

if (this.adapter) {
this.adapter.disconnect()
this.adapter = undefined
}

this.adapter = new MercuryAdapterMock()
this.adapter.init(
connectionOptions,
this.handleEvent.bind(this),
this.handleError.bind(this),
this.onConnect.bind(this),
this.onDisconnect.bind(this)
)

return true
}

private getMockResponsesForEvent<
EventName extends keyof EventContract,
EventSpace extends EventContract[EventName]
>(options: {
eventName: EventName
eventId: string
}): IOnData<EventContract, EventName, EventSpace>[] {
const { eventId, eventName } = options
let responses: IOnData<EventContract, EventName, EventSpace>[] = []

const mockResponses = this.mockEmitResponses[eventName as string]
if (mockResponses) {
responses = Object.values(mockResponses).map(r => ({
...r,
eventId
}))
}

return responses
}
}
9 changes: 9 additions & 0 deletions src/types/mercuryEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ export interface IOnData<
/** The event name that is being triggered */
eventName: EventName

/** The organization id where the event is triggered */
organizationId?: string | null

/** The location id where the event is triggered. If passed, organizationId should also be set. */
locationId?: string | null

/** The user id who is triggering this event */
userId?: string | null

/** The unique id for this event */
eventId: string

Expand Down
3 changes: 3 additions & 0 deletions tests/BaseMercuryTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import BaseTest from '@sprucelabs/test'

export default class BaseMercuryTest extends BaseTest {}

0 comments on commit f7c2f23

Please sign in to comment.