chore(test-seams): move _test*/_force* seams from public to internal
Per audit-2026-05-04 P2-14: VoiceController._forceState, _testFireSpeechEnd, _testCurrentSttSessionId were declared `public` solely so XCTest could see them. Same for MCPClient._testHandle and MCPServerHandle._testProcessIdentifier. These leaked test-only entry points into each package's public API — anyone importing Voice could call _forceState(.speaking) from production code. Voice (4 seams): public → internal. Voice tests already use @testable import Voice, so no test-side change required. MCP (2 seams): public → @_spi(Testing) public. MCP tests opt in via @testable @_spi(Testing) import JarvisMCP. The Harness package (non-test) consumes _testHandle / _testProcessIdentifier for the MCPCrashRunner SIGKILL recipe — opt in via @_spi(Testing) import. @testable alone does NOT expose @_spi symbols — the SPI must be explicitly imported, which is the desired discipline (each consumer acknowledges they're reaching past the public ABI). 6 test seams total moved off the public ABI. Voice + MCP tests pass (87 + 85). Harness builds clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
263705d39d
commit
57847f2f58
5 files changed
+14
-8
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
import Darwin
|
||||
import JarvisMCP
|
||||
@_spi(Testing) import JarvisMCP
|
||||
import MCP
|
||||
|
||||
/// MCPCrashRunner — Plan 08-03 Task 2.
|
||||
|
||||
@@ -189,7 +189,10 @@ public actor MCPClient {
|
||||
#if DEBUG
|
||||
/// Test-only accessor into the registry. Used by restart-mutex tests that
|
||||
/// need to SIGKILL the helper PID directly. NOT for production use.
|
||||
public func _testHandle(named name: String) -> MCPServerHandle? {
|
||||
/// Gated by `@_spi(Testing)` so callers must opt in via
|
||||
/// `@_spi(Testing) import JarvisMCP` (used by tests via `@testable` and
|
||||
/// by the eval `Harness` package). Plain `import JarvisMCP` cannot see it.
|
||||
@_spi(Testing) public func _testHandle(named name: String) -> MCPServerHandle? {
|
||||
registry[name]
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -338,7 +338,8 @@ public actor MCPServerHandle {
|
||||
|
||||
#if DEBUG
|
||||
/// Returns the helper's PID for tests that need to SIGKILL the child directly.
|
||||
public func _testProcessIdentifier() -> Int32 {
|
||||
/// Gated by `@_spi(Testing)` — see `MCPClient._testHandle` for rationale.
|
||||
@_spi(Testing) public func _testProcessIdentifier() -> Int32 {
|
||||
process?.processIdentifier ?? -1
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import XCTest
|
||||
import Darwin
|
||||
import MCP
|
||||
@testable import JarvisMCP
|
||||
@testable @_spi(Testing) import JarvisMCP
|
||||
|
||||
final class MCPRestartTests: XCTestCase {
|
||||
private var helperBinary: URL!
|
||||
|
||||
@@ -229,12 +229,14 @@ public actor VoiceController {
|
||||
// MARK: - Test seams
|
||||
|
||||
/// Force the controller into a specific state. Tests only.
|
||||
public func _forceState(_ newState: VoiceState) async {
|
||||
/// `internal` + `@testable import Voice` keeps this out of the public
|
||||
/// ABI so production callers (App, Harness) cannot reach in.
|
||||
internal func _forceState(_ newState: VoiceState) async {
|
||||
state = newState
|
||||
}
|
||||
|
||||
/// Simulate a VAD speech-end + STT finalization with given text. Tests only.
|
||||
public func _testFireSpeechEnd(text: String = "") async {
|
||||
internal func _testFireSpeechEnd(text: String = "") async {
|
||||
await handleSTTFinalized(text: text)
|
||||
}
|
||||
|
||||
@@ -242,13 +244,13 @@ public actor VoiceController {
|
||||
/// finalize path with an explicit `sessionId` so tests can simulate a
|
||||
/// finalize callback from a prior session arriving after the next
|
||||
/// session has started.
|
||||
public func _testFireSpeechEnd(text: String, sessionId: UInt64) async {
|
||||
internal func _testFireSpeechEnd(text: String, sessionId: UInt64) async {
|
||||
await handleSTTFinalized(text: text, sessionId: sessionId)
|
||||
}
|
||||
|
||||
/// P1-3 stale-text seam (tests only). Returns the current session id so
|
||||
/// tests can capture it before triggering a session boundary.
|
||||
public func _testCurrentSttSessionId() -> UInt64 {
|
||||
internal func _testCurrentSttSessionId() -> UInt64 {
|
||||
sttSessionId
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user