@ehmpathy/as-command
Version:
easily create commands within a pit-of-success
293 lines • 17 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_fs_1 = require("node:fs");
const promises_1 = require("node:fs/promises");
const node_path_1 = require("node:path");
const test_fns_1 = require("test-fns");
const asCommand_1 = require("./asCommand");
describe('asCommand (integration)', () => {
const testDir = (0, node_path_1.resolve)(__dirname, '.temp/asCommand');
(0, test_fns_1.given)('a command created with asCommand', () => {
const realLog = {
debug: (message, metadata) => {
console.debug(message, metadata);
},
info: (message, metadata) => {
console.info(message, metadata);
},
warn: (message, metadata) => {
console.warn(message, metadata);
},
error: (message, metadata) => {
console.error(message, metadata);
},
};
(0, test_fns_1.when)('invoked with input', () => {
(0, test_fns_1.then)('it should execute the logic and persist logs and outputs to disk', () => __awaiter(void 0, void 0, void 0, function* () {
// Create a simple command
const testCommand = (0, asCommand_1.asCommand)({
name: 'double-value',
purpose: 'doubles a number',
stage: 'test',
log: realLog,
dir: testDir,
}, (input, context) => __awaiter(void 0, void 0, void 0, function* () {
context.log.info('processing', { input });
return { doubled: input.value * 2 };
}));
// Execute the command
const result = yield testCommand({ value: 21 });
// Verify the result
expect(result).toEqual({ doubled: 42 });
// Verify directory structure was created
const commandDir = `${testDir}/__tmp__/test/double-value`;
expect((0, node_fs_1.existsSync)(commandDir)).toBe(true);
// Find the run directory
const runDirs = yield (0, promises_1.readdir)(commandDir);
expect(runDirs.length).toBeGreaterThan(0);
const runDir = `${commandDir}/${runDirs[0]}`;
const logFilePath = `${runDir}/log.json`;
const outFilePath = `${runDir}/out.json`;
expect((0, node_fs_1.existsSync)(logFilePath)).toBe(true);
expect((0, node_fs_1.existsSync)(outFilePath)).toBe(true);
// Read and verify log file contents
const logContents = yield (0, promises_1.readFile)(logFilePath, 'utf-8');
expect(logContents).toContain('"level": "info"');
expect(logContents).toContain('"message": "input"');
expect(logContents).toContain('"value": 21');
expect(logContents).toContain('"message": "processing"');
expect(logContents).toContain('"message": "output.result"');
expect(logContents).toContain('"doubled": 42');
// Read and verify output file contents
const outContents = yield (0, promises_1.readFile)(outFilePath, 'utf-8');
const output = JSON.parse(outContents);
expect(output).toEqual({ doubled: 42 });
}));
});
(0, test_fns_1.when)('the logic uses context.out.write', () => {
(0, test_fns_1.then)('it should write custom output files to disk', () => __awaiter(void 0, void 0, void 0, function* () {
const testCommand = (0, asCommand_1.asCommand)({
name: 'greet-user',
purpose: 'greets a user',
stage: 'test',
log: realLog,
dir: testDir,
}, (input, context) => __awaiter(void 0, void 0, void 0, function* () {
// Write a custom file using context.out.write
const csvData = `name,greeting\n${input.name},Hello ${input.name}!`;
const result = yield context.out.write({
name: 'greeting.csv',
data: csvData,
});
context.log.info('wrote csv file', { path: result.path });
return { message: `Greeted ${input.name}` };
}));
const result = yield testCommand({ name: 'Alice' });
expect(result).toEqual({ message: 'Greeted Alice' });
// Verify custom output file was created
const commandDir = `${testDir}/__tmp__/test/greet-user`;
const runDirs = yield (0, promises_1.readdir)(commandDir);
const runDir = `${commandDir}/${runDirs[0]}`;
const csvFilePath = `${runDir}/greeting.csv`;
expect((0, node_fs_1.existsSync)(csvFilePath)).toBe(true);
const csvContents = yield (0, promises_1.readFile)(csvFilePath, 'utf-8');
expect(csvContents).toContain('name,greeting');
expect(csvContents).toContain('Alice,Hello Alice!');
}));
});
(0, test_fns_1.when)('the logic uses context.out.write with nested directories', () => {
(0, test_fns_1.then)('it should create nested directories and write the file', () => __awaiter(void 0, void 0, void 0, function* () {
const testCommand = (0, asCommand_1.asCommand)({
name: 'nested-output',
purpose: 'writes to nested directories',
stage: 'test',
log: realLog,
dir: testDir,
}, (input, context) => __awaiter(void 0, void 0, void 0, function* () {
// Write a file to a nested path that doesn't exist yet
const result = yield context.out.write({
name: 'reports/analytics/data.json',
data: JSON.stringify({ value: input.data }, null, 2),
});
context.log.info('wrote nested file', { path: result.path });
return { saved: true };
}));
const result = yield testCommand({ data: 'test-data' });
expect(result).toEqual({ saved: true });
// Verify the file was created in the nested directory
const commandDir = `${testDir}/__tmp__/test/nested-output`;
const runDirs = yield (0, promises_1.readdir)(commandDir);
const runDir = `${commandDir}/${runDirs[0]}`;
const nestedFilePath = `${runDir}/reports/analytics/data.json`;
expect((0, node_fs_1.existsSync)(nestedFilePath)).toBe(true);
const fileContents = yield (0, promises_1.readFile)(nestedFilePath, 'utf-8');
const parsed = JSON.parse(fileContents);
expect(parsed).toEqual({ value: 'test-data' });
}));
});
(0, test_fns_1.when)('the logic throws an error', () => {
(0, test_fns_1.then)('it should log the error and rethrow it', () => __awaiter(void 0, void 0, void 0, function* () {
const testCommand = (0, asCommand_1.asCommand)({
name: 'failing-command',
purpose: 'intentionally fails',
stage: 'test',
log: realLog,
dir: testDir,
}, (input) => __awaiter(void 0, void 0, void 0, function* () {
if (input.shouldFail) {
throw new Error('Intentional failure');
}
throw new Error('Should not reach here');
}));
// Execute and expect error
const error = yield (0, test_fns_1.getError)(() => testCommand({ shouldFail: true }));
expect(error === null || error === void 0 ? void 0 : error.message).toContain('Intentional failure');
// Verify error was logged to file
const commandDir = `${testDir}/__tmp__/test/failing-command`;
const runDirs = yield (0, promises_1.readdir)(commandDir);
const runDir = `${commandDir}/${runDirs[0]}`;
const logFilePath = `${runDir}/log.json`;
expect((0, node_fs_1.existsSync)(logFilePath)).toBe(true);
const logContents = yield (0, promises_1.readFile)(logFilePath, 'utf-8');
expect(logContents).toContain('"level": "error"');
expect(logContents).toContain('"message": "output.error"');
expect(logContents).toContain('Intentional failure');
}));
});
(0, test_fns_1.when)('invoked multiple times with same input', () => {
(0, test_fns_1.then)('it should create separate log files with same input hash but different timestamps', () => __awaiter(void 0, void 0, void 0, function* () {
const testCommand = (0, asCommand_1.asCommand)({
name: 'echo-command',
purpose: 'echoes input',
stage: 'test',
log: realLog,
dir: testDir,
}, (input) => __awaiter(void 0, void 0, void 0, function* () {
// Add small delay to ensure different timestamps
yield new Promise((resolveTimer) => setTimeout(resolveTimer, 1100));
return { result: input.value };
}));
// Execute twice with same input
yield testCommand({ value: 'test' });
yield testCommand({ value: 'test' });
// Verify multiple run directories exist
const commandDir = `${testDir}/__tmp__/test/echo-command`;
const runDirs = yield (0, promises_1.readdir)(commandDir);
expect(runDirs.length).toBeGreaterThanOrEqual(2);
// Verify directories have same hash but different timestamps
const hashes = runDirs.map((d) => d.split('.')[2]);
const timestamps = runDirs.map((d) => d.split('.').slice(0, 2).join('.'));
expect(hashes[0]).toBe(hashes[1]); // Same input hash
expect(timestamps[0]).not.toBe(timestamps[1]); // Different timestamps
}));
});
(0, test_fns_1.when)('invoked with different inputs', () => {
(0, test_fns_1.then)('it should create files with different input hashes', () => __awaiter(void 0, void 0, void 0, function* () {
const testCommand = (0, asCommand_1.asCommand)({
name: 'process-id',
purpose: 'processes an id',
stage: 'test',
log: realLog,
dir: testDir,
}, () => __awaiter(void 0, void 0, void 0, function* () {
return { processed: true };
}));
// Execute with different inputs
yield testCommand({ id: 1 });
yield testCommand({ id: 2 });
// Verify run directories have different hashes
const commandDir = `${testDir}/__tmp__/test/process-id`;
const runDirs = yield (0, promises_1.readdir)(commandDir);
expect(runDirs.length).toBeGreaterThanOrEqual(2);
const hashes = runDirs.map((d) => d.split('.')[2]);
expect(hashes[0]).not.toBe(hashes[1]); // Different input hashes
}));
});
(0, test_fns_1.when)('the logic uses all log levels', () => {
(0, test_fns_1.then)('it should persist all log levels to the log file', () => __awaiter(void 0, void 0, void 0, function* () {
const testCommand = (0, asCommand_1.asCommand)({
name: 'all-log-levels',
purpose: 'tests all log levels',
stage: 'test',
log: realLog,
dir: testDir,
}, (_input, context) => __awaiter(void 0, void 0, void 0, function* () {
context.log.debug('debug message', { level: 'debug' });
context.log.info('info message', { level: 'info' });
context.log.warn('warn message', { level: 'warn' });
context.log.error('error message', { level: 'error' });
return { complete: true };
}));
yield testCommand({});
const commandDir = `${testDir}/__tmp__/test/all-log-levels`;
const runDirs = yield (0, promises_1.readdir)(commandDir);
const runDir = `${commandDir}/${runDirs[0]}`;
const logFilePath = `${runDir}/log.json`;
expect((0, node_fs_1.existsSync)(logFilePath)).toBe(true);
const logContents = yield (0, promises_1.readFile)(logFilePath, 'utf-8');
expect(logContents).toContain('"level": "debug"');
expect(logContents).toContain('"message": "debug message"');
expect(logContents).toContain('"level": "info"');
expect(logContents).toContain('"message": "info message"');
expect(logContents).toContain('"level": "warn"');
expect(logContents).toContain('"message": "warn message"');
expect(logContents).toContain('"level": "error"');
expect(logContents).toContain('"message": "error message"');
}));
});
(0, test_fns_1.when)('the logic returns a string', () => {
(0, test_fns_1.then)('it should persist the string directly to the output file', () => __awaiter(void 0, void 0, void 0, function* () {
const testCommand = (0, asCommand_1.asCommand)({
name: 'return-string',
purpose: 'returns a string',
stage: 'test',
log: realLog,
dir: testDir,
}, (input) => __awaiter(void 0, void 0, void 0, function* () {
return `Hello, ${input.name}!`;
}));
const result = yield testCommand({ name: 'World' });
expect(result).toBe('Hello, World!');
const commandDir = `${testDir}/__tmp__/test/return-string`;
const runDirs = yield (0, promises_1.readdir)(commandDir);
const runDir = `${commandDir}/${runDirs[0]}`;
const outFilePath = `${runDir}/out.json`;
expect((0, node_fs_1.existsSync)(outFilePath)).toBe(true);
const outContents = yield (0, promises_1.readFile)(outFilePath, 'utf-8');
expect(outContents).toBe('Hello, World!');
}));
});
(0, test_fns_1.when)('the logic returns undefined', () => {
(0, test_fns_1.then)('it should persist "undefined" to the output file', () => __awaiter(void 0, void 0, void 0, function* () {
const testCommand = (0, asCommand_1.asCommand)({
name: 'return-undefined',
purpose: 'returns undefined',
stage: 'test',
log: realLog,
dir: testDir,
}, () => __awaiter(void 0, void 0, void 0, function* () {
return undefined;
}));
const result = yield testCommand({});
expect(result).toBeUndefined();
const commandDir = `${testDir}/__tmp__/test/return-undefined`;
const runDirs = yield (0, promises_1.readdir)(commandDir);
const runDir = `${commandDir}/${runDirs[0]}`;
const outFilePath = `${runDir}/out.json`;
expect((0, node_fs_1.existsSync)(outFilePath)).toBe(true);
const outContents = yield (0, promises_1.readFile)(outFilePath, 'utf-8');
expect(outContents).toBe('undefined');
}));
});
});
});
//# sourceMappingURL=asCommand.integration.test.js.map