You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Add missing packageCachePath to PlatformBuildOptions interface
- Remove unused vi import and result variable in pipeline test
- Restore responses/index.ts barrel and processToolResponse function
deleted in PR 3, which broke docs:check and downstream imports
- Update DI documentation to clarify that CommandExecutor/FileSystemExecutor
is required for complex process orchestration (xcodebuild) but not for
simple utility modules
Copy file name to clipboardExpand all lines: .cursor/BUGBOT.md
+6-5Lines changed: 6 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,7 +12,7 @@ For full details see [README.md](README.md) and [docs/ARCHITECTURE.md](docs/ARCH
12
12
## 1. Security Checklist — Critical
13
13
14
14
* No hard-coded secrets, tokens or DSNs.
15
-
*All shell commands must flow through `CommandExecutor` with validated arguments (no direct `child_process` calls).
15
+
*MCP tool logic functions that orchestrate long-running processes with sub-processes (e.g., `xcodebuild`) must flow through `CommandExecutor` with validated arguments. Standalone utility modules that invoke simple, short-lived commands may use direct `child_process`/`fs` imports and standard vitest mocking.
16
16
* Paths must be sanitised via helpers in `src/utils/validation.ts`.
17
17
* Sentry breadcrumbs / logs must **NOT** include user PII.
18
18
@@ -22,7 +22,7 @@ For full details see [README.md](README.md) and [docs/ARCHITECTURE.md](docs/ARCH
22
22
23
23
| Rule | Quick diff heuristic |
24
24
|------|----------------------|
25
-
| Dependency injection only | New `child_process`\|`fs` import ⇒ **critical**|
25
+
| Dependency injection for tool logic | New `child_process`\|`fs` import in MCP tool logic ⇒ **warning** (check if process is complex/long-running)|
26
26
| Handler / Logic split |`handler` > 20 LOC or contains branching ⇒ **critical**|
***External-boundary rule**: Use `createMockExecutor` / `createMockFileSystemExecutor` for command execution and filesystem side effects.
48
+
***External-boundary rule for tool logic**: Use `createMockExecutor` / `createMockFileSystemExecutor` for complex process orchestration (xcodebuild, multi-step pipelines) in MCP tool logic functions.
49
+
***Simple utilities**: Standalone utility modules with simple command calls can use direct imports and standard vitest mocking.
49
50
***Internal mocking is allowed**: `vi.mock`, `vi.fn`, `vi.spyOn`, and `.mock*` are acceptable for internal modules/collaborators.
50
51
* Each tool must have tests covering happy-path **and** at least one failure path.
51
52
* Avoid the `any` type unless justified with an inline comment.
Copy file name to clipboardExpand all lines: docs/dev/ARCHITECTURE.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -418,13 +418,13 @@ Not all parts are required for every tool. For example, `swift_package_build` ha
418
418
419
419
### Testing Principles
420
420
421
-
XcodeBuildMCP uses a **Dependency Injection (DI)** pattern for testing external boundaries (command execution, filesystem, and other side effects). Vitest mocking libraries (`vi.mock`, `vi.fn`, etc.) are acceptable for internal collaborators when needed. This keeps tests robust while preserving deterministic behavior at external boundaries.
421
+
XcodeBuildMCP uses a **Dependency Injection (DI)** pattern for MCP tool logic functions that orchestrate complex, long-running processes with sub-processes (e.g., `xcodebuild`), where standard vitest mocking produces race conditions. Standalone utility modules with simple commands may use direct `child_process`/`fs` imports and standard vitest mocking. Vitest mocking libraries (`vi.mock`, `vi.fn`, etc.) are also acceptable for internal collaborators.
422
422
423
423
For detailed guidelines, see the [Testing Guide](TESTING.md).
424
424
425
425
### Test Structure Example
426
426
427
-
Tests inject mock "executors" for external interactions like command-line execution or file system access. This allows for deterministic testing of tool logic without mocking the implementation itself. The project provides helper functions like `createMockExecutor` and `createMockFileSystemExecutor` in `src/test-utils/mock-executors.ts` to facilitate this pattern.
427
+
Tests for MCP tool logic inject mock "executors" for complex process orchestration (e.g., xcodebuild). This allows for deterministic testing without race conditions from non-deterministic sub-process ordering. The project provides helper functions like `createMockExecutor` and `createMockFileSystemExecutor` in `src/test-utils/mock-executors.ts`. Standalone utility modules with simple commands use standard vitest mocking.
Copy file name to clipboardExpand all lines: docs/dev/CODE_QUALITY.md
+7-6Lines changed: 7 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -53,17 +53,18 @@ XcodeBuildMCP enforces several architectural patterns that cannot be expressed t
53
53
54
54
### 1. Dependency Injection Pattern
55
55
56
-
**Rule**: All tools must use dependency injection for external interactions.
56
+
**Rule**: MCP tool logic functions that orchestrate complex, long-running processes with sub-processes (e.g., `xcodebuild`) must use dependency injection for external interactions. This is because standard vitest mocking produces race conditions when sub-process ordering is non-deterministic.
57
+
58
+
Standalone utility modules that invoke simple, short-lived commands (e.g., `xcrun devicectl list`, `xcrun xcresulttool get`) may use direct `child_process`/`fs` imports and be tested with standard vitest mocking.
57
59
58
60
✅ **Allowed**:
59
-
-`createMockExecutor()` for command execution mocking
60
-
-`createMockFileSystemExecutor()`for file system mocking
Copy file name to clipboardExpand all lines: docs/dev/CONTRIBUTING.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -270,7 +270,7 @@ Before making changes, please familiarize yourself with:
270
270
All contributions must adhere to the testing standards outlined in the [**XcodeBuildMCP Plugin Testing Guidelines (TESTING.md)**](TESTING.md). This is the canonical source of truth for all testing practices.
271
271
272
272
**Key Principles (Summary):**
273
-
-**Dependency Injection for External Boundaries**: All external dependencies (command execution, file system access) must be injected into tool logic functions using the `CommandExecutor` and `FileSystemExecutor` patterns.
273
+
-**Dependency Injection for Complex Processes**: MCP tool logic functions that orchestrate complex, long-running processes with sub-processes (e.g., `xcodebuild`) must use injected `CommandExecutor` and `FileSystemExecutor` patterns. Standalone utility modules with simple commands may use direct imports and standard vitest mocking.
274
274
-**Internal Mocking Is Allowed**: Vitest mocking (`vi.mock`, `vi.fn`, `vi.spyOn`, etc.) is acceptable for internal modules/collaborators.
275
275
-**Test Production Code**: Tests must import and execute the actual tool logic, not mock implementations.
276
276
-**Comprehensive Coverage**: Tests must cover input validation, command generation, and output processing.
Copy file name to clipboardExpand all lines: docs/dev/TESTING.md
+16-15Lines changed: 16 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,28 +20,32 @@ This document provides comprehensive testing guidelines for XcodeBuildMCP plugin
20
20
21
21
### 🚨 CRITICAL: External Dependency Mocking Rules
22
22
23
-
### ABSOLUTE RULE: External side effects must use dependencyinjection utilities
23
+
### When to use dependency-injection executors
24
24
25
-
### Use dependency-injection mocks for EXTERNAL dependencies:
26
-
-`createMockExecutor()` / `createNoopExecutor()` for command execution (`xcrun`, `xcodebuild`, AXe, etc.)
27
-
-`createMockFileSystemExecutor()` / `createNoopFileSystemExecutor()` for file system interactions
25
+
`CommandExecutor` / `FileSystemExecutor` DI is required for **MCP tool logic functions** that orchestrate complex, long-running processes with sub-processes (e.g., `xcodebuild`, multi-step build pipelines). Standard vitest mocking produces race conditions with these because sub-process ordering is non-deterministic.
26
+
27
+
-`createMockExecutor()` / `createNoopExecutor()` for command execution in tool logic
28
+
-`createMockFileSystemExecutor()` / `createNoopFileSystemExecutor()` for file system interactions in tool logic
29
+
30
+
### When standard vitest mocking is fine
31
+
32
+
Standalone utility modules that invoke simple, short-lived commands (e.g., `xcrun devicectl list`, `xcrun xcresulttool get`) may use direct `child_process`/`fs` imports and be tested with standard vitest mocking (`vi.fn`, `vi.mock`, `vi.spyOn`, etc.). This is simpler and perfectly adequate for deterministic, single-command utilities.
28
33
29
34
### Internal mocking guidance:
30
35
- Vitest mocking (`vi.fn`, `vi.mock`, `vi.spyOn`, `.mockResolvedValue`, etc.) is allowed for internal modules and in-memory collaborators
31
36
- Prefer straightforward, readable test doubles over over-engineered mocks
32
37
33
38
### Still forbidden:
34
39
- Hitting real external systems in unit tests (real `xcodebuild`, `xcrun`, AXe, filesystem writes/reads outside test harness)
35
-
- Bypassing dependency injection for external effects
36
40
37
41
### OUR CORE PRINCIPLE
38
42
39
-
**Simple Rule**: Use dependency-injection mock executors for external boundaries; use Vitest mocking only for internal behavior.
43
+
**Simple Rule**: Use dependency-injection mock executors for complex process orchestration in tool logic; use standard vitest mocking for simple utility modules and internal behavior.
40
44
41
45
**Why This Rule Exists**:
42
-
1.**Reliability**: External side effects stay deterministic and hermetic
43
-
2.**Clarity**: Internal collaboration assertions remain concise and readable
44
-
3.**Architectural Enforcement**: External boundaries are explicit in tool logic signatures
46
+
1.**Reliability**: Complex multi-process orchestration stays deterministic and hermetic via DI executors
47
+
2.**Simplicity**: Simple utilities use standard vitest mocking without unnecessary abstraction
48
+
3.**Architectural Enforcement**: External boundaries for complex processes are explicit in tool logic signatures
45
49
4.**Maintainability**: Tests fail for behavior regressions, not incidental environment differences
All plugin handlers must support dependency injection:
118
+
MCP tool logic functions that orchestrate complex processes must support dependency injection:
115
119
116
120
```typescript
117
121
exportfunction tool_nameLogic(
@@ -134,12 +138,9 @@ export default {
134
138
};
135
139
```
136
140
137
-
**Important**: The dependency injection pattern applies to ALL handlers, including:
138
-
- Tool handlers
139
-
- Resource handlers
140
-
- Any future handler types (prompts, etc.)
141
+
**Important**: The dependency injection pattern applies to tool and resource handler logic that orchestrates complex, long-running processes (e.g., `xcodebuild`). Standalone utility modules with simple commands may use direct imports and standard vitest mocking.
141
142
142
-
Always use default parameter values (e.g., `= getDefaultCommandExecutor()`) to ensure production code works without explicit executor injection, while tests can override with mock executors.
143
+
Always use default parameter values (e.g., `= getDefaultCommandExecutor()`) in tool logic to ensure production code works without explicit executor injection, while tests can override with mock executors.
0 commit comments