From 945ea2b4db304c3a82e0791aae5caa831dbe26d1 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Sun, 4 Oct 2026 12:59:26 +0800 Subject: [PATCH] =?UTF-8?q?refactor(runtime):=20=E6=94=B6=E6=8D=AE?= =?UTF-8?q?=E5=86=99=E8=80=85=E5=BD=92=E4=B8=80=E4=B8=BA=20Agent=20wrapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RuntimeFileStore.writeRuntimeReceipt 生产零调用(17 处调用全为测试夹具), 从生产 interface 删除;测试侧经 RuntimeReceiptFixture 的 extension 以同签名 承接,调用点零改动。收据格式知识仍由 RuntimeDeploymentReceipt 类型承载, GUI 侧只保留 readRuntimeReceipt 读缝。 评审候选⑥ 6a:wrapper 是唯一收据写者,GUI 永不伪造收据。 --- .../Domain/RuntimeFileStore.swift | 16 ++----------- .../Tests/RuntimeReceiptFixture.swift | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 ShadowsocksX-NG2/Tests/RuntimeReceiptFixture.swift 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)) + } + } +}