Skip to content

Commit

Permalink
fix(source): bug in utils.Source preventing chained ACs from the source
Browse files Browse the repository at this point in the history
affects: @tao.js/socket.io, @tao.js/utils

a chained AC after receiving an AC from the Source into the wrapped
signal network was not being forwarded on and dropped

ISSUES CLOSED: #29
  • Loading branch information
eudaimos committed Mar 3, 2021
1 parent 1b9002b commit 09659f9
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 14 deletions.
33 changes: 19 additions & 14 deletions packages/tao-utils/src/Source.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ function sourceControl(source) {

export default class Source {
constructor(kernel, toSrc, name, fromSrc) {
this._network = kernel._network;
this.forwardAppCtx = kernel.forwardAppCtx;
if (!kernel || !kernel._network) {
throw new Error(
'must provide `kernel` to attach the Source to a network'
);
}
this._network = kernel._network;
this.forwardAppCtx = (ac, control) => {
kernel.forwardAppCtx(ac, control);
};
if (!toSrc) {
throw new Error('must provide `toSrc` way to send ACs to the source');
}
Expand All @@ -27,15 +29,19 @@ export default class Source {
}
// Make fromSrc optional for binding a handler
// if not passed it is a function exposed by the Source i.e. setCtx
fromSrc((tao, data) =>
this._network.setCtxControl(
tao,
data,
sourceControl(this.name),
// (ac, control) => kernel.forwardAppCtx(ac, control)
this.forwardAppCtx
)
);
if (fromSrc) {
if (typeof fromSrc !== 'function') {
throw new Error('optional `fromSrc` must be a function');
}
fromSrc((tao, data) =>
this._network.setCtxControl(
tao,
data,
sourceControl(this.name),
this.forwardAppCtx
)
);
}
this._toSrc = toSrc;
this._name = sourceName(name);
this._middleware = (handler, ac, fwd, control) =>
Expand All @@ -47,18 +53,17 @@ export default class Source {
return this._name;
}

handleAppCon(handler, ac, forwardAppCtx, control) {
handleAppCon = (handler, ac, forwardAppCtx, control) => {
if (!control || control.source !== this.name) {
this._toSrc(ac.unwrapCtx(), ac.data);
}
}
};

setCtx = ({ t, term, a, action, o, orient }, data) => {
this._network.setCtxControl(
{ t, term, a, action, o, orient },
data,
sourceControl(this.name),
// (ac, control) => kernel.forwardAppCtx(ac, control)
this.forwardAppCtx
);
};
Expand Down
118 changes: 118 additions & 0 deletions packages/tao-utils/test/Source.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { AppCtx, Kernel } from '@tao.js/core';
import Source from '../src/Source';

const TERM = 'source';
const ACTION = 'test';
const ORIENT = 'jest';

const ALT_TERM = 'source_alt';
const ALT_ACTION = 'test_alt';
const ALT_ORIENT = 'jest_alt';

let TAO = null;
function initTAO() {
TAO = new Kernel();
}
function clearTAO() {
TAO = null;
}

beforeEach(initTAO);
afterEach(clearTAO);

describe('Source exports a class', () => {
it('should provide a constructor that takes a TAO as argument', () => {
// Assemble
// Act
// Assert
expect(Source).toBeDefined();
expect(new Source(TAO, () => {}, () => {})).toBeInstanceOf(Source);
});

it('should throw if a Signal Network is not provided to the constructor', () => {
// Assemble
// Act
// Assert
expect(() => {
new Source();
}).toThrow(/must provide `kernel`/);
});

it('should throw if a function to send signals to the Source', () => {
// Assemble
// Act
// Assert
expect(() => {
new Source(TAO);
}).toThrow(/must provide `toSrc`/);
});

describe('provides ability to source ACs to a network', () => {
it('should send ACs from the network to the Source', () => {
// Assemble
const toSrc = jest.fn((tao, data) => {
tao, data;
});
const source = new Source(TAO, toSrc);
const trigram = { t: TERM, a: ACTION, o: ORIENT };
const data = undefined;
// Act
TAO.setCtx(trigram, data);
// Assert
expect(toSrc).toHaveBeenCalledTimes(1);
expect(toSrc).toHaveBeenCalledWith(expect.objectContaining(trigram), {});
});

it('should forward an AC to the attached network', () => {
// Assemble
const source = new Source(TAO, jest.fn());
const trigram = { t: TERM, a: ACTION, o: ORIENT };
const data = undefined;
const handler = jest.fn();
// Act
TAO.addInlineHandler(trigram, handler);
source.setCtx(trigram, data);
// Assert
expect(handler).toHaveBeenCalledTimes(1);
expect(handler).toHaveBeenCalledWith(
expect.objectContaining(trigram),
{}
);
});

it('should allow an AC chain to propagate in the network', async () => {
// Assemble
const source = new Source(TAO, jest.fn());
const trigram = { t: TERM, a: ACTION, o: ORIENT };
const followup = new AppCtx(ALT_TERM, ALT_ACTION, ALT_ORIENT);
const data = undefined;
const handler = jest.fn((tao, data) => {
// console.log({ handler: { tao, data } });
return followup;
});
let followHandler = null;
const followupPromise = new Promise((resolve, reject) => {
followHandler = jest.fn((tao, data) => {
// console.log({ followup: { tao, data } });
resolve(tao);
});
});
// Act
TAO.addInlineHandler(trigram, handler);
TAO.addInlineHandler(followup.unwrapCtx(), followHandler);
source.setCtx(trigram, data);
// Assert
await followupPromise;
expect(handler).toHaveBeenCalledTimes(1);
expect(handler).toHaveBeenCalledWith(
expect.objectContaining(trigram),
{}
);
expect(followHandler).toHaveBeenCalledTimes(1);
expect(followHandler).toHaveBeenCalledWith(
expect.objectContaining(followup.unwrapCtx()),
{}
);
});
});
});

0 comments on commit 09659f9

Please sign in to comment.