From ef290302472bd66ff9823aad1a4e029a4b4e2eba Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Thu, 28 Sep 2023 14:13:09 -0300 Subject: test: templateCompositeFrom (WIP), various composite test updates --- test/unit/data/composite/exposeConstant.js | 38 ++-- test/unit/data/composite/exposeDependency.js | 38 ++-- test/unit/data/composite/templateCompositeFrom.js | 218 +++++++++++++++++++++ .../composite/withResultOfAvailabilityCheck.js | 81 +++++++- 4 files changed, 323 insertions(+), 52 deletions(-) create mode 100644 test/unit/data/composite/templateCompositeFrom.js (limited to 'test/unit') diff --git a/test/unit/data/composite/exposeConstant.js b/test/unit/data/composite/exposeConstant.js index ce3f5e3d..829dc706 100644 --- a/test/unit/data/composite/exposeConstant.js +++ b/test/unit/data/composite/exposeConstant.js @@ -32,29 +32,21 @@ t.test(`exposeConstant: basic behavior`, t => { t.test(`exposeConstant: validate inputs`, t => { t.plan(2); - let caughtError; - - try { - caughtError = null; - exposeConstant({}); - } catch (error) { - caughtError = error; - } - - t.match(caughtError, { - errors: [/Required these inputs: value/], - }); - - try { - caughtError = null; - exposeConstant({ - value: 'some dependency', + t.throws( + () => exposeConstant({}), + { + message: `Errors in input options passed to exposeConstant`, + errors: [ + {message: `Required these inputs: value`}, + ], }); - } catch (error) { - caughtError = error; - } - t.match(caughtError, { - errors: [/Expected static values: value/], - }); + t.throws( + () => exposeConstant({value: 'some dependency'}), + { + message: `Errors in input options passed to exposeConstant`, + errors: [ + {message: `value: Expected input.value() call, got dependency name`}, + ], + }); }); diff --git a/test/unit/data/composite/exposeDependency.js b/test/unit/data/composite/exposeDependency.js index 7487e44c..78801343 100644 --- a/test/unit/data/composite/exposeDependency.js +++ b/test/unit/data/composite/exposeDependency.js @@ -52,29 +52,23 @@ t.test(`exposeDependency: basic behavior`, t => { t.test(`exposeDependency: validate inputs`, t => { t.plan(2); - let caughtError; - - try { - caughtError = null; - exposeDependency({}); - } catch (error) { - caughtError = error; - } - - t.match(caughtError, { - errors: [/Required these inputs: dependency/], - }); + t.throws( + () => exposeDependency({}), + { + message: `Errors in input options passed to exposeDependency`, + errors: [ + {message: `Required these inputs: dependency`}, + ], + }); - try { - caughtError = null; - exposeDependency({ + t.throws( + () => exposeDependency({ dependency: input.value('some static value'), + }), + { + message: `Errors in input options passed to exposeDependency`, + errors: [ + {message: `dependency: Expected dependency name, got input.value() call`}, + ], }); - } catch (error) { - caughtError = error; - } - - t.match(caughtError, { - errors: [/Expected static dependencies: dependency/], - }); }); diff --git a/test/unit/data/composite/templateCompositeFrom.js b/test/unit/data/composite/templateCompositeFrom.js new file mode 100644 index 00000000..e96b782e --- /dev/null +++ b/test/unit/data/composite/templateCompositeFrom.js @@ -0,0 +1,218 @@ +import t from 'tap'; + +import {isString} from '#validators'; + +import { + compositeFrom, + continuationSymbol, + input, + templateCompositeFrom, +} from '#composite'; + +t.test(`templateCompositeFrom: basic behavior`, t => { + t.plan(1); + + const myCoolUtility = templateCompositeFrom({ + annotation: `myCoolUtility`, + + inputs: { + foo: input(), + }, + + outputs: ['#bar'], + + steps: () => [ + { + dependencies: [input('foo')], + compute: (continuation, { + [input('foo')]: foo, + }) => continuation({ + ['#bar']: (typeof foo).toUpperCase() + }), + }, + ], + }); + + const instantiatedTemplate = myCoolUtility({ + foo: 'color', + }); + + t.match(instantiatedTemplate.toDescription(), { + annotation: `myCoolUtility`, + + inputMapping: { + foo: input.dependency('color'), + }, + + inputDescriptions: { + foo: input(), + }, + + outputs: { + '#bar': '#bar', + }, + + steps: Function, + }); +}); + +t.test(`templateCompositeFrom: validate static input values`, t => { + t.plan(3); + + const stub = { + annotation: 'stubComposite', + outputs: ['#result'], + steps: () => [{compute: continuation => continuation({'#result': 'OK'})}], + }; + + const quickThrows = (t, composite, inputOptions, ...errorMessages) => + t.throws( + () => composite(inputOptions), + { + message: `Errors in input options passed to stubComposite`, + errors: errorMessages.map(message => ({message})), + }); + + t.test(`templateCompositeFrom: validate input token shapes`, t => { + t.plan(15); + + const template1 = templateCompositeFrom({ + ...stub, inputs: { + foo: input(), + }, + }); + + t.doesNotThrow( + () => template1({foo: 'dependency'})); + + t.doesNotThrow( + () => template1({foo: input.dependency('dependency')})); + + t.doesNotThrow( + () => template1({foo: input.value('static value')})); + + t.doesNotThrow( + () => template1({foo: input('outerInput')})); + + t.doesNotThrow( + () => template1({foo: input.updateValue()})); + + t.doesNotThrow( + () => template1({foo: input.myself()})); + + quickThrows(t, template1, + {foo: input.staticValue()}, + `foo: Expected dependency name or value-providing input() call, got input.staticValue`); + + quickThrows(t, template1, + {foo: input.staticDependency()}, + `foo: Expected dependency name or value-providing input() call, got input.staticDependency`); + + const template2 = templateCompositeFrom({ + ...stub, inputs: { + bar: input.staticDependency(), + }, + }); + + t.doesNotThrow( + () => template2({bar: 'dependency'})); + + t.doesNotThrow( + () => template2({bar: input.dependency('dependency')})); + + quickThrows(t, template2, + {bar: input.value(123)}, + `bar: Expected dependency name, got input.value`); + + quickThrows(t, template2, + {bar: input('outOfPlace')}, + `bar: Expected dependency name, got input`); + + const template3 = templateCompositeFrom({ + ...stub, inputs: { + baz: input.staticValue(), + }, + }); + + t.doesNotThrow( + () => template3({baz: input.value(1025)})); + + quickThrows(t, template3, + {baz: 'dependency'}, + `baz: Expected input.value() call, got dependency name`); + + quickThrows(t, template3, + {baz: input('outOfPlace')}, + `baz: Expected input.value() call, got input() call`); + }); + + t.test(`templateCompositeFrom: validate missing / misplaced inputs`, t => { + t.plan(1); + + const template = templateCompositeFrom({ + ...stub, inputs: { + foo: input(), + bar: input(), + }, + }); + + t.throws( + () => template({ + baz: 'aeiou', + raz: input.value(123), + }), + { + message: `Errors in input options passed to stubComposite`, + errors: [ + {message: `Unexpected input names: baz, raz`}, + {message: `Required these inputs: foo, bar`}, + ], + }); + }); + + t.test(`templateCompositeFrom: validate acceptsNull / defaultValue: null`, t => { + t.plan(3); + + const template1 = templateCompositeFrom({ + ...stub, inputs: { + foo: input(), + }, + }); + + t.throws( + () => template1({}), + { + message: `Errors in input options passed to stubComposite`, + errors: [ + {message: `Required these inputs: foo`}, + ], + }, + `throws if input missing and not marked specially`); + + const template2 = templateCompositeFrom({ + ...stub, inputs: { + bar: input({acceptsNull: true}), + }, + }); + + t.throws( + () => template2({}), + { + message: `Errors in input options passed to stubComposite`, + errors: [ + {message: `Required these inputs: bar`}, + ], + }, + `throws if input missing even if marked {acceptsNull}`); + + const template3 = templateCompositeFrom({ + ...stub, inputs: { + baz: input({defaultValue: null}), + }, + }); + + t.doesNotThrow( + () => template3({}), + `does not throw if input missing if marked {defaultValue: null}`); + }); +}); diff --git a/test/unit/data/composite/withResultOfAvailabilityCheck.js b/test/unit/data/composite/withResultOfAvailabilityCheck.js index cac98d3c..01220a3a 100644 --- a/test/unit/data/composite/withResultOfAvailabilityCheck.js +++ b/test/unit/data/composite/withResultOfAvailabilityCheck.js @@ -110,8 +110,8 @@ t.test(`withResultOfAvailabilityCheck: default mode`, t => { }); }); -t.test(`withResultOfAvailabilityCheck: validate inputs`, t => { - t.plan(4); +t.test(`withResultOfAvailabilityCheck: validate static inputs`, t => { + t.plan(5); let caughtError; @@ -134,7 +134,7 @@ t.test(`withResultOfAvailabilityCheck: validate inputs`, t => { t.doesNotThrow(() => withResultOfAvailabilityCheck({ - from: input.value('static'), + from: input.value('some static value'), mode: input.value('null'), })); @@ -149,11 +149,78 @@ t.test(`withResultOfAvailabilityCheck: validate inputs`, t => { } t.match(caughtError, { + message: /Errors in input options passed to withResultOfAvailabilityCheck/, errors: [ - { - message: /mode: Validation failed for static value/, - cause: /Expected one of null empty falsy, got invalid/, - }, + /mode: Expected one of null empty falsy, got invalid/, ], }); + + try { + caughtError = null; + withResultOfAvailabilityCheck({ + from: input.value(null), + mode: input.value(null), + }); + } catch (error) { + caughtError = error; + } + + t.match(caughtError, { + message: /Errors in input options passed to withResultOfAvailabilityCheck/, + errors: [ + /mode: Expected value, got null/, + ], + }); +}); + +t.test(`withResultOfAvailabilityCheck: validate dynamic inputs`, t => { + t.plan(2); + + let caughtError; + + try { + caughtError = null; + composite.expose.compute({ + from: 'apple', + mode: 'banana', + }); + } catch (error) { + caughtError = error; + } + + t.match(caughtError, { + message: /Error computing composition/, + cause: { + message: /Error computing composition withResultOfAvailabilityCheck/, + cause: { + message: /Errors in input values provided to withResultOfAvailabilityCheck/, + errors: [ + /mode: Expected one of null empty falsy, got banana/, + ], + }, + }, + }); + + try { + caughtError = null; + composite.expose.compute({ + from: null, + mode: null, + }); + } catch (error) { + caughtError = error; + } + + t.match(caughtError, { + message: /Error computing composition/, + cause: { + message: /Error computing composition withResultOfAvailabilityCheck/, + cause: { + message: /Errors in input values provided to withResultOfAvailabilityCheck/, + errors: [ + /mode: Expected value, got null/, + ], + }, + }, + }); }); -- cgit 1.3.0-6-gf8a5