diff --git a/ShadowsocksX-NG2/Domain/RuntimeFileStore.swift b/ShadowsocksX-NG2/Domain/RuntimeFileStore.swift index a610e169..4f2fe8eb 100644 --- a/ShadowsocksX-NG2/Domain/RuntimeFileStore.swift +++ b/ShadowsocksX-NG2/Domain/RuntimeFileStore.swift @@ -156,25 +156,13 @@ struct RuntimeFileStore { try? Data(contentsOf: fileURL) } + /// 部署收据只读:唯一生产写者是 Agent wrapper(AgentContract.writeRuntimeReceipt), + /// GUI 在健康检查中核对 pid 与契约摘要,从不写。 func readRuntimeReceipt() -> RuntimeDeploymentReceipt? { guard let data = try? Data(contentsOf: runtimeStatusFileURL) else { return nil } return try? JSONDecoder().decode(RuntimeDeploymentReceipt.self, from: data) } - func writeRuntimeReceipt(for document: SslocalRuntimeDocument, processID: Int32) throws { - guard let digest = document.deploymentSHA256 else { - throw PersistenceError.ioFailure(detail: "Runtime contract digest is unavailable") - } - let receipt = RuntimeDeploymentReceipt(processID: processID, contractSHA256: digest) - let encoder = JSONEncoder() - encoder.outputFormatting = [.sortedKeys] - do { - try AtomicFileWriter.write(try encoder.encode(receipt), to: runtimeStatusFileURL) - } catch { - throw PersistenceError.ioFailure(detail: String(describing: error)) - } - } - /// 显式停止清理(D2 停止协议末端):运行时契约、wrapper pid、ACL 链接与 /// 变体、digest 清单及原子写残留。异常崩溃路径不调用本方法(文件保留供 /// KeepAlive 重放,D5)。文件缺失时调用无害。 diff --git a/ShadowsocksX-NG2/Tests/RuntimeReceiptFixture.swift b/ShadowsocksX-NG2/Tests/RuntimeReceiptFixture.swift new file mode 100644 index 00000000..708c6499 --- /dev/null +++ b/ShadowsocksX-NG2/Tests/RuntimeReceiptFixture.swift @@ -0,0 +1,23 @@ +import Foundation + +@testable import ShadowsocksX_NG2 + +/// 测试支持:模拟 wrapper 写入部署收据。生产唯一收据写者是 Agent wrapper +/// (AgentContract.writeRuntimeReceipt),GUI 侧只有读(readRuntimeReceipt); +/// 收据格式知识由 Domain 的 RuntimeDeploymentReceipt 类型承载,此处只复刻 +/// wrapper 的「写动作」供夹具布置磁盘状态。 +extension RuntimeFileStore { + func writeRuntimeReceipt(for document: SslocalRuntimeDocument, processID: Int32) throws { + guard let digest = document.deploymentSHA256 else { + throw PersistenceError.ioFailure(detail: "Runtime contract digest is unavailable") + } + let receipt = RuntimeDeploymentReceipt(processID: processID, contractSHA256: digest) + let encoder = JSONEncoder() + encoder.outputFormatting = [.sortedKeys] + do { + try AtomicFileWriter.write(try encoder.encode(receipt), to: runtimeStatusFileURL) + } catch { + throw PersistenceError.ioFailure(detail: String(describing: error)) + } + } +}