Files
ShadowsocksX-NG/ShadowsocksX-NG2/Tests/Rules/Custom/CustomRuleEditingControllerTests.swift

111 lines
5.1 KiB
Swift

import XCTest
@testable import ShadowsocksX_NG2
/// Verify the editor's public commands against the production commit adapter,
/// using the existing hostless runtime and system-proxy fixtures.
final class CustomRuleEditingControllerTests: ProxyRuntimeControllerTests {
override func makeDefaultRuleSnapshots() -> BuiltinRuleSnapshots {
BuiltinRuleSnapshots(loader: ProxyRuntimeFixture.controlFlowRuleSnapshot)
}
func testEditorSaveWhileOffPersistsWithoutStartingRuntime() async throws {
let (store, _) = try makeCustomRuleStore()
let controller = makeControllerWithCustomRules(
store: store,
settings: ProxyRuntimeSettings(
listen: ActivationFixture.listen, rulePreset: .chinaDirect, agentEnabled: false))
let workflow = editingWorkflow(controller)
await workflow.refresh()
var draft = try XCTUnwrap(workflow.makeCustomRuleDraft())
draft.kind = .ipAddress
draft.content = "8.8.8.8"
let result = await workflow.saveCustomRule(draft)
XCTAssertEqual(result, .committed(.saved))
XCTAssertEqual(try store.load().first?.id, draft.id)
XCTAssertEqual(controller.state, .off)
XCTAssertEqual(agent.registerCount, 0)
XCTAssertEqual(workflow.snapshot.commitFeedback?.operation, .add)
}
func testEditorChangesRuntimeACLOnceAndRetainsUUIDAcrossEdits() async throws {
let seeded = try makeSeededCatalog()
let (store, _) = try makeCustomRuleStore()
let controller = makeControllerWithCustomRules(
store: store,
settings: ProxyRuntimeSettings(
listen: ActivationFixture.listen, preferredMode: .rule,
rulePreset: .chinaDirect, agentEnabled: true), proxyMode: .rule)
try await controller.activate(seeded.server)
let workflow = editingWorkflow(controller)
await workflow.refresh()
let registrations = agent.registerCount
var draft = try XCTUnwrap(workflow.makeCustomRuleDraft())
draft.content = "new-rule.example"
let added = await workflow.saveCustomRule(draft)
XCTAssertEqual(added, .committed(.saved))
await controller.ruleApplicationTask?.value
XCTAssertEqual(agent.registerCount, registrations + 1)
let runtimeStore = RuntimeFileStore(fileURL: runtime.contract)
XCTAssertTrue(try activeACLContent(runtimeStore).contains("||new-rule.example"))
var edited = try XCTUnwrap(workflow.makeCustomRuleDraft(editing: draft.id))
edited.content = "edited-rule.example"
let saved = await workflow.saveCustomRule(edited)
XCTAssertEqual(saved, .committed(.saved))
await controller.ruleApplicationTask?.value
XCTAssertEqual(agent.registerCount, registrations + 2)
XCTAssertEqual(try store.load().map(\.id), [draft.id])
XCTAssertFalse(try activeACLContent(runtimeStore).contains("||new-rule.example"))
XCTAssertTrue(try activeACLContent(runtimeStore).contains("||edited-rule.example"))
XCTAssertEqual(workflow.snapshot.commitFeedback?.operation, .edit)
}
func testEditorFailedDeploymentKeepsSavedEditAndStopsRuntime() async throws {
let seeded = try makeSeededCatalog()
let (store, _) = try makeCustomRuleStore()
let existing = CustomRule(
action: .direct, match: try RuleMatch(domainSuffix: "kept.example"))
try store.save([existing])
let controller = makeControllerWithCustomRules(
store: store,
settings: ProxyRuntimeSettings(
listen: ActivationFixture.listen, preferredMode: .rule,
rulePreset: .chinaDirect, agentEnabled: true), proxyMode: .rule)
try await controller.activate(seeded.server)
let workflow = editingWorkflow(controller)
await workflow.refresh()
let version = workflow.snapshot.version
let runtimeStore = RuntimeFileStore(fileURL: runtime.contract)
let originalRuntime = try XCTUnwrap(runtimeStore.loadDocument())
agent.onRegister = {
try? runtimeStore.writeRuntimeReceipt(for: originalRuntime, processID: 42)
}
var draft = try XCTUnwrap(workflow.makeCustomRuleDraft(editing: existing.id))
draft.content = "new-rule.example"
let result = await workflow.saveCustomRule(draft)
XCTAssertEqual(result, .committed(.saved))
await controller.ruleApplicationTask?.value
XCTAssertEqual(try store.load().first?.id, existing.id)
XCTAssertEqual(
try store.load().first?.match, try RuleMatch(domainSuffix: "new-rule.example"))
XCTAssertNotEqual(workflow.snapshot.version, version)
XCTAssertEqual(
workflow.snapshot.rows.first { $0.customIDs.contains(existing.id) }?.identity,
ProxyRule(action: .direct, match: try RuleMatch(domainSuffix: "new-rule.example")))
XCTAssertNotEqual(runtimeStore.loadDocument(), originalRuntime)
XCTAssertNotNil(runtimeStore.readFailure())
XCTAssertNotEqual(controller.state, .running)
let preview = await workflow.previewCustomRule(draft)
XCTAssertEqual(preview.failure, .staleDraft)
XCTAssertNotNil(controller.runtimeFacts.failure)
}
private func editingWorkflow(_ controller: ProxyRuntimeController) -> RulesWorkflow {
RulesWorkflow(
preset: controller.rulePreset,
loadDocument: { try controller.ruleDocuments.load() },
commitDocument: { await controller.commitRuleDocument($0) },
builtinSnapshots: controller.ruleSnapshots)
}
}