UNPKG

qgenutils

Version:

A security-first Node.js utility library providing authentication, HTTP operations, URL processing, validation, datetime formatting, and template rendering. Designed as a lightweight alternative to heavy npm packages with comprehensive error handling and

335 lines (269 loc) 16.4 kB
# PLAN.md ## Boilerplate command: 'Use the npx quantumagent --task "Any prompt" command to task asynchronous parallel subagents. Check project documentation (AGENTS.md, replit.md) FIRST before attempting this tool usage or making assumptions about failures. The pattern should be : Check user's documented instructions for the tool Use their exact syntax/examples Only investigate other causes if their documented approach fails' ## 🎯 Goal Fix all test import path issues and test failures to achieve complete 00-AGENTS.md compliance. (Brief intent: make tests load reliably in CI/local by fixing module import/missing-package failures at module load time, ensure Jest setup file matches the configured file, and add defensive fallbacks so unit tests run without optional runtime dependencies.) ## 🔨 Required Changes - [ ] Create new files - [ ] tests/setup.js (no-op CommonJS Jest setup shim) - [ ] Modify existing files - [ ] lib/system/env/getEnvVar.js (make optional dependency loading defensive; prevent require-time crashes from missing qerrors) - [ ] lib/logger.js (make optional winston-daily-rotate-file loading defensive; avoid throwing at require-time and guard transports) - [ ] (Optional) tests/setup.ts — keep or delete; keep if project may use TS setup; no critical change required - [ ] Delete/refactor unnecessary code - [ ] None required — prefer minimal defensive edits rather than large refactors ## 📂 File Plans (Describe changes per file, showing actual code changes) ### File1: lib/system/env/getEnvVar.js js // Original problematic top-level require: // const { qerrors } = require('qerrors'); // // Replace with defensive loader and no-op fallback so module can be required in test envs let qerrors; try { const qerrorsModule = require('qerrors'); // Support both named export and default shape qerrors = qerrorsModule && qerrorsModule.qerrors ? qerrorsModule.qerrors : (qerrorsModule && qerrorsModule.default) ? qerrorsModule.default : qerrorsModule; } catch (err) { // Provide a no-op fallback so tests won't fail if qerrors is absent qerrors = function () { /* no-op error reporter for test envs */ }; } const logger = require('../../logger'); const isValidString = require('../../validation/isValidString'); ... (rest of file unchanged) Notes: - Only the top-of-file require is changed. All original logic is preserved. - Ensure qerrors() can be called safely in catch blocks — it will be a no-op. ### File2: lib/logger.js js // Add defensive require for optional DailyRotateFile const fs = require('fs'); const path = require('path'); const { createLogger, format, transports } = require('winston'); let DailyRotateFile = null; try { DailyRotateFile = require('winston-daily-rotate-file'); } catch (err) { // Optional package is not installed in lightweight test environments DailyRotateFile = null; } // Resolve log dir from optional config, fallback to default let LOG_DIR; try { const localVars = require('loqatevars/config/localVars'); LOG_DIR = localVars.LOG_DIR; } catch (err) { LOG_DIR = null; } const logDir = LOG_DIR || path.join(__dirname, '..', 'logs'); (async () => { try { await fs.promises.mkdir(logDir, { recursive: true }); } catch (error) { // Do not block initialization on directory creation failure } })(); const transportsList = []; // Console transport guard (as before) if (transports && transports.Console && transports.Console.prototype) { transportsList.push(new transports.Console({ level: 'debug', format: format.printf(({ level, message }) => `${level}: ${message}`) })); } // Only add DailyRotateFile if module exists if (DailyRotateFile) { transportsList.push(new DailyRotateFile({ filename: path.join(logDir, 'qgenutils-%DATE%.log'), datePattern: 'YYYY-MM-DD', maxFiles: '14d' })); } else { // Fallback: simple file transport using winston's File if desired, or no file transport in test envs. // Avoid requiring additional modules; keep tests lightweight. } const logger = createLogger({ level: 'info', format: format.combine( format.timestamp(), format.errors({ stack: true }), format.splat(), format.json() ), transports: transportsList }); module.exports = logger; Notes: - Replaced top-level DailyRotateFile require with try/catch and conditional transport inclusion to prevent require errors. - Preserves behavior in environments where the package exists. - Avoids adding new runtime dependencies. ### File3: tests/setup.js (NEW) js // No-op Jest setup for environments without a full src/app implementation. // This prevents test runners from attempting to start an application server // during unit tests that only exercise library code. beforeAll(() => { // Intentionally empty: avoid starting servers in test environment }); afterAll(() => { // Intentionally empty: cleanup is not required here }); Notes: - This file is a CommonJS JavaScript version of the existing tests/setup.ts to satisfy jest.config.js which expects tests/setup.js. - Add to repo root tests/ directory. ### Optional: jest.config.js No change required if current file already points to '<rootDir>/tests/setup.js'. If you prefer to support TypeScript setup directly, update jest.config.js to use setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'] and add ts-jest or ts-node/register configuration. The minimal approach is to add tests/setup.js as shown above. ## 🧱 Implementation Plan & Steps High-level approach: - Make module imports defensive (wrap optional requires with try/catch and provide safe fallbacks). - Add a CommonJS Jest setup file (tests/setup.js) so jest.config.js will find an actual .js file. - Run full test suite, iterate on remaining failing tests (likely local test assumptions) until all pass. - Prefer asynchronous parallelization: split tasks so subagents can perform file edits, run tests, update CI, and run linters concurrently. Important: Use the npx quantumagent --task "..." command to task asynchronous parallel subagents. For each asynchronous task below, a subagent context is provided. Step 1 — Code edits: Make optional-dependency loads defensive (ASYNC/PARALLEL) - Description: Modify lib/system/env/getEnvVar.js and lib/logger.js to avoid throwing when optional packages are not installed in the test environment. - Can be performed asynchronously and in parallel for each file by separate subagents. - Quantumagent command example: npx quantumagent --task "Apply defensive require changes to lib/system/env/getEnvVar.js: wrap qerrors require(...) in try/catch and provide no-op qerrors fallback; keep existing function code intact. Commit changes to branch fix/tests-imports." - Subagent context (for each file): Include the following subsections. Subagent AgetEnvVar.js (Relevant file contents) - File: lib/system/env/getEnvVar.js (only top portion is needed) - Current top lines: const { qerrors } = require('qerrors'); const logger = require('../../logger'); const isValidString = require('../../validation/isValidString'); - Change to: let qerrors; try { const q = require('qerrors'); qerrors = q && q.qerrors ? q.qerrors : (q && q.default) ? q.default : q; } catch (err) { qerrors = function () {}; } Subagent A — Interface contracts - getEnvVar function signature remains: function getEnvVar(varName, defaultValue = undefined, type = 'string') - Any call sites that expect qerrors to be callable still get a no-op function if package missing. Subagent AProject conventions - Keep CommonJS requires (module.exports) style used in repository. - Preserve existing logging calls; avoid introducing async logic here. Subagent A — Dependencies & imports - No new packages to be added. - Optional dependency: qerrors — fallback is a no-op to avoid test-time crashes. Subagent A — Specific requirements - Provide minimal change; unit tests must not throw during module load when qerrors is absent. - Commit with sensible message: "defensive-load: qerrors fallback for tests". Subagent B — logger.js (Relevant file contents) - File: lib/logger.js top of file currently: const { createLogger, format, transports } = require('winston'); const DailyRotateFile = require('winston-daily-rotate-file'); - Change to: let DailyRotateFile = null; try { DailyRotateFile = require('winston-daily-rotate-file'); } catch (err) { DailyRotateFile = null; } // After that, build transportsList[] and only add DailyRotateFile transport if module present. Subagent B — Interface contracts - logger remains a Winston logger instance exported as module.exports = logger; - No signature changes for users. Subagent B — Project conventions - Use existing error-tolerant approach (best-effort mkdir) as pattern; keep createLogger config consistent. Subagent B — Dependencies & imports - winston is required; winston-daily-rotate-file is optional. - No new dependencies. Subagent B — Specific requirements - Avoid require-time exceptions when winston-daily-rotate-file is not installed. - Do not alter log message formats or levels. Step 2 — Add CommonJS test setup file (ASYNC) - Description: Create tests/setup.js (CommonJS) copy of tests/setup.ts so jest.config.js existing setupFilesAfterEnv finds a real file. - Can be executed asynchronously in parallel with Step 1. - Quantumagent command example: npx quantumagent --task "Create tests/setup.js copying tests/setup.ts behavior; ensure CommonJS global hooks beforeAll/afterAll exist." Subagent C — Relevant file contents - New file: tests/setup.js beforeAll(() => {}); afterAll(() => {}); Subagent C — Interface contracts - Jest expects setup file at '<rootDir>/tests/setup.js' per jest.config.js - No exports required. Subagent C — Project conventions - Use CommonJS (no ES module imports). - Keep file minimal and synchronous. Subagent C — Dependencies & imports - None. Subagent C — Specific requirements - Save file with Unix line endings. - Commit message: "test-setup: add CommonJS tests/setup.js for Jest". Step 3 — Run the test suite and capture failures (ASYNC/PARALLEL) - Description: Run the test suite (npm test / yarn test / npx jest) in a subagent. Collect failing tests and stack traces. - This can be run concurrently with static linting tasks and long-running test subsets. - Quantumagent command example: npx quantumagent --task "Run full test suite: npm test --silent (or npx jest --runInBand). Save output to test-output/full-run.log and failing-tests.json." Subagent D — Relevant outputs - test-output/full-run.log - failing-tests.json (structured list with test name, file, stack trace, failure message) Subagent D — Interface contracts - Tests executed via project's configured test runner (Jest). - The runner must return exit code and raw output. Subagent D — Project conventions - Use NODE_ENV=test and do not start external servers. Subagent D — Dependencies & imports - Uses existing devDependencies (jest, etc.) already present. Subagent D — Specific requirements - Provide clear failing stack traces and file/line numbers. - If no failures remain, mark step complete. Step 4 — Triage remaining failing tests and fix (ASYNC/PARALLEL) - Description: For each failing test, create a focused subagent tasked with: - Reproducing the failure locally - Proposing minimal fix (test or implementation) - Implementing and verifying - Each failing test file is a separate asynchronous task/subagent, allowing parallel work. Example Subagent task for a failing test file: npx quantumagent --task "Fix failing test tests/unit/getEnvVar.test.js: reproduce, inspect stack traces, modify lib/system/env/getEnvVar.js behavior or test expectations as minimal fix, run test, commit change." Subagent E — Relevant file contents - The failing test file content (only the file under test and the test file) - The implementation file(s) involved (getEnvVar.js / logger.js / others) Subagent E — Interface contracts - Test harness expects function outputs as described by test assertions (e.g., getEnvVar returns default for invalid booleans, etc.) Subagent E — Project conventions - Maintain test-first safety; prefer changing implementation to meet tests unless test is wrong. - Keep naming and style consistent with repository. Subagent E — Dependencies & imports - No new packages unless absolutely necessary; if new packages needed, include justification. Subagent E — Specific requirements - Document changes and rationale in commit message. - Ensure no global state leakage between tests. Step 5 — Run smoke tests and deterministic CI run (ASYNC) - Description: After fixes are merged locally, run a CI-style test invocation: - Run npm test with NODE_ENV=test - Optionally run with --runInBand or increased timeout to avoid flakiness - This step can be parallelized with linting. Quantumagent command example: npx quantumagent --task "Run CI-style test command: NODE_ENV=test npm test -- --runInBand --detectOpenHandles; save the output." Step 6 — Lint, format, and precommit hooks (ASYNC) - Description: Run linter and code formatter to ensure consistency. - Tasks can be parallelized: - Run eslint in a subagent - Run Prettier in a subagent Quantumagent example: npx quantumagent --task "Run eslint --ext .js,.ts . and fix where applicable; report remaining issues." Step 7 — Documentation & CI update (ASYNC) - Description: If tests needed a specific environment or optional dependency detection, add a small note to README/AGENTS.md or 00-AGENTS.md about optional runtime dependencies and how tests are executed (e.g., test environment will use fallbacks if loqatevars or qerrors are not installed). - Update CI YAML to run npm install --no-optional? Or ensure optional packages are not required for unit tests. - Quantumagent example: npx quantumagent --task "Add README/CI note about optional dependencies and update CI job to 'npm ci --no-optional' if appropriate." Step 8 — Final verification & merge (ASYNC) - Description: After the test suite is green: - Run a final full test - Tag PR with summary and request review - Merge to main branch following repo workflow - This can happen in parallel with documentation updates. ## Additional Notes - Primary causes of test-time crashes found in provided code: - Top-level require('qerrors') without fallback causing module load failure in environments where qerrors is not installed. - require('winston-daily-rotate-file') without fallback resulting in module load failure if optional log rotate package not installed. - Jest setup path mismatch (jest.config.js expects tests/setup.js while only tests/setup.ts existed) — resolved by adding tests/setup.js. - The solution is intentionally minimal and defensive: avoid introducing new runtime dependencies to satisfy tests, prefer no-op fallbacks so the library functions still work in test environments. - If you prefer to support TypeScript in Jest setup directly, you can instead add ts-node/register or ts-jest to the Jest configuration; however the minimal approach is to add a JS setup shim. - After making changes, run tests in both local and CI environments. Some CI runners may install optional dependencies differently; consider adding an explicit CI step to install optional packages if your production environment requires them. ## Conclusion This plan provides precise small-surface-area modifications to make the test environment stable and reliable: - Add a CommonJS tests/setup.js so jest.config.js finds a setup file. - Make optional runtime requires defensive in lib/system/env/getEnvVar.js and lib/logger.js so missing optional packages don't crash module loading during tests. - Run test suite, triage any remaining failing tests in parallel subagents, fix them, and finalize CI updates and docs. Use the npx quantumagent --task "..." pattern described in each step to dispatch asynchronous parallel subagents for the work items above. Follow the provided subagent contexts to ensure subagents have the minimal required files, contracts, conventions, and specific requirements to operate independently and in parallel.