« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/lib/composite.js33
-rw-r--r--test/lib/content-function.js285
-rw-r--r--test/lib/generic-mock.js314
-rw-r--r--test/lib/index.js7
-rw-r--r--test/lib/wiki-data.js156
-rw-r--r--test/test-lib.js (renamed from test/lib/strict-match-error.js)36
-rw-r--r--test/unit/content/dependencies/generateAlbumTrackList.js43
-rw-r--r--test/unit/content/dependencies/linkArtist.js31
-rw-r--r--test/unit/content/dependencies/linkContribution.js145
-rw-r--r--test/unit/data/cacheable-object.js12
-rw-r--r--test/unit/data/composite/control-flow/exposeConstant.js42
-rw-r--r--test/unit/data/composite/control-flow/exposeDependency.js64
-rw-r--r--test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js197
-rw-r--r--test/unit/data/composite/data/withPropertiesFromObject.js241
-rw-r--r--test/unit/data/composite/data/withPropertyFromObject.js195
-rw-r--r--test/unit/data/composite/data/withUniqueItemsOnly.js69
-rw-r--r--test/unit/data/validators.js16
17 files changed, 42 insertions, 1844 deletions
diff --git a/test/lib/composite.js b/test/lib/composite.js
deleted file mode 100644
index 359d364d..00000000
--- a/test/lib/composite.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import {compositeFrom} from '#composite';
-
-export function quickCheckCompositeOutputs(t, dependencies) {
-  return (step, outputDict) => {
-    t.same(
-      Object.keys(step.toDescription().outputs),
-      Object.keys(outputDict));
-
-    const composite = compositeFrom({
-      compose: false,
-      steps: [
-        step,
-
-        {
-          dependencies: Object.keys(outputDict),
-
-          // Access all dependencies by their expected keys -
-          // the composition runner actually provides a proxy
-          // and is checking that *we* access the dependencies
-          // we've specified.
-          compute: dependencies =>
-            Object.fromEntries(
-              Object.keys(outputDict)
-                .map(key => [key, dependencies[key]])),
-        },
-      ],
-    });
-
-    t.same(
-      composite.expose.compute(dependencies),
-      outputDict);
-  };
-}
diff --git a/test/lib/content-function.js b/test/lib/content-function.js
deleted file mode 100644
index 49fe5c95..00000000
--- a/test/lib/content-function.js
+++ /dev/null
@@ -1,285 +0,0 @@
-import * as path from 'node:path';
-import {fileURLToPath} from 'node:url';
-import {inspect} from 'node:util';
-
-import chroma from 'chroma-js';
-
-import {showAggregate} from '#aggregate';
-import {getColors} from '#colors';
-import {quickLoadContentDependencies} from '#content-dependencies';
-import {quickEvaluate} from '#content-function';
-import * as html from '#html';
-import {internalDefaultStringsFile, processLanguageFile} from '#language';
-import {empty} from '#sugar';
-
-import {
-  applyLocalizedWithBaseDirectory,
-  generateURLs,
-  internalDefaultURLSpecFile,
-  processURLSpecFromFileSync,
-  thumb,
-} from '#urls';
-
-import mock from './generic-mock.js';
-
-const __dirname = path.dirname(fileURLToPath(import.meta.url));
-
-function cleanURLSpec(urlSpec) {
-  for (const spec of Object.values(urlSpec)) {
-    if (spec.prefix) {
-      // Strip out STATIC_VERSION. This updates fairly regularly and we
-      // don't want it to affect snapshot tests.
-      spec.prefix = spec.prefix
-        .replace(/static-\d+[a-z]\d+/i, 'static');
-    }
-  }
-}
-
-function urlsPlease() {
-  const {aggregate: urlsAggregate, result: urlSpec} =
-    processURLSpecFromFileSync(internalDefaultURLSpecFile);
-
-  urlsAggregate.close();
-
-  applyLocalizedWithBaseDirectory(urlSpec);
-
-  cleanURLSpec(urlSpec);
-
-  return generateURLs(urlSpec);
-}
-
-export function testContentFunctions(t, message, fn) {
-  const urls = urlsPlease();
-
-  t.test(message, async t => {
-    let loadedContentDependencies;
-
-    const language = await processLanguageFile(internalDefaultStringsFile);
-    const mocks = [];
-
-    const evaluate = ({
-      from = 'localized.home',
-      contentDependencies = {},
-      extraDependencies = {},
-      ...opts
-    }) => {
-      if (!loadedContentDependencies) {
-        throw new Error(`Await .load() before performing tests`);
-      }
-
-      const {to} = urls.from(from);
-
-      return cleanCatchAggregate(() => {
-        return quickEvaluate({
-          ...opts,
-          contentDependencies: {
-            ...contentDependencies,
-            ...loadedContentDependencies,
-          },
-          extraDependencies: {
-            html,
-            language,
-            thumb,
-            to,
-            urls,
-
-            pagePath: ['home'],
-            appendIndexHTML: false,
-            getColors: c => getColors(c, {chroma}),
-
-            wikiData: {
-              wikiInfo: {},
-            },
-
-            ...extraDependencies,
-          },
-        });
-      });
-    };
-
-    evaluate.load = async (opts) => {
-      if (loadedContentDependencies) {
-        throw new Error(`Already loaded!`);
-      }
-
-      loadedContentDependencies = await asyncCleanCatchAggregate(() =>
-        quickLoadContentDependencies({
-          logging: false,
-          ...opts,
-        }));
-    };
-
-    evaluate.snapshot = (...args) => {
-      if (!loadedContentDependencies) {
-        throw new Error(`Await .load() before performing tests`);
-      }
-
-      const [description, opts] =
-        (typeof args[0] === 'string'
-          ? args
-          : ['output', ...args]);
-
-      let result = evaluate(opts);
-
-      if (opts.multiple) {
-        result = result.map(item => item.toString()).join('\n');
-      } else {
-        result = result.toString();
-      }
-
-      t.matchSnapshot(result, description);
-    };
-
-    evaluate.stubTemplate = name =>
-      // Creates a particularly permissable template, allowing any slot values
-      // to be stored and just outputting the contents of those slots as-are.
-      _stubTemplate(name, false);
-
-    evaluate.stubContentFunction = name =>
-      // Like stubTemplate, but instead of a template directly, returns
-      // an object describing a content function - suitable for passing
-      // into evaluate.mock.
-      _stubTemplate(name, true);
-
-    const _stubTemplate = (name, mockContentFunction) => {
-      const inspectNicely = (value, opts = {}) =>
-        inspect(value, {
-          ...opts,
-          colors: false,
-          sort: true,
-        });
-
-      const makeTemplate = formatContentFn =>
-        new (class extends html.Template {
-          #slotValues = {};
-
-          constructor() {
-            super({
-              content: () => this.#getContent(formatContentFn),
-            });
-          }
-
-          setSlots(slotNamesToValues) {
-            Object.assign(this.#slotValues, slotNamesToValues);
-          }
-
-          setSlot(slotName, slotValue) {
-            this.#slotValues[slotName] = slotValue;
-          }
-
-          #getContent(formatContentFn) {
-            const toInspect =
-              Object.fromEntries(
-                Object.entries(this.#slotValues)
-                  .filter(([key, value]) => value !== null));
-
-            const inspected =
-              inspectNicely(toInspect, {
-                breakLength: Infinity,
-                compact: true,
-                depth: Infinity,
-              });
-
-            return formatContentFn(inspected); `${name}: ${inspected}`;
-          }
-        });
-
-      if (mockContentFunction) {
-        return {
-          data: (...args) => ({args}),
-          generate: (data) =>
-            makeTemplate(slots => {
-              const argsLines =
-                (empty(data.args)
-                  ? []
-                  : inspectNicely(data.args, {depth: Infinity})
-                      .split('\n'));
-
-              return (`[mocked: ${name}` +
-
-                (empty(data.args)
-                  ? ``
-               : argsLines.length === 1
-                  ? `\n args: ${argsLines[0]}`
-                  : `\n args: ${argsLines[0]}\n` +
-                    argsLines.slice(1).join('\n').replace(/^/gm, ' ')) +
-
-                (!empty(data.args)
-                  ? `\n `
-                  : ` - `) +
-
-                (slots
-                  ? `slots: ${slots}]`
-                  : `slots: none]`));
-            }),
-        };
-      } else {
-        return makeTemplate(slots => `${name}: ${slots}`);
-      }
-    };
-
-    evaluate.mock = (...opts) => {
-      const {value, close} = mock(...opts);
-      mocks.push({close});
-      return value;
-    };
-
-    evaluate.mock.transformContent = {
-      transformContent: {
-        extraDependencies: ['html'],
-        data: content => ({content}),
-        slots: {mode: {type: 'string'}},
-        generate: ({content}) => content,
-      },
-    };
-
-    await fn(t, evaluate);
-
-    if (!empty(mocks)) {
-      cleanCatchAggregate(() => {
-        const errors = [];
-        for (const {close} of mocks) {
-          try {
-            close();
-          } catch (error) {
-            errors.push(error);
-          }
-        }
-        if (!empty(errors)) {
-          throw new AggregateError(errors, `Errors closing mocks`);
-        }
-      });
-    }
-  });
-}
-
-function printAggregate(error) {
-  if (error instanceof AggregateError) {
-    const message = showAggregate(error, {
-      showTraces: true,
-      print: false,
-      pathToFileURL: f => path.relative(path.join(__dirname, '../..'), fileURLToPath(f)),
-    });
-    for (const line of message.split('\n')) {
-      console.error(line);
-    }
-  }
-}
-
-function cleanCatchAggregate(fn) {
-  try {
-    return fn();
-  } catch (error) {
-    printAggregate(error);
-    throw error;
-  }
-}
-
-async function asyncCleanCatchAggregate(fn) {
-  try {
-    return await fn();
-  } catch (error) {
-    printAggregate(error);
-    throw error;
-  }
-}
diff --git a/test/lib/generic-mock.js b/test/lib/generic-mock.js
deleted file mode 100644
index 28309ab0..00000000
--- a/test/lib/generic-mock.js
+++ /dev/null
@@ -1,314 +0,0 @@
-import {same} from 'tcompare';
-
-import {empty} from '#sugar';
-
-export default function mock(callback) {
-  const mocks = [];
-
-  const track = callback => (...args) => {
-    const {value, close} = callback(...args);
-    mocks.push({close});
-    return value;
-  };
-
-  const mock = {
-    function: track(mockFunction),
-  };
-
-  return {
-    value: callback(mock),
-    close: () => {
-      const errors = [];
-      for (const mock of mocks) {
-        try {
-          mock.close();
-        } catch (error) {
-          errors.push(error);
-        }
-      }
-      if (!empty(errors)) {
-        throw new AggregateError(errors, `Errors closing sub-mocks`);
-      }
-    },
-  };
-}
-
-export function mockFunction(...args) {
-  let name = '(anonymous)';
-  let behavior = null;
-
-  if (args.length === 2) {
-    if (
-      typeof args[0] === 'string' &&
-      typeof args[1] === 'function'
-    ) {
-      name = args[0];
-      behavior = args[1];
-    } else {
-      throw new TypeError(`Expected name to be a string`);
-    }
-  } else if (args.length === 1) {
-    if (typeof args[0] === 'string') {
-      name = args[0];
-    } else if (typeof args[0] === 'function') {
-      behavior = args[0];
-    } else if (args[0] !== null) {
-      throw new TypeError(`Expected string (name), function (behavior), both, or null / no arguments`);
-    }
-  } else if (args.length > 2) {
-    throw new TypeError(`Expected string (name), function (behavior), both, or null / no arguments`);
-  }
-
-  let currentCallDescription = newCallDescription();
-  const allCallDescriptions = [currentCallDescription];
-
-  const topLevelErrors = [];
-  let runningCallCount = 0;
-  let limitCallCount = false;
-  let markedAsOnce = false;
-
-  const fn = (...args) => {
-    const description = processCall(...args);
-    return description.behavior(...args);
-  };
-
-  fn.behavior = value => {
-    if (!(value === null || (
-      typeof value === 'function'
-    ))) {
-      throw new TypeError(`Expected function or null`);
-    }
-
-    currentCallDescription.behavior = behavior;
-    currentCallDescription.described = true;
-
-    return fn;
-  }
-
-  fn.argumentCount = value => {
-    if (!(value === null || (
-      typeof value === 'number' &&
-      value === parseInt(value) &&
-      value >= 0
-    ))) {
-      throw new TypeError(`Expected whole number or null`);
-    }
-
-    if (currentCallDescription.argsPattern) {
-      throw new TypeError(`Unexpected .argumentCount() when .args() has been called`);
-    }
-
-    currentCallDescription.argsPattern = {length: value};
-    currentCallDescription.described = true;
-
-    return fn;
-  };
-
-  fn.args = (...args) => {
-    const value = args[0];
-
-    if (args.length > 1 || !(value === null || Array.isArray(value))) {
-      throw new TypeError(`Expected one array or null`);
-    }
-
-    currentCallDescription.argsPattern = Object.fromEntries(
-      value
-        .map((v, i) => v === undefined ? false : [i, v])
-        .filter(Boolean)
-        .concat([['length', value.length]]));
-
-    currentCallDescription.described = true;
-
-    return fn;
-  };
-
-  fn.neverCalled = (...args) => {
-    if (!empty(args)) {
-      throw new TypeError(`Didn't expect any arguments`);
-    }
-
-    if (allCallDescriptions[0].described) {
-      throw new TypeError(`Unexpected .neverCalled() when any descriptions provided`);
-    }
-
-    limitCallCount = true;
-    allCallDescriptions.splice(0, allCallDescriptions.length);
-
-    currentCallDescription = new Proxy({}, {
-      set() {
-        throw new Error(`Unexpected description when .neverCalled() has been called`);
-      },
-    });
-
-    return fn;
-  };
-
-  fn.once = (...args) => {
-    if (!empty(args)) {
-      throw new TypeError(`Didn't expect any arguments`);
-    }
-
-    if (allCallDescriptions.length > 1) {
-      throw new TypeError(`Unexpected .once() when providing multiple descriptions`);
-    }
-
-    currentCallDescription.described = true;
-    limitCallCount = true;
-    markedAsOnce = true;
-
-    return fn;
-  };
-
-  fn.next = (...args) => {
-    if (!empty(args)) {
-      throw new TypeError(`Didn't expect any arguments`);
-    }
-
-    if (markedAsOnce) {
-      throw new TypeError(`Unexpected .next() when .once() has been called`);
-    }
-
-    currentCallDescription = newCallDescription();
-    allCallDescriptions.push(currentCallDescription);
-
-    limitCallCount = true;
-
-    return fn;
-  };
-
-  fn.repeat = times => {
-    // Note: This function should be called AFTER filling out the
-    // call description which is being repeated.
-
-    if (!(
-      typeof times === 'number' &&
-      times === parseInt(times) &&
-      times >= 2
-    )) {
-      throw new TypeError(`Expected whole number of at least 2`);
-    }
-
-    if (markedAsOnce) {
-      throw new TypeError(`Unexpected .repeat() when .once() has been called`);
-    }
-
-    // The current call description is already in the full list,
-    // so skip the first push.
-    for (let i = 2; i <= times; i++) {
-      allCallDescriptions.push(currentCallDescription);
-    }
-
-    // Prep a new description like when calling .next().
-    currentCallDescription = newCallDescription();
-    allCallDescriptions.push(currentCallDescription);
-
-    limitCallCount = true;
-
-    return fn;
-  };
-
-  return {
-    value: fn,
-    close: () => {
-      const totalCallCount = runningCallCount;
-      const expectedCallCount = countDescribedCalls();
-
-      if (limitCallCount && totalCallCount !== expectedCallCount) {
-        if (expectedCallCount > 1) {
-          topLevelErrors.push(new Error(`Expected ${expectedCallCount} calls, got ${totalCallCount}`));
-        } else if (expectedCallCount === 1) {
-          topLevelErrors.push(new Error(`Expected 1 call, got ${totalCallCount}`));
-        } else {
-          topLevelErrors.push(new Error(`Expected no calls, got ${totalCallCount}`));
-        }
-      }
-
-      if (topLevelErrors.length) {
-        throw new AggregateError(topLevelErrors, `Errors in mock ${name}`);
-      }
-    },
-  };
-
-  function newCallDescription() {
-    return {
-      described: false,
-      behavior: behavior ?? null,
-      argumentCount: null,
-      argsPattern: null,
-    };
-  }
-
-  function processCall(...args) {
-    const callErrors = [];
-
-    runningCallCount++;
-
-    // No further processing, this indicates the function shouldn't have been
-    // called at all and there aren't any descriptions to match this call with.
-    if (empty(allCallDescriptions)) {
-      return newCallDescription();
-    }
-
-    const currentCallNumber = runningCallCount;
-    const currentDescription = selectCallDescription(currentCallNumber);
-
-    const {
-      argumentCount,
-      argsPattern,
-    } = currentDescription;
-
-    if (argumentCount !== null && args.length !== argumentCount) {
-      callErrors.push(
-        new Error(`Argument count mismatch: expected ${argumentCount}, got ${args.length}`));
-    }
-
-    if (argsPattern !== null) {
-      const keysToCheck = Object.keys(argsPattern);
-      const argsAsObject = Object.fromEntries(
-        args
-          .map((v, i) => [i.toString(), v])
-          .filter(([i]) => keysToCheck.includes(i))
-          .concat([['length', args.length]]));
-
-      const {match, diff} = same(argsAsObject, argsPattern);
-      if (!match) {
-        callErrors.push(new Error(`Argument pattern mismatch:\n` + diff));
-      }
-    }
-
-    if (!empty(callErrors)) {
-      const aggregate = new AggregateError(callErrors, `Errors in call #${currentCallNumber}`);
-      topLevelErrors.push(aggregate);
-    }
-
-    return currentDescription;
-  }
-
-  function selectCallDescription(currentCallNumber) {
-    if (currentCallNumber > countDescribedCalls()) {
-      const lastDescription = lastCallDescription();
-      if (lastDescription.described) {
-        return newCallDescription();
-      } else {
-        return lastDescription;
-      }
-    } else {
-      return allCallDescriptions[currentCallNumber - 1];
-    }
-  }
-
-  function countDescribedCalls() {
-    if (empty(allCallDescriptions)) {
-      return 0;
-    }
-
-    return (
-      (lastCallDescription().described
-        ? allCallDescriptions.length
-        : allCallDescriptions.length - 1));
-  }
-
-  function lastCallDescription() {
-    return allCallDescriptions[allCallDescriptions.length - 1];
-  }
-}
diff --git a/test/lib/index.js b/test/lib/index.js
deleted file mode 100644
index 4c9ee23f..00000000
--- a/test/lib/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-Error.stackTraceLimit = Infinity;
-
-export * from './composite.js';
-export * from './content-function.js';
-export * from './generic-mock.js';
-export * from './wiki-data.js';
-export * from './strict-match-error.js';
diff --git a/test/lib/wiki-data.js b/test/lib/wiki-data.js
deleted file mode 100644
index f0ee0ef5..00000000
--- a/test/lib/wiki-data.js
+++ /dev/null
@@ -1,156 +0,0 @@
-import CacheableObject from '#cacheable-object';
-import find, {bindFind} from '#find';
-import {bindReverse} from '#reverse';
-import {withEntries} from '#sugar';
-import Thing from '#thing';
-import thingConstructors from '#things';
-import {linkWikiDataArrays} from '#yaml';
-
-export function linkAndBindWikiData(wikiData, {
-  inferAlbumsOwnTrackData = true,
-} = {}) {
-  function customLinkWikiDataArrays(wikiData, options = {}) {
-    if (options.XXX_decacheWikiData) {
-      wikiData =
-        withEntries(wikiData, entries => entries
-          .map(([key, value]) => [key, value.slice()]));
-    }
-
-    linkWikiDataArrays(wikiData, {bindFind, bindReverse});
-  }
-
-  customLinkWikiDataArrays(wikiData);
-
-  return {
-    // Mutate to make the below functions aware of new data objects, or of
-    // reordering the existing ones. Don't mutate arrays such as trackData
-    // in-place; assign completely new arrays to this wikiData object instead.
-    wikiData,
-
-    // Use this after you've mutated wikiData to assign new data arrays.
-    // It'll automatically relink everything on wikiData so all the objects
-    // are caught up to date.
-    linkWikiDataArrays:
-      customLinkWikiDataArrays
-        .bind(null, wikiData),
-
-    // Use this if you HAVEN'T mutated wikiData and just need to decache
-    // indirect dependencies on exposed properties of other data objects.
-    //
-    // XXX_decacheWikiData option should be used specifically to mark points
-    // where you *aren't* replacing any of the arrays under wikiData with
-    // new values, and are using linkWikiDataArrays to instead "decache" data
-    // properties which depend on any of them. It's currently not possible for
-    // a CacheableObject to depend directly on the value of a property exposed
-    // on some other CacheableObject, so when those values change, you have to
-    // manually decache before the object will realize its cache isn't valid
-    // anymore.
-    //
-    // The previous implementation for this involved overwriting the relevant
-    // wikiData properties with null, then replacing it with the original
-    // array, which effectively cleared a CacheableObject cache. But it isn't
-    // enough to clear other caches that depend on the identity of wikiData
-    // arrays, such as withReverseReferenceList, so now it replaces with fresh
-    // copies of the data arrays instead; the original identities don't get
-    // reused.
-    XXX_decacheWikiData:
-      customLinkWikiDataArrays
-        .bind(null, wikiData, {XXX_decacheWikiData: true}),
-  };
-}
-
-export function stubWikiData() {
-  return {
-    albumData: [],
-    artistData: [],
-    artTagData: [],
-    flashData: [],
-    flashActData: [],
-    flashSideData: [],
-    groupData: [],
-    groupCategoryData: [],
-    newsData: [],
-    staticPageData: [],
-    trackData: [],
-    trackSectionData: [],
-  };
-}
-
-export function stubThing(wikiData, constructor, properties = {}) {
-  const thing = Reflect.construct(constructor, []);
-  Object.assign(thing, properties);
-
-  const wikiDataSpec = {
-    Album: 'albumData',
-    Artist: 'artistData',
-    ArtTag: 'artTagData',
-    Flash: 'flashData',
-    FlashAct: 'flashActData',
-    FlashSide: 'flashSideData',
-    Group: 'groupData',
-    GroupCategory: 'groupCategoryData',
-    NewsEntry: 'newsData',
-    StaticPage: 'staticPageData',
-    Track: 'trackData',
-    TrackSection: 'trackSectionData',
-  };
-
-  const wikiDataMap =
-    new Map(
-      Object.entries(wikiDataSpec)
-        .map(([thingKey, wikiDataKey]) => [
-          thingConstructors[thingKey],
-          wikiData[wikiDataKey],
-        ]));
-
-  const wikiDataArray =
-    wikiDataMap.get(constructor);
-
-  wikiDataArray.push(thing);
-
-  return thing;
-}
-
-export function stubTrackAndAlbum(wikiData, trackDirectory = null, albumDirectory = null) {
-  const {Track, TrackSection, Album} = thingConstructors;
-
-  const track =
-    stubThing(wikiData, Track, {directory: trackDirectory});
-
-  const section =
-    stubThing(wikiData, TrackSection, {tracks: [track]});
-
-  const album =
-    stubThing(wikiData, Album, {directory: albumDirectory, trackSections: [section]});
-
-  return {track, album, section};
-}
-
-export function stubArtistAndContribs(wikiData, artistName = `Test Artist`) {
-  const {Artist} = thingConstructors;
-
-  const artist =
-    stubThing(wikiData, Artist, {name: artistName});
-
-  const contribs =
-    [{artist: artistName, annotation: null}];
-
-  const badContribs =
-    [{artist: `Figment of Your Imagination`, annotation: null}];
-
-  return {artist, contribs, badContribs};
-}
-
-export function stubFlashAndAct(wikiData, flashDirectory = null) {
-  const {Flash, FlashAct} = thingConstructors;
-
-  const flash =
-    stubThing(wikiData, Flash, {directory: flashDirectory});
-
-  const flashAct =
-    stubThing(wikiData, FlashAct, {
-      flashes: [Thing.getReference(flash)],
-    });
-
-  return {flash, flashAct};
-}
diff --git a/test/lib/strict-match-error.js b/test/test-lib.js
index e3b36e93..a12974cd 100644
--- a/test/lib/strict-match-error.js
+++ b/test/test-lib.js
@@ -1,3 +1,39 @@
+import {compositeFrom} from '#composite';
+
+Error.stackTraceLimit = Infinity;
+
+export function quickCheckCompositeOutputs(t, dependencies) {
+  return (step, outputDict) => {
+    t.same(
+      Object.keys(step.toDescription().outputs),
+      Object.keys(outputDict));
+
+    const composite = compositeFrom({
+      compose: false,
+      steps: [
+        step,
+
+        {
+          dependencies: Object.keys(outputDict),
+
+          // Access all dependencies by their expected keys -
+          // the composition runner actually provides a proxy
+          // and is checking that *we* access the dependencies
+          // we've specified.
+          compute: dependencies =>
+            Object.fromEntries(
+              Object.keys(outputDict)
+                .map(key => [key, dependencies[key]])),
+        },
+      ],
+    });
+
+    t.same(
+      composite.expose.compute(dependencies),
+      outputDict);
+  };
+}
+
 export function strictlyThrows(t, fn, pattern) {
   const error = catchErrorOrNull(fn);
 
diff --git a/test/unit/content/dependencies/generateAlbumTrackList.js b/test/unit/content/dependencies/generateAlbumTrackList.js
deleted file mode 100644
index 988f8505..00000000
--- a/test/unit/content/dependencies/generateAlbumTrackList.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import t from 'tap';
-import {testContentFunctions} from '#test-lib';
-
-testContentFunctions(t, 'generateAlbumTrackList (unit)', async (t, evaluate) => {
-  await evaluate.load({
-    mock: {
-      generateAlbumTrackListItem: {
-        extraDependencies: ['html'],
-        data: track => track.name,
-        generate: (name, {html}) =>
-          html.tag('li', `Item: ${name}`),
-      },
-
-      image:
-        evaluate.stubContentFunction('image'),
-    },
-  });
-
-  let readDuration = false;
-
-  const track = (name, duration) => ({
-    name,
-    get duration() {
-      readDuration = true;
-      return duration;
-    },
-  });
-
-  const tracks = [
-    track('Track 1', 30),
-    track('Track 2', 15),
-  ];
-
-  evaluate({
-    name: 'generateAlbumTrackList',
-    args: [{
-      trackSections: [{isDefaultTrackSection: true, tracks}],
-      tracks,
-    }],
-  });
-
-  t.notOk(readDuration, 'expect no access to track.duration property');
-});
diff --git a/test/unit/content/dependencies/linkArtist.js b/test/unit/content/dependencies/linkArtist.js
deleted file mode 100644
index e6e19d2f..00000000
--- a/test/unit/content/dependencies/linkArtist.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import t from 'tap';
-import {testContentFunctions} from '#test-lib';
-
-testContentFunctions(t, 'linkArtist (unit)', async (t, evaluate) => {
-  const artistObject = {};
-  const linkTemplate = {};
-
-  await evaluate.load({
-    mock: evaluate.mock(mock => ({
-      linkThing: {
-        relations: mock.function('linkThing.relations', () => ({}))
-          .args([undefined, 'localized.artist', artistObject])
-          .once(),
-
-        data: mock.function('linkThing.data', () => ({}))
-          .args(['localized.artist', artistObject])
-          .once(),
-
-        generate: mock.function('linkThing.data', () => linkTemplate)
-          .once(),
-      }
-    })),
-  });
-
-  const result = evaluate({
-    name: 'linkArtist',
-    args: [artistObject],
-  });
-
-  t.equal(result, linkTemplate);
-});
diff --git a/test/unit/content/dependencies/linkContribution.js b/test/unit/content/dependencies/linkContribution.js
deleted file mode 100644
index 1baa80f8..00000000
--- a/test/unit/content/dependencies/linkContribution.js
+++ /dev/null
@@ -1,145 +0,0 @@
-import t from 'tap';
-import {testContentFunctions} from '#test-lib';
-
-t.test('linkContribution (unit)', async t => {
-  const artist1 = {
-    name: 'Clark Powell',
-    directory: 'clark-powell',
-    urls: ['https://soundcloud.com/plazmataz'],
-  };
-
-  const artist2 = {
-    name: 'Grounder & Scratch',
-    directory: 'the-big-baddies',
-    urls: [],
-  };
-
-  const artist3 = {
-    name: 'Toby Fox',
-    directory: 'toby-fox',
-    urls: ['https://tobyfox.bandcamp.com/', 'https://toby.fox/'],
-  };
-
-  const annotation1 = null;
-  const annotation2 = 'Snooping';
-  const annotation3 = 'Arrangement';
-
-  const thing1 = {};
-  const thing2 = {};
-  const thing3 = {};
-
-  const contribution1 = {artist: artist1, annotation: annotation1, thing: thing1};
-  const contribution2 = {artist: artist2, annotation: annotation2, thing: thing2};
-  const contribution3 = {artist: artist3, annotation: annotation3, thing: thing3};
-
-  await testContentFunctions(t, 'linkContribution (unit 1)', async (t, evaluate) => {
-    const slots = {
-      showAnnotation: true,
-      showExternalLinks: true,
-    };
-
-    await evaluate.load({
-      mock: evaluate.mock(mock => ({
-        linkArtist: {
-          relations: mock
-            .function('linkArtist.relations', () => ({}))
-            .args([undefined, artist1]).next()
-            .args([undefined, artist2]).next()
-            .args([undefined, artist3]),
-
-          data: mock
-            .function('linkArtist.data', () => ({}))
-            .args([artist1]).next()
-            .args([artist2]).next()
-            .args([artist3]),
-
-          // This can be tweaked to return a specific (mocked) template
-          // for each artist if we need to test for slots in the future.
-          generate: mock.function('linkArtist.generate', () => 'artist link')
-            .repeat(3),
-        },
-
-        generateExternalIcon: {
-          data: mock
-            .function('generateExternalIcon.data', () => ({}))
-            .args([artist1.urls[0]]).next()
-            .args([artist3.urls[0]]).next()
-            .args([artist3.urls[1]]),
-
-          generate: mock
-            .function('generateExternalIcon.generate', () => ({
-              toString: () => 'icon',
-              setSlot: () => {},
-            }))
-            .repeat(3),
-        }
-      })),
-    });
-
-    evaluate({
-      name: 'linkContribution',
-      multiple: [
-        {args: [contribution1]},
-        {args: [contribution2]},
-        {args: [contribution3]},
-      ],
-      slots,
-    });
-  });
-
-  await testContentFunctions(t, 'linkContribution (unit 2)', async (t, evaluate) => {
-    const slots = {
-      showAnnotation: false,
-      showExternalLinks: false,
-    };
-
-    await evaluate.load({
-      mock: evaluate.mock(mock => ({
-        linkArtist: {
-          relations: mock
-            .function('linkArtist.relations', () => ({}))
-            .args([undefined, artist1]).next()
-            .args([undefined, artist2]).next()
-            .args([undefined, artist3]),
-
-          data: mock
-            .function('linkArtist.data', () => ({}))
-            .args([artist1]).next()
-            .args([artist2]).next()
-            .args([artist3]),
-
-          generate: mock
-            .function(() => 'artist link')
-            .repeat(3),
-        },
-
-        // Even though icons are hidden, these are still called! The dependency
-        // tree is the same since whether or not the external icon links are
-        // shown is dependent on a slot, which is undefined and arbitrary at
-        // relations/data time (it might change on a whim at generate time).
-        generateExternalIcon: {
-          data: mock
-            .function('generateExternalIcon.data', () => ({}))
-            .repeat(3),
-
-          generate: mock
-            .function('generateExternalIcon.generate', () => ({
-              toString: () => 'icon',
-              setSlot: () => {},
-            }))
-            .repeat(3),
-        },
-      })),
-    });
-
-    evaluate({
-      name: 'linkContribution',
-      multiple: [
-        {args: [contribution1]},
-        {args: [contribution2]},
-        {args: [contribution3]},
-      ],
-      slots,
-    });
-  });
-});
diff --git a/test/unit/data/cacheable-object.js b/test/unit/data/cacheable-object.js
index 4be31788..d0448025 100644
--- a/test/unit/data/cacheable-object.js
+++ b/test/unit/data/cacheable-object.js
@@ -26,8 +26,8 @@ t.test(`CacheableObject simple separate update & expose`, t => {
       },
 
       expose: {
-        dependencies: ['number'],
-        compute: ({ number }) => number * 2
+        dependencies: ['_number'],
+        compute: ({ _number: number }) => number * 2
       }
     }
   });
@@ -53,8 +53,8 @@ t.test(`CacheableObject basic cache behavior`, t => {
       },
 
       expose: {
-        dependencies: ['string'],
-        compute: ({ string }) => {
+        dependencies: ['_string'],
+        compute: ({ _string: string }) => {
           computeCount++;
           return string.toUpperCase();
         }
@@ -136,8 +136,8 @@ t.test(`CacheableObject combined update & expose (transform with dependency)`, t
       },
 
       expose: {
-        dependencies: ['times'],
-        transform: (value, { times }) => value.repeat(times)
+        dependencies: ['_times'],
+        transform: (value, { _times: times }) => value.repeat(times)
       }
     },
 
diff --git a/test/unit/data/composite/control-flow/exposeConstant.js b/test/unit/data/composite/control-flow/exposeConstant.js
deleted file mode 100644
index 0c75894b..00000000
--- a/test/unit/data/composite/control-flow/exposeConstant.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import t from 'tap';
-
-import {compositeFrom, continuationSymbol, input} from '#composite';
-import {exposeConstant} from '#composite/control-flow';
-
-t.test(`exposeConstant: basic behavior`, t => {
-  t.plan(2);
-
-  const composite1 = compositeFrom({
-    compose: false,
-
-    steps: [
-      exposeConstant({
-        value: input.value('foo'),
-      }),
-    ],
-  });
-
-  t.match(composite1, {
-    expose: {
-      dependencies: [],
-    },
-  });
-
-  t.equal(composite1.expose.compute(), 'foo');
-});
-
-t.test(`exposeConstant: validate inputs`, t => {
-  t.plan(2);
-
-  t.throws(
-    () => exposeConstant({}),
-    {message: `Errors in input options passed to exposeConstant`, errors: [
-      {message: `Required these inputs: 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/control-flow/exposeDependency.js b/test/unit/data/composite/control-flow/exposeDependency.js
deleted file mode 100644
index 8f6bfd01..00000000
--- a/test/unit/data/composite/control-flow/exposeDependency.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import t from 'tap';
-
-import {compositeFrom, continuationSymbol, input} from '#composite';
-import {exposeDependency} from '#composite/control-flow';
-
-t.test(`exposeDependency: basic behavior`, t => {
-  t.plan(4);
-
-  const composite1 = compositeFrom({
-    compose: false,
-
-    steps: [
-      exposeDependency({dependency: 'foo'}),
-    ],
-  });
-
-  t.match(composite1, {
-    expose: {
-      dependencies: ['foo'],
-    },
-  });
-
-  t.equal(composite1.expose.compute({foo: 'bar'}), 'bar');
-
-  const composite2 = compositeFrom({
-    compose: false,
-
-    steps: [
-      {
-        dependencies: ['foo'],
-        compute: (continuation, {foo}) =>
-          continuation({'#bar': foo.toUpperCase()}),
-      },
-
-      exposeDependency({dependency: '#bar'}),
-    ],
-  });
-
-  t.match(composite2, {
-    expose: {
-      dependencies: ['foo'],
-    },
-  });
-
-  t.equal(composite2.expose.compute({foo: 'bar'}), 'BAR');
-});
-
-t.test(`exposeDependency: validate inputs`, t => {
-  t.plan(2);
-
-  t.throws(
-    () => exposeDependency({}),
-    {message: `Errors in input options passed to exposeDependency`, errors: [
-      {message: `Required these inputs: dependency`},
-    ]});
-
-  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`},
-    ]});
-});
diff --git a/test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js b/test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js
deleted file mode 100644
index 9d588e4c..00000000
--- a/test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js
+++ /dev/null
@@ -1,197 +0,0 @@
-import t from 'tap';
-
-import {compositeFrom, continuationSymbol, input} from '#composite';
-import {withResultOfAvailabilityCheck} from '#composite/control-flow';
-
-const composite = compositeFrom({
-  compose: false,
-
-  steps: [
-    withResultOfAvailabilityCheck({
-      from: 'from',
-      mode: 'mode',
-    }).outputs({
-      ['#availability']: '#result',
-    }),
-
-    {
-      dependencies: ['#result'],
-      compute: ({'#result': result}) => result,
-    },
-  ],
-});
-
-t.test(`withResultOfAvailabilityCheck: basic behavior`, t => {
-  t.plan(1);
-
-  t.match(composite, {
-    expose: {
-      dependencies: ['from', 'mode'],
-    },
-  });
-});
-
-const quickCompare = (t, expect, {from, mode}) =>
-  t.equal(composite.expose.compute({from, mode}), expect);
-
-const quickThrows = (t, {from, mode}) =>
-  t.throws(() => composite.expose.compute({from, mode}));
-
-t.test(`withResultOfAvailabilityCheck: mode = null`, t => {
-  t.plan(11);
-
-  quickCompare(t, true,  {mode: 'null', from: 'truthy string'});
-  quickCompare(t, true,  {mode: 'null', from: 123});
-  quickCompare(t, true,  {mode: 'null', from: true});
-
-  quickCompare(t, true,  {mode: 'null', from: ''});
-  quickCompare(t, true,  {mode: 'null', from: 0});
-  quickCompare(t, true,  {mode: 'null', from: -1});
-  quickCompare(t, true,  {mode: 'null', from: false});
-
-  quickCompare(t, true,  {mode: 'null', from: [1, 2, 3]});
-  quickCompare(t, true,  {mode: 'null', from: []});
-
-  quickCompare(t, false, {mode: 'null', from: null});
-  quickCompare(t, false, {mode: 'null', from: undefined});
-});
-
-t.test(`withResultOfAvailabilityCheck: mode = empty`, t => {
-  t.plan(11);
-
-  quickThrows(t, {mode: 'empty', from: 'truthy string'});
-  quickThrows(t, {mode: 'empty', from: 123});
-  quickThrows(t, {mode: 'empty', from: true});
-
-  quickThrows(t, {mode: 'empty', from: ''});
-  quickThrows(t, {mode: 'empty', from: 0});
-  quickThrows(t, {mode: 'empty', from: -1});
-  quickThrows(t, {mode: 'empty', from: false});
-
-  quickCompare(t, true,  {mode: 'empty', from: [1, 2, 3]});
-  quickCompare(t, false, {mode: 'empty', from: []});
-
-  quickCompare(t, false, {mode: 'empty', from: null});
-  quickCompare(t, false, {mode: 'empty', from: undefined});
-});
-
-t.test(`withResultOfAvailabilityCheck: mode = falsy`, t => {
-  t.plan(11);
-
-  quickCompare(t, true,  {mode: 'falsy', from: 'truthy string'});
-  quickCompare(t, true,  {mode: 'falsy', from: 123});
-  quickCompare(t, true,  {mode: 'falsy', from: true});
-
-  quickCompare(t, false, {mode: 'falsy', from: ''});
-  quickCompare(t, false, {mode: 'falsy', from: 0});
-  quickCompare(t, true,  {mode: 'falsy', from: -1});
-  quickCompare(t, false, {mode: 'falsy', from: false});
-
-  quickCompare(t, true,  {mode: 'falsy', from: [1, 2, 3]});
-  quickCompare(t, false, {mode: 'falsy', from: []});
-
-  quickCompare(t, false, {mode: 'falsy', from: null});
-  quickCompare(t, false, {mode: 'falsy', from: undefined});
-});
-
-t.test(`withResultOfAvailabilityCheck: mode = index`, t => {
-  t.plan(11);
-
-  quickCompare(t, false, {mode: 'index', from: 'truthy string'});
-  quickCompare(t, true,  {mode: 'index', from: 123});
-  quickCompare(t, false, {mode: 'index', from: true});
-
-  quickCompare(t, false, {mode: 'index', from: ''});
-  quickCompare(t, true,  {mode: 'index', from: 0});
-  quickCompare(t, false, {mode: 'index', from: -1});
-  quickCompare(t, false, {mode: 'index', from: false});
-
-  quickCompare(t, false, {mode: 'index', from: [1, 2, 3]});
-  quickCompare(t, false, {mode: 'index', from: []});
-
-  quickCompare(t, false, {mode: 'index', from: null});
-  quickCompare(t, false, {mode: 'index', from: undefined});
-});
-
-t.test(`withResultOfAvailabilityCheck: default mode`, t => {
-  t.plan(1);
-
-  const template = withResultOfAvailabilityCheck({
-    from: 'foo',
-  });
-
-  t.match(template.toDescription(), {
-    inputMapping: {
-      from: input.dependency('foo'),
-      mode: input.value('null'),
-    },
-  });
-});
-
-t.test(`withResultOfAvailabilityCheck: validate static inputs`, t => {
-  t.plan(5);
-
-  t.throws(
-    () => withResultOfAvailabilityCheck({}),
-    {message: `Errors in input options passed to withResultOfAvailabilityCheck`, errors: [
-      {message: `Required these inputs: from`},
-    ]});
-
-  t.doesNotThrow(() =>
-    withResultOfAvailabilityCheck({
-      from: 'dependency1',
-      mode: 'dependency2',
-    }));
-
-  t.doesNotThrow(() =>
-    withResultOfAvailabilityCheck({
-      from: input.value('some static value'),
-      mode: input.value('null'),
-    }));
-
-  t.throws(
-    () => withResultOfAvailabilityCheck({
-      from: 'foo',
-      mode: input.value('invalid'),
-    }),
-    {message: `Errors in input options passed to withResultOfAvailabilityCheck`, errors: [
-      {message: `mode: Expected one of null empty falsy index, got invalid`},
-    ]});
-
-  t.throws(() =>
-    withResultOfAvailabilityCheck({
-      from: input.value(null),
-      mode: input.value(null),
-    }),
-    {message: `Errors in input options passed to withResultOfAvailabilityCheck`, errors: [
-      {message: `mode: Expected a value, got null`},
-    ]});
-});
-
-t.test(`withResultOfAvailabilityCheck: validate dynamic inputs`, t => {
-  t.plan(2);
-
-  t.throws(
-    () => composite.expose.compute({
-      from: 'apple',
-      mode: 'banana',
-    }),
-    {message: `Error computing composition`, cause:
-      {message: `Error in step 1 of 2, withResultOfAvailabilityCheck`, cause:
-        {message: `Error computing composition withResultOfAvailabilityCheck`, cause:
-          {message: `Errors in input values provided to withResultOfAvailabilityCheck`, errors: [
-            {message: `mode: Expected one of null empty falsy index, got banana`},
-          ]}}}});
-
-  t.throws(
-    () => composite.expose.compute({
-      from: null,
-      mode: null,
-    }),
-    {message: `Error computing composition`, cause:
-      {message: `Error in step 1 of 2, withResultOfAvailabilityCheck`, cause:
-        {message: `Error computing composition withResultOfAvailabilityCheck`, cause:
-          {message: `Errors in input values provided to withResultOfAvailabilityCheck`, errors: [
-            {message: `mode: Expected a value, got null`},
-          ]}}}});
-});
diff --git a/test/unit/data/composite/data/withPropertiesFromObject.js b/test/unit/data/composite/data/withPropertiesFromObject.js
deleted file mode 100644
index b81d51a5..00000000
--- a/test/unit/data/composite/data/withPropertiesFromObject.js
+++ /dev/null
@@ -1,241 +0,0 @@
-import t from 'tap';
-import {quickCheckCompositeOutputs} from '#test-lib';
-
-import {compositeFrom, input} from '#composite';
-import {exposeDependency} from '#composite/control-flow';
-import {withPropertiesFromObject} from '#composite/data';
-
-const composite = compositeFrom({
-  compose: false,
-
-  steps: [
-    withPropertiesFromObject({
-      object: 'object',
-      properties: 'properties',
-    }),
-
-    exposeDependency({dependency: '#object'}),
-  ],
-});
-
-t.test(`withPropertiesFromObject: basic behavior`, t => {
-  t.plan(4);
-
-  t.match(composite, {
-    expose: {
-      dependencies: ['object', 'properties'],
-    },
-  });
-
-  t.same(
-    composite.expose.compute({
-      object: {foo: 'bar', bim: 'BOOM', bam: 'baz'},
-      properties: ['foo', 'bim'],
-    }),
-    {foo: 'bar', bim: 'BOOM'});
-
-  t.same(
-    composite.expose.compute({
-      object: {value1: 'uwah', value2: 'arah'},
-      properties: ['value1', 'value3'],
-    }),
-    {value1: 'uwah', value3: null});
-
-  t.same(
-    composite.expose.compute({
-      object: null,
-      properties: ['ohMe', 'ohMy', 'ohDear'],
-    }),
-    {ohMe: null, ohMy: null, ohDear: null});
-});
-
-t.test(`withPropertiesFromObject: output shapes & values`, t => {
-  t.plan(2 * 2 * 3 ** 2);
-
-  const dependencies = {
-    ['object_dependency']:
-      {foo: 'apple', bar: 'banana', baz: 'orange'},
-    [input('object_neither')]:
-      {foo: 'koala', bar: 'okapi', baz: 'mongoose'},
-    ['properties_dependency']:
-      ['foo', 'bar', 'missing1'],
-    [input('properties_neither')]:
-      ['foo', 'baz', 'missing3'],
-  };
-
-  const qcco = quickCheckCompositeOutputs(t, dependencies);
-
-  const mapLevel1 = [
-    [input.value('prefix_value'), [
-      ['object_dependency', [
-        ['properties_dependency', {
-          '#object': {foo: 'apple', bar: 'banana', missing1: null},
-        }],
-        [input.value(['bar', 'baz', 'missing2']), {
-          '#prefix_value.bar': 'banana',
-          '#prefix_value.baz': 'orange',
-          '#prefix_value.missing2': null,
-        }],
-        [input('properties_neither'), {
-          '#object': {foo: 'apple', baz: 'orange', missing3: null},
-        }]]],
-
-      [input.value({foo: 'ouh', bar: 'rah', baz: 'nyu'}), [
-        ['properties_dependency', {
-          '#object': {foo: 'ouh', bar: 'rah', missing1: null},
-        }],
-        [input.value(['bar', 'baz', 'missing2']), {
-          '#prefix_value.bar': 'rah',
-          '#prefix_value.baz': 'nyu',
-          '#prefix_value.missing2': null,
-        }],
-        [input('properties_neither'), {
-          '#object': {foo: 'ouh', baz: 'nyu', missing3: null},
-        }]]],
-
-      [input('object_neither'), [
-        ['properties_dependency', {
-          '#object': {foo: 'koala', bar: 'okapi', missing1: null},
-        }],
-        [input.value(['bar', 'baz', 'missing2']), {
-          '#prefix_value.bar': 'okapi',
-          '#prefix_value.baz': 'mongoose',
-          '#prefix_value.missing2': null,
-        }],
-        [input('properties_neither'), {
-          '#object': {foo: 'koala', baz: 'mongoose', missing3: null},
-        }]]]]],
-
-    [input.value(null), [
-      ['object_dependency', [
-        ['properties_dependency', {
-          '#object': {foo: 'apple', bar: 'banana', missing1: null},
-        }],
-        [input.value(['bar', 'baz', 'missing2']), {
-          '#object_dependency.bar': 'banana',
-          '#object_dependency.baz': 'orange',
-          '#object_dependency.missing2': null,
-        }],
-        [input('properties_neither'), {
-          '#object': {foo: 'apple', baz: 'orange', missing3: null},
-        }]]],
-
-      [input.value({foo: 'ouh', bar: 'rah', baz: 'nyu'}), [
-        ['properties_dependency', {
-          '#object': {foo: 'ouh', bar: 'rah', missing1: null},
-        }],
-        [input.value(['bar', 'baz', 'missing2']), {
-          '#object.bar': 'rah',
-          '#object.baz': 'nyu',
-          '#object.missing2': null,
-        }],
-        [input('properties_neither'), {
-          '#object': {foo: 'ouh', baz: 'nyu', missing3: null},
-        }]]],
-
-      [input('object_neither'), [
-        ['properties_dependency', {
-          '#object': {foo: 'koala', bar: 'okapi', missing1: null},
-        }],
-        [input.value(['bar', 'baz', 'missing2']), {
-          '#object.bar': 'okapi',
-          '#object.baz': 'mongoose',
-          '#object.missing2': null,
-        }],
-        [input('properties_neither'), {
-          '#object': {foo: 'koala', baz: 'mongoose', missing3: null},
-        }]]]]],
-  ];
-
-  for (const [prefixInput, mapLevel2] of mapLevel1) {
-    for (const [objectInput, mapLevel3] of mapLevel2) {
-      for (const [propertiesInput, outputDict] of mapLevel3) {
-        const step = withPropertiesFromObject({
-          prefix: prefixInput,
-          object: objectInput,
-          properties: propertiesInput,
-        });
-
-        qcco(step, outputDict);
-      }
-    }
-  }
-});
-
-t.test(`withPropertiesFromObject: validate static inputs`, t => {
-  t.plan(3);
-
-  t.throws(
-    () => withPropertiesFromObject({}),
-    {message: `Errors in input options passed to withPropertiesFromObject`, errors: [
-      {message: `Required these inputs: object, properties`},
-    ]});
-
-  t.throws(
-    () => withPropertiesFromObject({
-      object: input.value('intriguing'),
-      properties: input.value('very'),
-      prefix: input.value({yes: 'yup'}),
-    }),
-    {message: `Errors in input options passed to withPropertiesFromObject`, errors: [
-      {message: `object: Expected an object, got string`},
-      {message: `properties: Expected an array, got string`},
-      {message: `prefix: Expected a string, got object`},
-    ]});
-
-  t.throws(
-    () => withPropertiesFromObject({
-      object: input.value([['abc', 1], ['def', 2], [123, 3]]),
-      properties: input.value(['abc', 'def', 123]),
-    }),
-    {message: `Errors in input options passed to withPropertiesFromObject`, errors: [
-      {message: `object: Expected an object, got array`},
-      {message: `properties: Errors validating array items`, errors: [
-        {
-          [Symbol.for('hsmusic.annotateError.indexInSourceArray')]: 2,
-          message: `Error at zero-index 2: 123`,
-          cause: {
-            message: `Expected a string, got number`,
-          },
-        },
-      ]},
-    ]});
-});
-
-t.test(`withPropertiesFromObject: validate dynamic inputs`, t => {
-  t.plan(2);
-
-  t.throws(
-    () => composite.expose.compute({
-      object: 'intriguing',
-      properties: 'onceMore',
-    }),
-    {message: `Error computing composition`, cause:
-      {message: `Error in step 1 of 2, withPropertiesFromObject`, cause:
-        {message: `Error computing composition withPropertiesFromObject`, cause:
-          {message: `Errors in input values provided to withPropertiesFromObject`, errors: [
-            {message: `object: Expected an object, got string`},
-            {message: `properties: Expected an array, got string`},
-          ]}}}});
-
-  t.throws(
-    () => composite.expose.compute({
-      object: [['abc', 1], ['def', 2], [123, 3]],
-      properties: ['abc', 'def', 123],
-    }),
-    {message: `Error computing composition`, cause:
-      {message: `Error in step 1 of 2, withPropertiesFromObject`, cause:
-        {message: `Error computing composition withPropertiesFromObject`, cause:
-          {message: `Errors in input values provided to withPropertiesFromObject`, errors: [
-            {message: `object: Expected an object, got array`},
-            {message: `properties: Errors validating array items`, errors: [
-              {
-                [Symbol.for('hsmusic.annotateError.indexInSourceArray')]: 2,
-                message: `Error at zero-index 2: 123`,
-                cause: {
-                  message: `Expected a string, got number`,
-                },
-              },
-            ]},
-          ]}}}});
-});
diff --git a/test/unit/data/composite/data/withPropertyFromObject.js b/test/unit/data/composite/data/withPropertyFromObject.js
deleted file mode 100644
index 068932e2..00000000
--- a/test/unit/data/composite/data/withPropertyFromObject.js
+++ /dev/null
@@ -1,195 +0,0 @@
-import t from 'tap';
-import {quickCheckCompositeOutputs} from '#test-lib';
-
-import CacheableObject from '#cacheable-object';
-import {compositeFrom, input} from '#composite';
-import {exposeDependency} from '#composite/control-flow';
-import {withPropertyFromObject} from '#composite/data';
-
-t.test(`withPropertyFromObject: basic behavior`, t => {
-  t.plan(4);
-
-  const composite = compositeFrom({
-    compose: false,
-
-    steps: [
-      withPropertyFromObject({
-        object: 'object',
-        property: 'property',
-      }),
-
-      exposeDependency({dependency: '#value'}),
-    ],
-  });
-
-  t.match(composite, {
-    expose: {
-      dependencies: ['object', 'property'],
-    },
-  });
-
-  t.equal(composite.expose.compute({
-    object: {foo: 'bar', bim: 'BOOM'},
-    property: 'bim',
-  }), 'BOOM');
-
-  t.equal(composite.expose.compute({
-    object: {value1: 'uwah'},
-    property: 'value2',
-  }), null);
-
-  t.equal(composite.expose.compute({
-    object: null,
-    property: 'oml where did me object go',
-  }), null);
-});
-
-t.test(`withPropertyFromObject: "internal" input`, t => {
-  t.plan(7);
-
-  const composite = compositeFrom({
-    compose: false,
-
-    steps: [
-      withPropertyFromObject({
-        object: 'object',
-        property: 'property',
-        internal: 'internal',
-      }),
-
-      exposeDependency({dependency: '#value'}),
-    ],
-  });
-
-  const constructor = class extends CacheableObject {
-    static [CacheableObject.propertyDescriptors] = {
-      foo: {
-        flags: {update: true, expose: false},
-      },
-
-      bar: {
-        flags: {update: true, expose: true},
-      },
-
-      baz: {
-        flags: {update: true, expose: true},
-        expose: {
-          transform: baz => baz * 2,
-        },
-      },
-    };
-  };
-
-  constructor.finalizeCacheableObjectPrototype();
-
-  const thing = Reflect.construct(constructor, []);
-
-  thing.foo = 100;
-  thing.bar = 200;
-  thing.baz = 300;
-
-  t.match(composite, {
-    expose: {
-      dependencies: ['object', 'property', 'internal'],
-    },
-  });
-
-  t.equal(composite.expose.compute({
-    object: thing,
-    property: 'foo',
-    internal: true,
-  }), 100);
-
-  t.equal(composite.expose.compute({
-    object: thing,
-    property: 'bar',
-    internal: true,
-  }), 200);
-
-  t.equal(composite.expose.compute({
-    object: thing,
-    property: 'baz',
-    internal: true,
-  }), 300);
-
-  t.equal(composite.expose.compute({
-    object: thing,
-    property: 'baz',
-    internal: false,
-  }), 600);
-
-  t.equal(composite.expose.compute({
-    object: thing,
-    property: 'bimbam',
-    internal: false,
-  }), null);
-
-  t.equal(composite.expose.compute({
-    object: null,
-    property: 'bambim',
-    internal: false,
-  }), null);
-});
-
-t.test(`withPropertyFromObject: output shapes & values`, t => {
-  t.plan(2 * 3 ** 2);
-
-  const dependencies = {
-    ['object_dependency']:
-      {foo: 'apple', bar: 'banana', baz: 'orange'},
-    [input('object_neither')]:
-      {foo: 'koala', bar: 'okapi', baz: 'mongoose'},
-    ['property_dependency']:
-      'foo',
-    [input('property_neither')]:
-      'baz',
-  };
-
-  const qcco = quickCheckCompositeOutputs(t, dependencies);
-
-  const mapLevel1 = [
-    ['object_dependency', [
-      ['property_dependency', {
-        '#value': 'apple',
-      }],
-      [input.value('bar'), {
-        '#object_dependency.bar': 'banana',
-      }],
-      [input('property_neither'), {
-        '#value': 'orange',
-      }]]],
-
-    [input.value({foo: 'ouh', bar: 'rah', baz: 'nyu'}), [
-      ['property_dependency', {
-        '#value': 'ouh',
-      }],
-      [input.value('bar'), {
-        '#value': 'rah',
-      }],
-      [input('property_neither'), {
-        '#value': 'nyu',
-      }]]],
-
-    [input('object_neither'), [
-      ['property_dependency', {
-        '#value': 'koala',
-      }],
-      [input.value('bar'), {
-        '#value': 'okapi',
-      }],
-      [input('property_neither'), {
-        '#value': 'mongoose',
-      }]]],
-  ];
-
-  for (const [objectInput, mapLevel2] of mapLevel1) {
-    for (const [propertyInput, outputDict] of mapLevel2) {
-      const step = withPropertyFromObject({
-        object: objectInput,
-        property: propertyInput,
-      });
-
-      qcco(step, outputDict);
-    }
-  }
-});
diff --git a/test/unit/data/composite/data/withUniqueItemsOnly.js b/test/unit/data/composite/data/withUniqueItemsOnly.js
deleted file mode 100644
index 50b16f43..00000000
--- a/test/unit/data/composite/data/withUniqueItemsOnly.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import t from 'tap';
-import {quickCheckCompositeOutputs} from '#test-lib';
-
-import {compositeFrom, input} from '#composite';
-import {exposeDependency} from '#composite/control-flow';
-import {withUniqueItemsOnly} from '#composite/data';
-
-t.test(`withUniqueItemsOnly: basic behavior`, t => {
-  t.plan(3);
-
-  const composite = compositeFrom({
-    compose: false,
-
-    steps: [
-      withUniqueItemsOnly({
-        list: 'list',
-      }),
-
-      exposeDependency({dependency: '#list'}),
-    ],
-  });
-
-  t.match(composite, {
-    expose: {
-      dependencies: ['list'],
-    },
-  });
-
-  t.same(composite.expose.compute({
-    list: ['apple', 'banana', 'banana', 'banana', 'apple', 'watermelon'],
-  }), ['apple', 'banana', 'watermelon']);
-
-  t.same(composite.expose.compute({
-    list: [],
-  }), []);
-});
-
-t.test(`withUniqueItemsOnly: output shapes & values`, t => {
-  t.plan(2 * 3 ** 1);
-
-  const dependencies = {
-    ['list_dependency']:
-      [1, 1, 2, 3, 3, 4, 'foo', false, false, 4],
-    [input('list_neither')]:
-      [8, 8, 7, 6, 6, 5, 'bar', true, true, 5],
-  };
-
-  const qcco = quickCheckCompositeOutputs(t, dependencies);
-
-  const mapLevel1 = [
-    ['list_dependency', {
-      '#list_dependency': [1, 2, 3, 4, 'foo', false],
-    }],
-    [input.value([-1, -1, 'interesting', 'very', 'interesting']), {
-      '#uniqueItems': [-1, 'interesting', 'very'],
-    }],
-    [input('list_neither'), {
-      '#uniqueItems': [8, 7, 6, 5, 'bar', true],
-    }],
-  ];
-
-  for (const [listInput, outputDict] of mapLevel1) {
-    const step = withUniqueItemsOnly({
-      list: listInput,
-    });
-
-    qcco(step, outputDict);
-  }
-});
diff --git a/test/unit/data/validators.js b/test/unit/data/validators.js
index 02f94866..6e37f170 100644
--- a/test/unit/data/validators.js
+++ b/test/unit/data/validators.js
@@ -17,7 +17,6 @@ import {
 
   // Wiki data
   isColor,
-  isCommentary,
   isContentString,
   isContribution,
   isContributionList,
@@ -152,21 +151,6 @@ t.test('isColor', t => {
   t.throws(() => isColor('hsl(150deg 30% 60%)'), TypeError);
 });
 
-t.test('isCommentary', t => {
-  t.plan(9);
-
-  // TODO: Test specific error messages.
-  t.ok(isCommentary(`<i>Toby Fox:</i>\ndogsong.mp3`));
-  t.ok(isCommentary(`<i>Toby Fox:</i> (music)\ndogsong.mp3`));
-  t.throws(() => isCommentary(`dogsong.mp3\n<i>Toby Fox:</i>\ndogsong.mp3`));
-  t.throws(() => isCommentary(`<i>Toby Fox:</i> dogsong.mp3`));
-  t.throws(() => isCommentary(`<i>Toby Fox:</i> (music) dogsong.mp3`));
-  t.throws(() => isCommentary(`<i>I Have Nothing To Say:</i>`));
-  t.throws(() => isCommentary(123));
-  t.throws(() => isCommentary(``));
-  t.throws(() => isCommentary(`Technically, ah, er:</i>\nCorrect`));
-});
-
 t.test('isContentString', t => {
   t.plan(12);