UNPKG

@factorialco/shadowdog

Version:

<img src="https://raw.githubusercontent.com/factorialco/shadowdog/refs/heads/main/logo.png" alt="drawing" width="100"/>

233 lines (232 loc) 11.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const vitest_1 = require("vitest"); const generate_1 = require("./generate"); const events_1 = require("./events"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const os = __importStar(require("os")); // Helper function to create a minimal watcher config const createWatcher = (overrides = {}) => ({ enabled: true, files: [], environment: [], ignored: [], commands: [], ...overrides, }); // Helper function to create a minimal config const createConfig = (overrides = {}) => ({ debounceTime: 100, plugins: [], defaultIgnoredFiles: [], watchers: [], ...overrides, }); (0, vitest_1.describe)('generate', () => { let testDir; (0, vitest_1.beforeEach)(async () => { testDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'shadowdog-test-')); process.chdir(testDir); }); (0, vitest_1.afterEach)(async () => { if (testDir && fs.existsSync(testDir)) { await fs.promises.rm(testDir, { recursive: true, force: true }); } }); (0, vitest_1.describe)('artifact verification', () => { (0, vitest_1.it)('should wait for artifacts to be created and readable', async () => { const config = createConfig({ watchers: [ createWatcher({ files: ['input.txt'], commands: [ { command: "node -e \"setTimeout(() => require('fs').writeFileSync('output.txt', 'delayed content'), 200)\"", workingDirectory: '', tags: [], artifacts: [{ output: 'output.txt' }], }, ], }), ], }); // Create input file await fs.promises.writeFile('input.txt', 'test input'); await (0, generate_1.generate)(config, new events_1.ShadowdogEventEmitter(), { continueOnError: false }); // Verify the artifact was created (0, vitest_1.expect)(fs.existsSync('output.txt')).toBe(true); const content = await fs.promises.readFile('output.txt', 'utf-8'); (0, vitest_1.expect)(content).toBe('delayed content'); }, 10000); (0, vitest_1.it)('should throw error if artifact is not created after max retries', async () => { // Set a very low retry count for this test const originalEnv = process.env.SHADOWDOG_ARTIFACT_WAIT_MAX_RETRIES; process.env.SHADOWDOG_ARTIFACT_WAIT_MAX_RETRIES = '5'; try { const config = createConfig({ watchers: [ createWatcher({ files: ['input.txt'], commands: [ { command: 'node -e "console.log(\'Command completed but no artifact created\')"', workingDirectory: '', tags: [], artifacts: [{ output: 'output.txt' }], }, ], }), ], }); // Create input file await fs.promises.writeFile('input.txt', 'test input'); await (0, vitest_1.expect)((0, generate_1.generate)(config, new events_1.ShadowdogEventEmitter(), { continueOnError: false })).rejects.toThrow(/Artifact 'output\.txt' was not created or is not readable after task completion/); } finally { if (originalEnv !== undefined) { process.env.SHADOWDOG_ARTIFACT_WAIT_MAX_RETRIES = originalEnv; } else { delete process.env.SHADOWDOG_ARTIFACT_WAIT_MAX_RETRIES; } } }, 10000); (0, vitest_1.it)('should handle dependency chains correctly with artifact verification', async () => { // Create source file await fs.promises.writeFile('source.rb', 'permissions updated'); const config = createConfig({ watchers: [ createWatcher({ files: ['source.rb'], commands: [ { command: "node -e \"const fs = require('fs'); const content = fs.readFileSync('source.rb', 'utf-8'); fs.writeFileSync('intermediate.json', 'generated from ' + content.trim())\"", workingDirectory: '', tags: [], artifacts: [{ output: 'intermediate.json' }], }, ], }), ], }); await (0, generate_1.generate)(config, new events_1.ShadowdogEventEmitter(), { continueOnError: false }); // Verify the first artifact was created (0, vitest_1.expect)(fs.existsSync('intermediate.json')).toBe(true); const intermediateContent = await fs.promises.readFile('intermediate.json', 'utf-8'); (0, vitest_1.expect)(intermediateContent).toBe('generated from permissions updated'); // Now test that a dependent task can run after the first one completes const config2 = createConfig({ watchers: [ createWatcher({ files: ['intermediate.json'], commands: [ { command: "node -e \"const fs = require('fs'); const content = fs.readFileSync('intermediate.json', 'utf-8'); fs.writeFileSync('final.graphql', 'schema from ' + content)\"", workingDirectory: '', tags: [], artifacts: [{ output: 'final.graphql' }], }, ], }), ], }); await (0, generate_1.generate)(config2, new events_1.ShadowdogEventEmitter(), { continueOnError: false }); // Verify the final artifact was created (0, vitest_1.expect)(fs.existsSync('final.graphql')).toBe(true); const finalContent = await fs.promises.readFile('final.graphql', 'utf-8'); (0, vitest_1.expect)(finalContent).toBe('schema from generated from permissions updated'); }, 10000); (0, vitest_1.it)('should not fail if artifact does not exist initially', async () => { const config = createConfig({ watchers: [ createWatcher({ files: ['input.txt'], commands: [ { command: "node -e \"require('fs').writeFileSync('output.txt', 'content')\"", workingDirectory: '', tags: [], artifacts: [{ output: 'output.txt' }], }, ], }), ], }); // Create input file but no existing artifact await fs.promises.writeFile('input.txt', 'test input'); // Should not throw an error await (0, vitest_1.expect)((0, generate_1.generate)(config, new events_1.ShadowdogEventEmitter(), { continueOnError: false })).resolves.not.toThrow(); // Verify the artifact was created (0, vitest_1.expect)(fs.existsSync('output.txt')).toBe(true); const content = await fs.promises.readFile('output.txt', 'utf-8'); (0, vitest_1.expect)(content).toBe('content'); }, 10000); (0, vitest_1.it)('should reject empty files during verification', async () => { // Set a very low retry count for this test const originalEnv = process.env.SHADOWDOG_ARTIFACT_WAIT_MAX_RETRIES; process.env.SHADOWDOG_ARTIFACT_WAIT_MAX_RETRIES = '5'; try { const config = createConfig({ watchers: [ createWatcher({ files: ['input.txt'], commands: [ { command: "node -e \"require('fs').writeFileSync('empty-output.txt', '')\"", workingDirectory: '', tags: [], artifacts: [{ output: 'empty-output.txt' }], }, ], }), ], }); // Create input file await fs.promises.writeFile('input.txt', 'test input'); await (0, vitest_1.expect)((0, generate_1.generate)(config, new events_1.ShadowdogEventEmitter(), { continueOnError: false })).rejects.toThrow(/Artifact 'empty-output\.txt' was not created or is not readable after task completion/); } finally { if (originalEnv !== undefined) { process.env.SHADOWDOG_ARTIFACT_WAIT_MAX_RETRIES = originalEnv; } else { delete process.env.SHADOWDOG_ARTIFACT_WAIT_MAX_RETRIES; } } }, 10000); }); });