templateCompositeFrom.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import t from 'tap';
  2. import {isString} from '#validators';
  3. import {
  4. compositeFrom,
  5. continuationSymbol,
  6. input,
  7. templateCompositeFrom,
  8. } from '#composite';
  9. t.test(`templateCompositeFrom: basic behavior`, t => {
  10. t.plan(1);
  11. const myCoolUtility = templateCompositeFrom({
  12. annotation: `myCoolUtility`,
  13. inputs: {
  14. foo: input(),
  15. },
  16. outputs: ['#bar'],
  17. steps: () => [
  18. {
  19. dependencies: [input('foo')],
  20. compute: (continuation, {
  21. [input('foo')]: foo,
  22. }) => continuation({
  23. ['#bar']: (typeof foo).toUpperCase()
  24. }),
  25. },
  26. ],
  27. });
  28. const instantiatedTemplate = myCoolUtility({
  29. foo: 'color',
  30. });
  31. t.match(instantiatedTemplate.toDescription(), {
  32. annotation: `myCoolUtility`,
  33. inputMapping: {
  34. foo: input.dependency('color'),
  35. },
  36. inputDescriptions: {
  37. foo: input(),
  38. },
  39. outputs: {
  40. '#bar': '#bar',
  41. },
  42. steps: Function,
  43. });
  44. });
  45. t.test(`templateCompositeFrom: validate static input values`, t => {
  46. t.plan(3);
  47. const stub = {
  48. annotation: 'stubComposite',
  49. outputs: ['#result'],
  50. steps: () => [{compute: continuation => continuation({'#result': 'OK'})}],
  51. };
  52. const quickThrows = (t, composite, inputOptions, ...errorMessages) =>
  53. t.throws(
  54. () => composite(inputOptions),
  55. {
  56. message: `Errors in input options passed to stubComposite`,
  57. errors: errorMessages.map(message => ({message})),
  58. });
  59. t.test(`templateCompositeFrom: validate input token shapes`, t => {
  60. t.plan(15);
  61. const template1 = templateCompositeFrom({
  62. ...stub, inputs: {
  63. foo: input(),
  64. },
  65. });
  66. t.doesNotThrow(
  67. () => template1({foo: 'dependency'}));
  68. t.doesNotThrow(
  69. () => template1({foo: input.dependency('dependency')}));
  70. t.doesNotThrow(
  71. () => template1({foo: input.value('static value')}));
  72. t.doesNotThrow(
  73. () => template1({foo: input('outerInput')}));
  74. t.doesNotThrow(
  75. () => template1({foo: input.updateValue()}));
  76. t.doesNotThrow(
  77. () => template1({foo: input.myself()}));
  78. quickThrows(t, template1,
  79. {foo: input.staticValue()},
  80. `foo: Expected dependency name or value-providing input() call, got input.staticValue`);
  81. quickThrows(t, template1,
  82. {foo: input.staticDependency()},
  83. `foo: Expected dependency name or value-providing input() call, got input.staticDependency`);
  84. const template2 = templateCompositeFrom({
  85. ...stub, inputs: {
  86. bar: input.staticDependency(),
  87. },
  88. });
  89. t.doesNotThrow(
  90. () => template2({bar: 'dependency'}));
  91. t.doesNotThrow(
  92. () => template2({bar: input.dependency('dependency')}));
  93. quickThrows(t, template2,
  94. {bar: input.value(123)},
  95. `bar: Expected dependency name, got input.value`);
  96. quickThrows(t, template2,
  97. {bar: input('outOfPlace')},
  98. `bar: Expected dependency name, got input`);
  99. const template3 = templateCompositeFrom({
  100. ...stub, inputs: {
  101. baz: input.staticValue(),
  102. },
  103. });
  104. t.doesNotThrow(
  105. () => template3({baz: input.value(1025)}));
  106. quickThrows(t, template3,
  107. {baz: 'dependency'},
  108. `baz: Expected input.value() call, got dependency name`);
  109. quickThrows(t, template3,
  110. {baz: input('outOfPlace')},
  111. `baz: Expected input.value() call, got input() call`);
  112. });
  113. t.test(`templateCompositeFrom: validate missing / misplaced inputs`, t => {
  114. t.plan(1);
  115. const template = templateCompositeFrom({
  116. ...stub, inputs: {
  117. foo: input(),
  118. bar: input(),
  119. },
  120. });
  121. t.throws(
  122. () => template({
  123. baz: 'aeiou',
  124. raz: input.value(123),
  125. }),
  126. {message: `Errors in input options passed to stubComposite`, errors: [
  127. {message: `Unexpected input names: baz, raz`},
  128. {message: `Required these inputs: foo, bar`},
  129. ]});
  130. });
  131. t.test(`templateCompositeFrom: validate acceptsNull / defaultValue: null`, t => {
  132. t.plan(3);
  133. const template1 = templateCompositeFrom({
  134. ...stub, inputs: {
  135. foo: input(),
  136. },
  137. });
  138. t.throws(
  139. () => template1({}),
  140. {message: `Errors in input options passed to stubComposite`, errors: [
  141. {message: `Required these inputs: foo`},
  142. ]},
  143. `throws if input missing and not marked specially`);
  144. const template2 = templateCompositeFrom({
  145. ...stub, inputs: {
  146. bar: input({acceptsNull: true}),
  147. },
  148. });
  149. t.throws(
  150. () => template2({}),
  151. {message: `Errors in input options passed to stubComposite`, errors: [
  152. {message: `Required these inputs: bar`},
  153. ]},
  154. `throws if input missing even if marked {acceptsNull}`);
  155. const template3 = templateCompositeFrom({
  156. ...stub, inputs: {
  157. baz: input({defaultValue: null}),
  158. },
  159. });
  160. t.doesNotThrow(
  161. () => template3({}),
  162. `does not throw if input missing if marked {defaultValue: null}`);
  163. });
  164. });