mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-10-30 06:03:48 +00:00
refactor: keep test doubles and test-only wrappers out of production
Move InMemoryDiagnosticReportExporter and InMemoryConfigurationGroupFileExporter from App/PlatformEffects to Tests/Support: both are test doubles with no production caller, and production composition wires only the AppKit adapters. Delete RuleRuntimeCompiler.validation, a wrapper whose only callers were two assertion blocks in the controller deployment test; those restated custom-to-custom shadowing and validation order-independence, both pinned by CustomRuleValidatorTests and RuleAnalysisTests. RuleRuntimeCompiler.compile stays and keeps its production caller. Co-Authored-By: DeepSeek Harness <noreply@deepseek.com>
This commit is contained in:
co-authored by
DeepSeek Harness
parent
5872015ca0
commit
7977daafea
@@ -72,19 +72,3 @@ final class AppKitConfigurationGroupFileExporter: ConfigurationGroupFileExporter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests and previews receive the complete draft without opening a panel or writing a file.
|
||||
@MainActor
|
||||
final class InMemoryConfigurationGroupFileExporter: ConfigurationGroupFileExporter {
|
||||
var result: ConfigurationGroupFileExportResult
|
||||
private(set) var drafts: [ConfigurationGroupExportDraft] = []
|
||||
|
||||
init(result: ConfigurationGroupFileExportResult = .cancelled) {
|
||||
self.result = result
|
||||
}
|
||||
|
||||
func export(draft: ConfigurationGroupExportDraft) -> ConfigurationGroupFileExportResult {
|
||||
drafts.append(draft)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,19 +47,3 @@ struct DiagnosticReportExportAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试与 Preview 使用的内存实现:记录收到的 draft,但不打开保存面板或写文件。
|
||||
@MainActor
|
||||
final class InMemoryDiagnosticReportExporter: DiagnosticReportExporter {
|
||||
var result: DiagnosticReportExportResult
|
||||
private(set) var drafts: [DiagnosticReportDraft] = []
|
||||
|
||||
init(result: DiagnosticReportExportResult = .cancelled) {
|
||||
self.result = result
|
||||
}
|
||||
|
||||
func export(draft: DiagnosticReportDraft) -> DiagnosticReportExportResult {
|
||||
drafts.append(draft)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,18 +346,6 @@ enum RuleRuntimeCompiler {
|
||||
let builtIn: [ProxyRule]
|
||||
let aclURL: URL
|
||||
}
|
||||
static func validation(
|
||||
document: CustomRuleDocument, builtIn: [ProxyRule], defaultAction: RuleDefaultAction
|
||||
) -> CustomRuleValidationResult {
|
||||
let custom = document.rules.filter { !document.disabledIdentities.contains($0.identity) }
|
||||
let enabled = builtIn.filter { !document.disabledIdentities.contains($0) }
|
||||
let validated = CustomRuleValidator.validate(
|
||||
custom: custom, builtIn: enabled, defaultAction: defaultAction)
|
||||
return CustomRuleValidationResult(
|
||||
accepted: RuleAnalysis.runtimeCandidates(
|
||||
enabled + validated.accepted, defaultAction: defaultAction),
|
||||
rejected: validated.rejected, relationships: validated.relationships)
|
||||
}
|
||||
|
||||
static func compile(_ input: Input) -> SslocalRuntimeDocument {
|
||||
let source = input.source
|
||||
|
||||
@@ -102,14 +102,6 @@ final class CustomRuleControllerTests: ProxyRuntimeControllerTests {
|
||||
XCTAssertEqual(first, .saved)
|
||||
await controller.ruleApplicationTask?.value
|
||||
XCTAssertEqual(try store.load(), rules, "保存保留用户 UUID 和全部意图")
|
||||
// 遮蔽关系由领域层验证器判定(与运行时部署无关的纯函数)。
|
||||
let validation = RuleRuntimeCompiler.validation(
|
||||
document: CustomRuleDocument(rules: rules, disabledIdentities: []),
|
||||
builtIn: [], defaultAction: .directWhenUnmatched)
|
||||
XCTAssertTrue(
|
||||
validation.relationships.contains {
|
||||
$0.rule == rules[2].identity && $0.kind == .shadowing && $0.extent == .full
|
||||
})
|
||||
let runtimeStore = RuntimeFileStore(fileURL: runtime.contract)
|
||||
let before = try activeACLContent(runtimeStore)
|
||||
let unregisterBefore = agent.unregisterCount
|
||||
@@ -123,10 +115,6 @@ final class CustomRuleControllerTests: ProxyRuntimeControllerTests {
|
||||
XCTAssertEqual(try activeACLContent(runtimeStore), before)
|
||||
XCTAssertEqual(agent.unregisterCount, unregisterBefore)
|
||||
XCTAssertEqual(controller.readCustomRuleSummary(), summary)
|
||||
let repeatedValidation = RuleRuntimeCompiler.validation(
|
||||
document: CustomRuleDocument(rules: reversed, disabledIdentities: []),
|
||||
builtIn: [], defaultAction: .directWhenUnmatched)
|
||||
XCTAssertEqual(repeatedValidation, validation)
|
||||
}
|
||||
|
||||
/// 校验拒绝:固定本地冲突整批不落地,旧规则保持不变。
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import Foundation
|
||||
|
||||
@testable import ShadowsocksX_NG2
|
||||
|
||||
/// 导出缝的测试替身:记录收到的 draft,但不打开保存面板、不写文件。
|
||||
@MainActor
|
||||
final class InMemoryDiagnosticReportExporter: DiagnosticReportExporter {
|
||||
var result: DiagnosticReportExportResult
|
||||
private(set) var drafts: [DiagnosticReportDraft] = []
|
||||
|
||||
init(result: DiagnosticReportExportResult = .cancelled) {
|
||||
self.result = result
|
||||
}
|
||||
|
||||
func export(draft: DiagnosticReportDraft) -> DiagnosticReportExportResult {
|
||||
drafts.append(draft)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/// 配置组文件导出缝的测试替身:同上,只记录 draft 并返回预设结果。
|
||||
@MainActor
|
||||
final class InMemoryConfigurationGroupFileExporter: ConfigurationGroupFileExporter {
|
||||
var result: ConfigurationGroupFileExportResult
|
||||
private(set) var drafts: [ConfigurationGroupExportDraft] = []
|
||||
|
||||
init(result: ConfigurationGroupFileExportResult = .cancelled) {
|
||||
self.result = result
|
||||
}
|
||||
|
||||
func export(draft: ConfigurationGroupExportDraft) -> ConfigurationGroupFileExportResult {
|
||||
drafts.append(draft)
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
- `Application/` — 应用层(无 SwiftUI 视图):`*Workflow/` 工作流 module(UI-facing interface 与实现协作者)、`ProxyRuntime/` 运行时控制器家族与事实投影、`Services/` LaunchAgent/登录项/helper/防火墙。
|
||||
- `Presentation/` — 跨功能表现层:错误呈现、被多个界面共用的状态模型。
|
||||
- `UI/` — 视图层,按子视图分目录(`Workspace/`、`Home/`、`Servers/`、`Subscriptions/`、`Rules/`、`Settings/`、`Diagnostics/`、`StatusMenu/`,`Servers/Plugin/` 为插件管理子视图)。
|
||||
- `PlatformEffects/` — 平台效应 seam(协议 + InMemory 替身 + AppKit adapter),系统框架访问集中于此。
|
||||
- `PlatformEffects/` — 平台效应 seam(协议 + AppKit adapter;测试替身在 `Tests/Support/`),系统框架访问集中于此。
|
||||
- `Resources/` — `Info.plist`、`entitlements`(生成物)与 `Assets.xcassets`、`*.xcstrings`(入库)。
|
||||
|
||||
分层边界由 `Tests/Architecture/SkeletonTests.swift` 的正向圈定守卫断言:只扫描 `App/UI/`,视图层不得引用运行时控制器、工作流实现协作者或原始目录/凭据存储类型;新增运行时适配器放进 `Application/` 即自动落在扫描范围外,无需登记豁免。注意 `App/` 下任何非源码文件都会被 XcodeGen 收进 Resources 阶段并封进签名后的 bundle,因此文档不放 `App/`(`project.yml` 已排除 `**/*.md`)。分层与主题的组织方式、守卫为何取正面圈定、以及 XcodeGen 的上述约束见 [ADR-0029](adr/0029-source-layout-declares-layer-and-theme.md)。
|
||||
|
||||
Reference in New Issue
Block a user