Comprehensive guide to the Minsky test architecture that achieved 100% test success rate (1458/1458 tests)
- Overview
- Core Principles
- Proven Success Patterns
- Test Organization
- Testing Utilities
- ESLint Integration
- Migration Guidelines
- Common Anti-Patterns
- Troubleshooting Guide
- Quick Reference
The Minsky project has developed a robust test architecture that achieved 100% test success rate (1458/1458 tests) through systematic application of proven patterns and architectural principles. This documentation consolidates all the knowledge, patterns, and guidelines that made this success possible.
- Total Tests: 1,425 passing tests (6 skipped by design)
- Success Rate: 100% (0 failures, 0 unexpected skips)
- Framework: Bun test with custom utilities
- Architecture: Domain-driven with dependency injection
- Isolation: Complete test isolation preventing cross-test interference
- Performance: ~2s execution time for full test suite
- Security: Active validation of critical git workflow protection
Test business logic and domain methods directly, not interface layers.
// ❌ WRONG: Testing CLI interface
const { stdout } = await execAsync('minsky task create "Test Task"');
expect(stdout).toContain("Task created");
// ✅ CORRECT: Testing domain logic
const task = await createTaskFromParams({ title: "Test Task" });
expect(task.title).toBe("Test Task");Always use project-specific test utilities instead of direct framework APIs.
// ❌ WRONG: Direct bun:test APIs
import { mock } from "bun:test";
const mockFn = mock();
// ✅ CORRECT: Centralized utilities
import { createMock, setupTestMocks } from "../utils/test-utils/mocking";
setupTestMocks();
const mockFn = createMock();Use dependency injection patterns to enable reliable testing.
// ❌ WRONG: Hard dependencies
export async function createSession(taskId: string) {
const taskService = new TaskService(); // Hard dependency
return taskService.createSession(taskId);
}
// ✅ CORRECT: Dependency injection
export async function createSessionWithDependencies(
taskId: string,
deps: { taskService: TaskService }
) {
return deps.taskService.createSession(taskId);
}Prevent cross-test interference through proper cleanup and isolation.
describe("Test Suite", () => {
setupTestMocks(); // Automatic cleanup
beforeEach(() => {
// Reset mocks to known state
mockService.mockClear();
});
});These patterns were discovered through systematic test fixing and achieved 100% success rate:
These patterns were discovered during recent test stabilization work that fixed infinite loop test hangs and achieved 1,425 passing tests.
Problem: Constructor parameter naming mismatches cause infinite loops (99.999%+ performance impact) Discovery: Task #458 - TaskService tests running for 4+ billion milliseconds Success Rate: 100% when variable naming is consistent
// ❌ CRITICAL BUG: Parameter/usage mismatch causing infinite loops
class TaskService {
constructor(backends: TaskBackend[], workspacePath: string) {
/* ... */
}
}
// In tests - CAUSES INFINITE LOOPS:
const taskService = new TaskService(customBackends, workspacePath); // ❌ WRONG parameter name
// ^^^^^^^^^^^^^ 'customBackends' instead of 'backends'
// ✅ CORRECT: Consistent parameter naming
const taskService = new TaskService(backends, workspacePath); // ✅ Matches constructor
// ^^^^^^^^ Correct parameter namePerformance Evidence (historical benchmarks from when JsonFileTaskBackend existed; backend since removed):
- JsonFileTaskBackend: 4,319,673,451ms → 241ms (99.999% improvement)
- SessionPathResolver: 4,319,805,914ms → 143ms (99.999% improvement)
Key Rule: NEVER add underscores to fix "variable not defined" errors - fix the parameter naming instead.
Problem: Real temp directory operations cause test failures and skips
Discovery: SessionPathResolver tests failing due to createRobustTempDir() issues
Success Rate: 100% test enablement when using mock paths
// ❌ PROBLEMATIC: Real temp directory dependency
beforeEach(async () => {
const tempDirResult = createRobustTempDir("minsky-test-", { softFail: true });
if (!tempDirResult) {
console.warn("Skipping tests due to temp directory creation failure");
return; // Tests get skipped
}
tempDir = tempDirResult;
sessionWorkspace = join(tempDir, "session-workspace");
});
// ✅ RELIABLE: Mock path pattern
beforeEach(() => {
// Use mock paths - no real filesystem operations needed
sessionWorkspace = "/mock/session-workspace";
resolver = new SessionPathResolver();
});Results: All 19 SessionPathResolver tests now pass without skipping
Problem: Critical security tests disabled with .skip() provide zero protection
Discovery: PR branch validation tests were skipped despite working logic
Success Rate: 100% when security validation is enabled
// ❌ PROVIDES NO SECURITY: Skipped critical tests
it.skip("should reject PR creation when current branch is a PR branch", async () => {
// Security logic never tested
});
// ✅ ACTIVE SECURITY: Enabled validation tests
it("should reject PR creation when current branch is a PR branch", async () => {
// Security logic actively validated
await expect(preparePrImpl(options, deps)).rejects.toThrow(
/Cannot create PR from PR branch 'pr\/task-md#357'/
);
});Security Impact: Now actively prevents pr/pr/task-name double-prefix bugs and workflow violations
Problem: Mock interfaces don't match current service APIs causing test failures
Discovery: TaskService mock missing required getCapabilities and createTaskFromTitleAndSpec methods
Success Rate: 100% when mocks implement full current interface
// ❌ INCOMPLETE: Outdated mock interface
const createMockBackend = () => ({
listTasks: mock(),
getTaskStatus: mock(),
setTaskStatus: mock(),
// Missing required methods from current interface
});
// ✅ COMPLETE: Full current interface implementation
const createMockBackend = (): TaskBackend => ({
listTasks: mock(),
getTaskStatus: mock(),
setTaskStatus: mock(),
createTaskFromTitleAndSpec: mock(), // Required by current API
getCapabilities: mock(() => ({
// Required by current API
canCreateTasks: true,
canUpdateStatus: true,
supportsQualifiedIds: true,
})),
});Critical Rule: Keep mock interfaces synchronized with actual service interfaces to prevent runtime errors.
Discovery: Not all skipped tests represent problems - some are intentionally educational Success: Clear classification prevents wasted effort on intentional skips
Educational/Demonstration Skips (Should remain skipped):
// ❌ Anti-pattern demonstration tests (src/eslint-rules/no-real-fs-in-tests.test.js)
describe.skip("filesystem operations test", () => {
it("should detect filesystem operations", () => {
// Intentionally demonstrates bad patterns for ESLint rule testing
mkdirSync(testDir); // This SHOULD fail - it's an example of what NOT to do
});
});Integration Test Skips (Should be conditional):
// ✅ Conditional integration tests
describe.if(process.env.RUN_INTEGRATION_TESTS)(
"session pr edit (and changeset aliases) - conventional commit title validation",
() => {
// These run only when explicitly requested
}
);Problematic Skips (Should be fixed immediately):
// ❌ MUST FIX: Broken functionality skipped
it.skip("critical security validation", () => {
// This provides zero protection when skipped
});Classification Rule: Distinguish between educational skips (acceptable), conditional skips (by design), and broken skips (must fix).
Problem: Factory-generated mocks with async functions are unreliable Success Rate: 100% when applied correctly
// ❌ UNRELIABLE: Factory-generated mock
const mockService = createMockService(async (id) => {
if (id === "test") return mockData;
return null;
});
// ✅ RELIABLE: Explicit Mock Pattern
const mockService = {
getData: async (id: string) => {
if (id === "test") {
return { id: "test", name: "Test Item" };
}
return null;
},
listItems: async () => [],
updateItem: async () => {},
deleteItem: async () => false,
// All methods explicitly defined
};Key Benefits:
- ✅ Reliable mock construction
- ✅ All required methods explicitly defined
- ✅ Predictable behavior
- ✅ Handles both input and qualified ID formats
Problem: Repeated string construction leads to format mismatches Success Rate: 100% for eliminating magic string errors
// ❌ PROBLEMATIC: Magic strings and repetition
expect(result.id).toBe("user-123");
expect(result.name).toBe("user-123-session");
expect(commands).toContain("process user-123 for session user-123-session");
// ✅ RELIABLE: Template Literal Pattern
const USER_ID = "123";
const QUALIFIED_ID = `user-${USER_ID}`;
const SESSION_NAME = `${QUALIFIED_ID}-session`;
const COMMAND = `process ${QUALIFIED_ID} for session ${SESSION_NAME}`;
expect(result.id).toBe(QUALIFIED_ID);
expect(result.name).toBe(SESSION_NAME);
expect(commands).toContain(COMMAND);Problem: Mock data format doesn't match system-generated formats Success Rate: 100% for format-related test failures
// ❌ MISALIGNED: Mock format doesn't match system format
const mockDatabase = {
getRecord: (id: string) =>
Promise.resolve({
name: `item${id}`, // → "item123" (no separator) ❌
path: `data/item${id}`, // → "data/item123" (inconsistent) ❌
}),
};
// ✅ ALIGNED: Mock format matches system format
const mockDatabase = {
getRecord: (id: string) =>
Promise.resolve({
name: `item-${id}`, // → "item-123" (with separator) ✅
path: `data/item-${id}`, // → "data/item-123" (consistent) ✅
}),
};Problem: Global mock.module() calls persist across tests
Root Cause: Tests passing in isolation but failing in full suite
// ❌ DANGEROUS: Global module mocks that persist
mock.module("../utils/logger", () => ({
log: mockLog,
}));
// ✅ SAFE: Use dependency injection instead
const dependencies = {
logger: mockLogger,
database: mockDatabase,
service: mockService,
};Problem: Complex functions with I/O operations are hard to unit test Solution: Extract pure business logic for focused unit testing
// ❌ COMPLEX: Function with mixed concerns
async function processData(params, deps) {
// Complex I/O operations, business logic, notifications, etc.
if (!params.skipValidation && hasErrors) {
await validationService.validate();
}
}
// ✅ EXTRACTED: Pure business logic functions
export function shouldValidate(options: ProcessOptions, state: ProcessState): boolean {
if (options.force) return false;
if (options.skipValidation) return false;
return state.hasErrors;
}- Proven Success: 100% test success rate achieved through systematic approach
- Architectural Clarity: Clear separation between business logic and I/O operations
- Test Reliability: No cross-test interference or isolation issues
- Performance: Fast test execution with proper dependency injection
- Maintainability: Template literals and explicit mocks reduce maintenance burden
- Debugging: Root cause investigation over symptom masking
- Consistency: All tests use the same proven patterns across the codebase
This documentation represents the collective knowledge from achieving 100% test success rate (1458/1458 tests) in the Minsky project. Follow these patterns to ensure reliable, maintainable tests.
For detailed implementation guides, migration procedures, and daily development workflows, see the complete documentation suite in docs/testing/.