« get me outta code hell

test: templateCompositeFrom (WIP), various composite test updates - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2023-09-28 14:13:09 -0300
committer(quasar) nebula <qznebula@protonmail.com>2023-09-28 14:13:09 -0300
commitef290302472bd66ff9823aad1a4e029a4b4e2eba (patch)
treec45656c75fc156da8b5df2f95ef04f54cff84c39
parentf7376bb5eb2671de7242872ec0c4615b5e244aba (diff)
test: templateCompositeFrom (WIP), various composite test updates
-rw-r--r--test/unit/data/composite/exposeConstant.js38
-rw-r--r--test/unit/data/composite/exposeDependency.js38
-rw-r--r--test/unit/data/composite/templateCompositeFrom.js218
-rw-r--r--test/unit/data/composite/withResultOfAvailabilityCheck.js81
4 files changed, 323 insertions, 52 deletions
diff --git a/test/unit/data/composite/exposeConstant.js b/test/unit/data/composite/exposeConstant.js
index ce3f5e3..829dc70 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 7487e44..7880134 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 0000000..e96b782
--- /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 cac98d3..01220a3 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/,
+        ],
+      },
+    },
+  });
 });