Operations interfaces for dependency injection.
This is the core pattern enabling testability without mocks.
All side effects (git, npm, fs, process, build, and the repos binary) are
abstracted into interfaces.
Design principles:
- All operations accept a single
options object parameter - All fallible operations return
Result from @fuzdev/fuz_util - Never throw
Error in operations - return Result with ok: false - Use
null for expected "not found" cases (not errors) - Include
log?: Logger in options where logging is useful
Production usage:
import {default_gitops_operations} from './operations_defaults.ts';
const ops = default_gitops_operations;
const result = await ops.git.current_commit_hash({cwd: '/path'});
if (!result.ok) {
throw new TaskError(result.message);
}
const commit = result.value;Test usage:
import {create_mock_gitops_ops} from './test_helpers.ts';
const ops = create_mock_gitops_ops({
changeset: {has_changesets: async () => ({ok: true, value: false})}
});
const result = await publish_repos(repos, {...options, ops});
See operations_defaults.ts for real implementations, and the test-side mock
factories: create_mock_gitops_ops in src/test/test_helpers.ts (plain
objects with per-group overrides) and create_fixture_gitops_ops in
src/test/fixtures/mock_operations.ts (a fixture's changesets over
create_mock_gitops_ops, its fs empty).