base-data-manager/test/task.ts
cobertos f6d0427a45 Converted TaskTargetPipelineHelper to more functional style, added aggregate() functionality to bring together multiple exports (no tests, but works)
* made parallel generic (not tied to TaskTarget)
* pulled common higher-order/frontend operations into io.ts
* split timelinize specific functionality into own file
* Tests made to pass and match previous facebook export snapshots _exactly_
2026-02-26 00:14:10 -05:00

227 lines
7.5 KiB
TypeScript

import test from "node:test";
import nodePath from "node:path";
import { strict as assert } from "node:assert/strict";
import {
TaskTarget,
cd,
glob as taskGlob,
read,
cmd,
assignMeta,
verify,
} from "../data-export/task.ts";
const THIS_FILE = import.meta.dirname;
const FIXTURE_DIR = nodePath.join(THIS_FILE, 'fixtures/facebook-json-2021-05-01');
const FIXTURE_FILE = nodePath.join(FIXTURE_DIR, 'friends/friends.json');
// -- TaskTarget ---------------------------------------------------------------
test("TaskTarget: constructor initializes path, pipeline", () => {
const t = new TaskTarget("/foo/bar");
assert.equal(t.path, "/foo/bar");
assert.deepEqual(t.pipeline, []);
});
test("TaskTarget: exists() returns true for a real file", () => {
assert.equal(new TaskTarget(FIXTURE_FILE).exists(), true);
});
test("TaskTarget: exists() returns false for a missing file", () => {
assert.equal(new TaskTarget("/nonexistent-file-xyz").exists(), false);
});
test("TaskTarget: basename safe-ifies the path basename", () => {
const t = new TaskTarget("/foo/bar/some-file.txt");
assert.equal(t.basename, "some_file_txt");
});
test("TaskTarget: basenameN returns last n path segments joined with ___", () => {
const t = new TaskTarget("/a/b/c/d");
assert.equal(t.basenameN(2), "c___d");
assert.equal(t.basenameN(1), "d");
});
test("TaskTarget: id throws when no idValue is set", () => {
assert.throws(() => new TaskTarget("/foo").id, /must have an id/);
});
test("TaskTarget: id with a string value is safe-ified", () => {
const t = new TaskTarget("/foo").assignMeta({ idValue: "my-id" });
assert.equal(t.id, "my_id");
});
test("TaskTarget: id with a function value is resolved against the target", () => {
const t = new TaskTarget("/foo/bar").assignMeta({ idValue: tgt => tgt.basename });
assert.equal(t.id, "bar");
});
test("TaskTarget: cd with an absolute path replaces the path", () => {
const t = new TaskTarget("/foo");
t.cd("/bar/baz");
assert.equal(t.path, "/bar/baz");
});
test("TaskTarget: cd with a relative path joins with the current path", () => {
const t = new TaskTarget("/foo");
t.cd("bar");
assert.equal(t.path, "/foo/bar");
});
test("TaskTarget: read adds a read op to the pipeline", () => {
const t = new TaskTarget("/foo/bar.txt");
t.read();
assert.equal(t.pipeline.length, 1);
assert.equal(t.pipeline[0].type, "read");
});
test("TaskTarget: cmd adds a mid op to the pipeline", () => {
const t = new TaskTarget("/foo");
t.cmd("jq .");
assert.equal(t.pipeline.length, 1);
assert.equal(t.pipeline[0].type, "mid");
});
test("TaskTarget: pushToPipeline throws if read is not the first op", () => {
const t = new TaskTarget("/foo");
t.cmd("jq .");
assert.throws(() => t.read(), /first item/);
});
test("TaskTarget: clone produces an independent copy", () => {
const t = new TaskTarget("/foo").assignMeta({
idValue: "orig",
columnMeta: ["any"]
});
t.read();
const c = t.clone();
assert.equal(c.path, "/foo");
assert.equal(c.id, "orig");
assert(c.pipeline !== t.pipeline); // Different object references
assert.equal(c.pipeline.length, 1);
assert(c.columnMeta !== t.columnMeta); // Different object references
c.path = "/other";
assert.equal(t.path, "/foo"); // original unchanged
});
test("TaskTarget: glob returns matching TaskTargets from disk", () => {
const t = new TaskTarget(FIXTURE_DIR);
const results = t.glob("friends/*.json");
assert.ok(results.length > 0);
assert.ok(results.every(r => r instanceof TaskTarget));
assert.ok(results.every(r => r.path.endsWith(".json")));
});
// -- toShell / shEscape -------------------------------------------------------
test("toShell: a single read produces a cat command", () => {
const t = new TaskTarget("/foo/bar.txt");
t.read();
assert.equal(t.toShell(), "cat /foo/bar.txt");
});
test("toShell: read piped into cmd", () => {
const t = new TaskTarget("/foo/bar.txt");
t.read();
t.cmd("jq .");
assert.equal(t.toShell(), "cat /foo/bar.txt | jq .");
});
for (const c of " $!&".split("")) {
test(`toShell: quotes paths that contain ${JSON.stringify(c)}`, () => {
const t = new TaskTarget(`/foo/bar${c}baz.txt`);
t.read();
assert.equal(t.toShell(), `cat $'/foo/bar${c}baz.txt'`);
});
}
test(`toShell: quotes and escapes paths that contain '`, () => {
const t = new TaskTarget(`/foo/bar'baz.txt`);
t.read();
assert.equal(t.toShell(), `cat $'/foo/bar\\'baz.txt'`);
});
test("toShell: cmd with array splits tokens", () => {
const t = new TaskTarget("/foo");
t.cmd(["jq", "."]);
assert.equal(t.toShell(), "jq .");
});
test("toShell: cmd with function resolves at shell-generation time", () => {
const t = new TaskTarget("/foo/bar.json");
t.cmd(tgt => `jq -r .name ${tgt.path}`);
assert.equal(t.toShell(), "jq -r .name /foo/bar.json");
});
// -- module-level functions ---------------------------------------------------
test("cd: clones and changes directory of each target", async () => {
const targets = [new TaskTarget("/a"), new TaskTarget("/b")];
const result = await cd("sub")(targets);
assert.equal(result[0].path, "/a/sub");
assert.equal(result[1].path, "/b/sub");
assert.equal(targets[0].path, "/a"); // originals unchanged
});
test("read: clones and adds a read op to each target", async () => {
const targets = [new TaskTarget("/a.txt"), new TaskTarget("/b.txt")];
const result = await read()(targets);
assert.equal(result[0].pipeline[0].type, "read");
assert.equal(result[1].pipeline[0].type, "read");
assert.equal(targets[0].pipeline.length, 0); // originals unchanged
});
test("cmd: clones and appends a cmd op to each target", async () => {
const targets = [new TaskTarget("/a.txt")];
targets[0].read();
const result = await cmd("jq .")(targets);
assert.equal(result[0].pipeline.length, 2);
assert.equal(targets[0].pipeline.length, 1); // original unchanged
});
test("assignMeta: clones and sets meta on each target", async () => {
const targets = [new TaskTarget("/a"), new TaskTarget("/b")];
const result = await assignMeta({ idValue: "myid" })(targets);
assert.equal(result[0].id, "myid");
assert.equal(result[1].id, "myid");
assert.throws(() => targets[0].id); // originals have no id
});
test("taskGlob: returns matching targets across all input targets", async () => {
const targets = [new TaskTarget(FIXTURE_DIR)];
const result = await taskGlob("friends/*.json")(targets);
assert.ok(result.length > 0);
assert.ok(result.every(r => r.path.endsWith(".json")));
});
// -- verify -------------------------------------------------------------------
test("verify: removes targets with an empty pipeline", async () => {
const t = new TaskTarget(FIXTURE_FILE);
const result = await verify([t]);
assert.equal(result.length, 0);
});
test("verify: removes targets whose file does not exist", async () => {
const t = new TaskTarget("/nonexistent-file-xyz");
t.read();
const result = await verify([t]);
assert.equal(result.length, 0);
});
test("verify: keeps targets that exist and have a pipeline", async () => {
const t = new TaskTarget(FIXTURE_FILE);
t.read();
const result = await verify([t]);
assert.equal(result.length, 1);
assert.equal(result[0].path, FIXTURE_FILE);
});
test("verify: filters a mixed list to only valid targets", async () => {
const good = new TaskTarget(FIXTURE_FILE); good.read();
const noPipeline = new TaskTarget(FIXTURE_FILE);
const noFile = new TaskTarget("/nonexistent-xyz"); noFile.read();
const result = await verify([good, noPipeline, noFile]);
assert.equal(result.length, 1);
assert.equal(result[0], good);
});