Skip to content

feat(webdav): complete RFC 3253 core DeltaV contracts - #31

Merged
AptS-1547 merged 3 commits into
masterfrom
feat/webdav-rfc3253-deltav-core
Aug 7, 2026
Merged

feat(webdav): complete RFC 3253 core DeltaV contracts#31
AptS-1547 merged 3 commits into
masterfrom
feat/webdav-rfc3253-deltav-core

Conversation

@AptS-1547

@AptS-1547 AptS-1547 commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

  • add explicit RFC 3253 versionable, checked-in, checked-out, and immutable-version target facts to the capability snapshot
  • replace report-type-only selection with typed version-tree and nested expand-property request ASTs
  • add requested-property-driven version-tree propstat composition and bounded recursive expand-property execution
  • add VERSION-CONTROL planning, transaction port, result, and response contracts
  • add typed PUT/PROPPATCH/DELETE/COPY/MOVE/UNLOCK versioning mutation plans and DAV preconditions
  • keep OPTIONS, 405, dispatch, body policy, live-property discovery, and report discovery on the same capability snapshot
  • keep the Actix feature transport-only and document the Forge/product ownership boundary

Contract details

  • covers all RFC 3253 auto-version values, including write-lock-associated automatic checkout/checkin behavior
  • preserves explicit property order, namespace, and lexical QName for version-tree
  • bounds REPORT input/XML depth, expansion depth, resource/property counts, Multi-Status items, and output bytes
  • checks cancellation before and between backend lookups and detects active-href cycles
  • groups successful, missing, unknown, and backend-error properties into canonical property-level propstat responses
  • uses the authoritative snapshot for DeltaV and PUT 405 responses so resource-specific Allow remains consistent

Removed obsolete API

This intentionally removes the fixed or duplicate DeltaV paths rather than retaining compatibility facades:

  • validate_version_control_request
  • version_control_request_error_response
  • DavVersionXml
  • dav_version_multistatus_bytes
  • version_tree_non_file_response

Validation

  • cargo fmt --all
  • cargo test -p aster_forge_webdav
  • cargo test -p aster_forge_webdav --features actix
  • cargo clippy -p aster_forge_webdav --all-targets --all-features -- -D warnings
  • cargo check --workspace
  • cargo test --workspace
  • git diff --check

Closes #30

Summary by CodeRabbit

  • 新功能

    • 支持 RFC 3253 DeltaV 版本控制,包括版本状态、自动版本化、锁定及版本删除策略。
    • 新增 VERSION-CONTROL、版本树和 expand-property 报告支持。
    • 版本树响应支持按请求属性生成,并提供资源、属性及错误信息。
    • XML 请求解析新增输入、深度、属性数量和响应大小限制,支持取消及循环检测。
  • 改进

    • PUT 操作会根据版本控制状态执行前置条件检查。
    • OPTIONS、REPORT 和 DAV 能力声明会依据资源版本状态动态调整。
    • 加强 XML 命名空间名称校验,拒绝包含非法字符的 URI。

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AptS-1547, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 43 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a851f09-3f76-4081-a3f2-66a56ee317f4

📥 Commits

Reviewing files that changed from the base of the PR and between f414eb0 and 574207d.

📒 Files selected for processing (5)
  • crates/aster_forge_webdav/tests/capability.rs
  • crates/aster_forge_xml/src/syntax.rs
  • crates/aster_forge_xml/tests/writer.rs
  • crates/aster_forge_xml/tests/xml.rs
  • docs/crates/aster_forge_webdav.md
📝 Walkthrough

Walkthrough

PR 完成 RFC 3253 核心 DeltaV 契约:新增版本能力规划、类型化 REPORT、VERSION-CONTROL、受限 expand-property、版本树响应组合、PUT 前置条件及 XML 命名空间 URI 校验。

Changes

DeltaV 核心协议

Layer / File(s) Summary
版本能力与适用性规划
crates/aster_forge_webdav/src/capability.rs, crates/aster_forge_webdav/src/request.rs, crates/aster_forge_webdav/tests/capability.rs, crates/aster_forge_webdav/tests/preference.rs, crates/aster_forge_webdav/tests/property.rs, docs/crates/aster_forge_webdav.md
能力声明新增版本状态、自动版本、写锁和版本删除策略。规划器按资源状态过滤方法、实时属性和报告,并校验版本能力组合。
类型化 REPORT 与有界属性展开
crates/aster_forge_webdav/src/xml.rs, crates/aster_forge_webdav/src/deltav.rs, crates/aster_forge_webdav/src/lib.rs, crates/aster_forge_webdav/tests/deltav.rs, crates/aster_forge_webdav/tests/xml.rs, crates/aster_forge_webdav/tests/actix.rs
REPORT 解析返回结构化请求。version-tree 支持属性选择和 propstat 分组。expand-property 支持嵌套遍历、深度、资源、属性、输出、取消和循环限制。
版本控制与写入前置条件
crates/aster_forge_webdav/src/deltav.rs, crates/aster_forge_webdav/src/put.rs, crates/aster_forge_webdav/src/xml_response.rs, crates/aster_forge_webdav/tests/deltav.rs, crates/aster_forge_webdav/tests/put.rs, docs/crates/aster_forge_webdav.md
新增 VERSION-CONTROL 计划、执行端口、版本化方法计划和 DAV 错误条件。PUT 在执行前检查版本状态,并映射版本控制前置条件响应。
XML 命名空间名称校验
crates/aster_forge_xml/src/syntax.rs, crates/aster_forge_xml/src/writer.rs, crates/aster_forge_xml/src/lib.rs, crates/aster_forge_xml/tests/*
新增 is_valid_xml_namespace_name。解析和写入路径拒绝包含空白、控制字符及非法 URI 字符的命名空间名称。

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: documentation, enhancement

Poem

版本状态落入快照,
REPORT 沿着边界奔跑。
属性展开不越深、不成环,
VERSION-CONTROL 让副作用先排队。
XML 的脏 URI?
直接拦下,别想混过去。

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了本次 PR 完成 RFC 3253 核心 DeltaV 契约的主要变更,表述简洁且明确。
Linked Issues check ✅ Passed 变更覆盖版本能力、typed REPORT、bounded expand-property、VERSION-CONTROL、版本前置条件、响应组合、快照一致性、测试和文档要求。
Out of Scope Changes check ✅ Passed XML 命名空间校验、测试和文档变更均服务于 DeltaV 请求解析、响应安全及协议契约,没有发现无关代码变更。
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/webdav-rfc3253-deltav-core

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/aster_forge_webdav/src/put.rs (1)

146-155: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

既然引入了 snapshot,那两条 405 路径就应该都带上 Allow header。

这次改动给 put_plan_error_response 加了 snapshot 参数。但 MethodNotAllowedCollectionTarget 仍走 no_store_empty_response(StatusCode::METHOD_NOT_ALLOWED),它只设 Cache-Control: no-store,不设 Allow

而新增的 Versioning 分支(172-173 行)经 versioning_precondition_response 走到 crate::method_not_allowed_response(snapshot),那条路径Allow(tests/deltav.rs 1081 行断言了这一点)。

结果是同一个 PUT 端点在两种拒绝情况下返回形状不同的 405。RFC 7231 section 6.5.5 要求 405 必须携带 Allow

snapshot 现在就在手边,直接用它。

🐛 建议改动
     match error {
         DavPutPlanError::MethodNotAllowed | DavPutPlanError::CollectionTarget => {
-            no_store_empty_response(StatusCode::METHOD_NOT_ALLOWED)
+            crate::method_not_allowed_response(snapshot)
         }

method_not_allowed_response 是否已包含 Cache-Control: no-store 需要确认。若未包含,请在返回前补上,以保持既有的缓存语义。

#!/bin/bash
ast-grep run --pattern 'pub fn method_not_allowed_response($$$) -> $RET { $$$ }' --lang rust crates/aster_forge_webdav/src
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aster_forge_webdav/src/put.rs` around lines 146 - 155, Update
put_plan_error_response so the MethodNotAllowed and CollectionTarget branches
use method_not_allowed_response(snapshot), ensuring both 405 responses include
Allow from the capability snapshot. Preserve the existing no-store caching
behavior by verifying that method_not_allowed_response provides Cache-Control:
no-store and adding it there if necessary.
🧹 Nitpick comments (5)
crates/aster_forge_webdav/tests/capability.rs (1)

899-943: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

三个新错误变体没有失败路径断言。

这个测试覆盖了 AutoCheckoutLockNotApplicableAutoCheckoutLockWithoutApplicableModeVersionDeletePolicyNotApplicable,写得很干净。但 validate_extensions 这次一共加了六条规则,剩下三个变体全程没人管:

  • VersionControlWithoutTarget:声明了 VersionControl 包,但 state 留在 Unsupported
  • VersioningTargetWithoutPackage:设了 state,忘了加包。
  • AutoVersionNotApplicableauto_version != None 配上 VersionableVersion

前两条是整个版本事实一致性的门禁——plan_capabilities 后面所有的属性过滤、报告过滤、方法过滤都建立在"包与状态必须成对出现"这个前提上。这道门坏了没人会发现。

编码规范要求高影响集成同时测试成功路径和失败路径。照你现有的模式补三段就行,成本很低。

✅ 建议补充的断言
     assert_eq!(
         plan_capabilities(invalid_delete),
         Err(DavCapabilityPlanError::VersionDeletePolicyNotApplicable)
     );
+
+    let mut package_without_state = declaration(DavResourceState::File, &[DavMethod::Options]);
+    package_without_state.extensions =
+        DavExtensionSet::from_packages(&[DavExtensionPackage::VersionControl]);
+    assert_eq!(
+        plan_capabilities(package_without_state),
+        Err(DavCapabilityPlanError::VersionControlWithoutTarget)
+    );
+
+    let mut state_without_package = declaration(DavResourceState::File, &[DavMethod::Options]);
+    state_without_package.versioning = DavVersioningCapabilities {
+        state: DavVersioningState::CheckedIn,
+        ..DavVersioningCapabilities::default()
+    };
+    assert_eq!(
+        plan_capabilities(state_without_package),
+        Err(DavCapabilityPlanError::VersioningTargetWithoutPackage)
+    );
+
+    let mut auto_version_on_versionable =
+        declaration(DavResourceState::File, &[DavMethod::Options]);
+    auto_version_on_versionable.extensions =
+        DavExtensionSet::from_packages(&[DavExtensionPackage::VersionControl]);
+    auto_version_on_versionable.versioning = DavVersioningCapabilities {
+        state: DavVersioningState::Versionable,
+        auto_version: DavAutoVersion::CheckoutCheckin,
+        ..DavVersioningCapabilities::default()
+    };
+    assert_eq!(
+        plan_capabilities(auto_version_on_versionable),
+        Err(DavCapabilityPlanError::AutoVersionNotApplicable)
+    );
 }

依据编码规范:"High-impact integrations must test success and failure paths, error-mapping boundaries" 以及 "integration tests should cover the behavior required by the changed mechanism"。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aster_forge_webdav/tests/capability.rs` around lines 899 - 943, 在
deltav_runtime_facts_reject_inapplicable_auto_checkout_and_delete_policy
中按现有测试模式补充三个失败路径断言:VersionControl 包配合 Unsupported 状态应返回
VersionControlWithoutTarget;设置版本化 state 但不包含 VersionControl 包应返回
VersioningTargetWithoutPackage;在 Versionable 或 Version 状态下设置非 None 的
auto_version 应返回 AutoVersionNotApplicable。确保每个场景都通过 plan_capabilities 验证对应错误变体。

Source: Coding guidelines

crates/aster_forge_webdav/tests/deltav.rs (2)

807-807: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

两个测试的名字和它们实际断言的内容不符。

expand_property_enforces_cycle_limits_cancellation_and_backend_failure(807 行)没有任何 cancellation 断言,也没有 backend failure 断言。那两项在 expand_property_checks_cancellation_before_and_between_backend_lookupsexpand_property_preserves_backend_failure_classification 里。

expand_property_enforces_depth_property_value_and_output_byte_boundaries(928 行)没有 property value 断言,也没有 output byte 断言。那两项在 expand_property_rejects_non_href_nested_values_and_bounds_output_bytes 里。

名字撒谎的测试比没有测试更麻烦——下次有人 grep "cancellation" 会以为已经覆盖了。改成 expand_property_enforces_cycle_and_resource_limitsexpand_property_enforces_depth_and_property_count_limits 就行。

♻️ 建议改名
-fn expand_property_enforces_cycle_limits_cancellation_and_backend_failure() {
+fn expand_property_enforces_cycle_and_resource_limits() {
-fn expand_property_enforces_depth_property_value_and_output_byte_boundaries() {
+fn expand_property_enforces_depth_and_property_count_limits() {

Also applies to: 928-928

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aster_forge_webdav/tests/deltav.rs` at line 807, Rename the test at
`expand_property_enforces_cycle_limits_cancellation_and_backend_failure` to
`expand_property_enforces_cycle_and_resource_limits`, and rename
`expand_property_enforces_depth_property_value_and_output_byte_boundaries` to
`expand_property_enforces_depth_and_property_count_limits`. Change only these
test function names; leave their assertions and implementations unchanged.

205-246: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

几个限制和错误映射没有测试覆盖。

覆盖面整体很扎实,取消时序那部分(863-901 行)尤其精确。但有几处漏了:

  1. report_limits_cover_input_xml_depth_and_nested_selection_depth(205-246 行):测试名说覆盖 xml depth,实际 maximum_xml_depth 一直固定为 8,没有精确边界用例。DavReportLimits::is_valid 检查 5 个字段,测试只覆盖 maximum_input_bytes = 0 一种零值。
  2. report_errors_map_xml_unknown_and_unavailable_categories(248-284 行):没测 DavReportPlanError::InvalidLimits 映射到 500。
  3. version_control_plans_versionable_and_already_controlled_targets(286-324 行):只测了 CheckedOutAlreadyControlled,没测 CheckedIn。也没测 VersionUnsupported 状态下 VERSION-CONTROL 被拒绝。docs/crates/aster_forge_webdav.md 的 569-570 行明确要求覆盖 "versionable/checked-in/checked-out/immutable/collection/unmapped planner"。
  4. expand_property_failures_map_before_response_start(1088-1120 行):漏了 ResourceLimitExceededPropertyLimitExceededInvalidLimits,以及 MultiStatus 的各个子类映射。

immutable version 上 VERSION-CONTROL 必须被拒绝,这条是 RFC 3253 的硬要求,我建议优先补。

As per coding guidelines: "High-impact integrations must test success and failure paths, error-mapping boundaries, retry/degradation/cancellation behavior".

💚 建议补充的用例
#[test]
fn version_control_is_rejected_for_immutable_and_unsupported_targets() {
    for state in [DavVersioningState::Version, DavVersioningState::Unsupported] {
        let snapshot = versioning_snapshot(
            DavResourceState::File,
            state,
            &[DavMethod::Options, DavMethod::VersionControl],
        );
        assert_eq!(
            plan_version_control_request(&snapshot, b""),
            Err(DavVersionControlPlanError::MethodNotAllowed),
            "{state:?}"
        );
    }

    let checked_in = versioning_snapshot(
        DavResourceState::File,
        DavVersioningState::CheckedIn,
        &[DavMethod::Options, DavMethod::VersionControl],
    );
    assert_eq!(
        plan_version_control_request(&checked_in, b"")
            .expect("checked-in plan")
            .action,
        DavVersionControlAction::AlreadyControlled
    );
}

#[test]
fn report_limits_reject_every_zero_field_and_map_to_a_server_error() {
    let snapshot = report_snapshot(DavVersioningState::CheckedIn);
    let body = br#"<D:version-tree xmlns:D="DAV:"/>"#;
    for limits in [
        DavReportLimits::new(0, 8, 2, 8, 8, DavMultiStatusLimits::default()),
        DavReportLimits::new(4096, 0, 2, 8, 8, DavMultiStatusLimits::default()),
        DavReportLimits::new(4096, 8, 0, 8, 8, DavMultiStatusLimits::default()),
        DavReportLimits::new(4096, 8, 2, 0, 8, DavMultiStatusLimits::default()),
        DavReportLimits::new(4096, 8, 2, 8, 0, DavMultiStatusLimits::default()),
    ] {
        assert_eq!(
            plan_report_request_with_limits(&snapshot, body, None, limits),
            Err(DavReportPlanError::InvalidLimits)
        );
    }
    let response = report_plan_error_response(
        &DavReportPlanError::InvalidLimits,
        &ReportResponsePolicy,
    )
    .expect("invalid limits response");
    assert_eq!(response.status, StatusCode::INTERNAL_SERVER_ERROR);
}

Also applies to: 248-284, 286-324, 1088-1120

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aster_forge_webdav/tests/deltav.rs` around lines 205 - 246, Expand the
affected WebDAV tests to cover every zero-valued field validated by
DavReportLimits::is_valid, including the maximum_xml_depth boundary, and verify
InvalidLimits maps to INTERNAL_SERVER_ERROR through report_plan_error_response.
Extend version-control planning tests around plan_version_control_request to
reject Version and Unsupported immutable targets while preserving CheckedIn’s
AlreadyControlled result. Add expand-property failure cases for
ResourceLimitExceeded, PropertyLimitExceeded, InvalidLimits, and each
MultiStatus error mapping in the existing response-mapping test.

Source: Coding guidelines

crates/aster_forge_webdav/src/xml.rs (1)

484-486: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

零限制检查的错误分类不对,而且这条路径走不到。

plan_report_request_with_limits 在 189-191 行已经用 limits.is_valid() 拦截了任何为零的限制,返回 DavReportPlanError::InvalidLimits。所以这里的 484 行判断在实际调用链上不可达。

即使假设未来有别的调用方,把"服务器配置了零限制"分类成 DavXmlError::InvalidGrammar 也是错的——它会让 xml_request_error_response 生成 400,把服务器配置错误算到客户端头上。InvalidLimits 对应的是 500。

建议把这个判断改成 debug_assert!,或者直接删掉,让唯一的限制校验入口留在 DavReportLimits::is_valid

♻️ 建议改动
 fn parse_expand_property<S: AsRef<[u8]>>(
     root: ElementRef<'_, S>,
     maximum_depth: usize,
     maximum_properties: usize,
 ) -> Result<Vec<DavExpandPropertySelection>, DavXmlError> {
-    if maximum_depth == 0 || maximum_properties == 0 {
-        return Err(DavXmlError::InvalidGrammar);
-    }
+    debug_assert!(
+        maximum_depth != 0 && maximum_properties != 0,
+        "callers must validate limits through DavReportLimits::is_valid"
+    );
     require_element_content(root)?;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aster_forge_webdav/src/xml.rs` around lines 484 - 486, Remove the
zero-limit check in the XML parsing path around maximum_depth and
maximum_properties, or replace it with a debug_assert! that treats zero limits
as an internal invariant violation rather than DavXmlError::InvalidGrammar. Keep
limit validation exclusively in DavReportLimits::is_valid and preserve
InvalidLimits handling in plan_report_request_with_limits.
crates/aster_forge_webdav/tests/xml_response.rs (1)

5-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

删掉的 DeltaV 转义测试没有在新位置补回来。

随导入一起删除的 deltav_multistatus_escapes_values_and_keeps_protocol_property_order 覆盖了两件事:值转义和协议属性顺序。

顺序那部分现在由 tests/deltav.rs 的 version_tree_response_groups_success_missing_unknown_and_backend_errors 间接覆盖。转义那部分没有替代——新测试断言的是 xml.contains("V1")xml.contains("Z:custom") 这类普通字符串,没有任何用例把 <&" 或控制字符喂给 DavVersionProperty::textDavVersionProperty::hrefs

产品返回的 version-name、comment、creator 都是任意文本,href 也可能带 &。这条路径现在没有转义测试。

docs/crates/aster_forge_webdav.md 的 550 行把"异常旧值转义"列为 XML response 测试要求。

As per coding guidelines: "Test code may be direct, but must not hide real boundary problems; integration tests should cover the behavior required by the changed mechanism."

💚 建议在 tests/deltav.rs 补充
#[test]
fn version_tree_response_escapes_product_text_and_hrefs() {
    let request = DavVersionTreeRequest {
        properties: Some(vec![dav_property("comment"), dav_property("predecessor-set")]),
        depth: Depth::Zero,
    };
    let response = version_tree_response(
        &request,
        vec![DavVersionReportItem {
            href: "/versions/a&b".to_owned(),
            properties: vec![
                DavVersionProperty::text(dav_property("comment"), r#"<script>&"'"#),
                DavVersionProperty::hrefs(
                    dav_property("predecessor-set"),
                    ["/versions/x?y=1&z=2".to_owned()],
                ),
            ],
        }],
    )
    .expect("escaped version-tree response");
    let xml = body_text(&response);
    assert!(!xml.contains("<script>"), "{xml}");
    assert!(xml.contains("&lt;script&gt;"), "{xml}");
    assert!(xml.contains("&amp;"), "{xml}");
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aster_forge_webdav/tests/xml_response.rs` around lines 5 - 8, 在
tests/deltav.rs 的 version_tree_response 测试区域补回 XML 转义覆盖,新增针对
version_tree_response 的测试,向 DavVersionProperty::text 和 DavVersionProperty::hrefs
传入包含 <、&、引号及控制字符的文本与 href,并断言响应只包含对应的 XML 转义结果而不包含未转义内容。保留现有协议属性顺序测试,不重复改动其覆盖范围。

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/aster_forge_webdav/src/capability.rs`:
- Around line 1188-1205: 将 VersionControlMethodNotApplicable 的状态适用性检查移到
extension_methods_for_declaration 与 extension-method 循环之前,并仅在声明包含 VersionControl
时执行;保留无包声明仍返回 ExtensionMethodWithoutPackage 的现有行为,同时确保 Version、Unsupported
等不适用状态返回 VersionControlMethodNotApplicable。
- Around line 1148-1163: 在 validate_compliance 和 validate_extensions 中补充
declaration.versioning.write_locked 与 declaration.locking 的一致性校验:write_locked 为
true 时必须具备启用的 DAV 写锁能力,禁止 locking 为
DavLockingCapability::Disabled;若该字段表示产品内部锁,则明确其语义和适用边界,并避免让 deltav.rs 将其误当作 DAV
锁使用。

In `@crates/aster_forge_webdav/src/deltav.rs`:
- Around line 238-241: Remove the hard-coded "Invalid REPORT limits" response
body from both InvalidLimits branches in the REPORT response handling, including
expand_property_error_response, and return the same empty-body response used by
the other branches. Preserve the existing INTERNAL_SERVER_ERROR status while
leaving product-facing messaging to the API layer.
- Around line 707-709: 统一 DavReportLimits 中解析阶段与执行阶段的预算语义:为请求 AST 深度/selection
数量和实际资源展开跳数/后端查询次数使用独立且明确命名的限制字段。同步更新
parse_expand_property_selection、plan_report_request_with_limits、execute_expand_property
及其调用方、测试和字段文档,确保各阶段只使用对应预算并保留超限错误行为。
- Around line 795-797: Update the expand-property handling around
DavExpandPropertyValue::Element so a client-requested non-href nested value is
reported in that property’s propstat with the appropriate 403 or 404 status
while processing of other selections continues. Remove InvalidPropertyValue from
the client-error path, or retain it only for genuine internal invariant
violations; update expand_property_error_response and
expand_property_rejects_non_href_nested_values_and_bounds_output_bytes to verify
the REPORT no longer becomes a 500.

In `@crates/aster_forge_webdav/src/lib.rs`:
- Around line 84-95: 更新 docs/crates/aster_forge_webdav.md,在 DeltaV 或版本树响应说明中补充
DavVersionXml 与 dav_version_multistatus_bytes 已移除的迁移说明,明确引导使用
version_tree_response 或 version_tree_response_with_limits,并保持现有 API 文档结构不变。

In `@crates/aster_forge_webdav/src/mutation.rs`:
- Around line 955-957: 在处理 DavMutationStop::Backend 的分支中,先将当前
frames[frame].failed 设为 true,再调用 propagate_frame_failure(frame,
frames),确保当前帧及其父帧正确传播失败状态;同时补充或更新测试,断言该停止路径会标记当前帧失败并验证适用的父帧传播行为。

In `@crates/aster_forge_webdav/src/xml.rs`:
- Around line 530-537: 在解析 property 的 namespace 处补充 URI
语法校验,不能仅检查非空和首尾空白;应拒绝尖括号、引号及控制字符等无法作为 XMLNS 值的内容,并返回
DavXmlError::InvalidGrammar。优先复用现有 URI 校验能力;否则在 writer 的
validate_namespace_binding 中统一执行该校验,确保 deltav 与 xml_response 写入 namespaces
前不会透传非法值。

---

Outside diff comments:
In `@crates/aster_forge_webdav/src/put.rs`:
- Around line 146-155: Update put_plan_error_response so the MethodNotAllowed
and CollectionTarget branches use method_not_allowed_response(snapshot),
ensuring both 405 responses include Allow from the capability snapshot. Preserve
the existing no-store caching behavior by verifying that
method_not_allowed_response provides Cache-Control: no-store and adding it there
if necessary.

---

Nitpick comments:
In `@crates/aster_forge_webdav/src/xml.rs`:
- Around line 484-486: Remove the zero-limit check in the XML parsing path
around maximum_depth and maximum_properties, or replace it with a debug_assert!
that treats zero limits as an internal invariant violation rather than
DavXmlError::InvalidGrammar. Keep limit validation exclusively in
DavReportLimits::is_valid and preserve InvalidLimits handling in
plan_report_request_with_limits.

In `@crates/aster_forge_webdav/tests/capability.rs`:
- Around line 899-943: 在
deltav_runtime_facts_reject_inapplicable_auto_checkout_and_delete_policy
中按现有测试模式补充三个失败路径断言:VersionControl 包配合 Unsupported 状态应返回
VersionControlWithoutTarget;设置版本化 state 但不包含 VersionControl 包应返回
VersioningTargetWithoutPackage;在 Versionable 或 Version 状态下设置非 None 的
auto_version 应返回 AutoVersionNotApplicable。确保每个场景都通过 plan_capabilities 验证对应错误变体。

In `@crates/aster_forge_webdav/tests/deltav.rs`:
- Line 807: Rename the test at
`expand_property_enforces_cycle_limits_cancellation_and_backend_failure` to
`expand_property_enforces_cycle_and_resource_limits`, and rename
`expand_property_enforces_depth_property_value_and_output_byte_boundaries` to
`expand_property_enforces_depth_and_property_count_limits`. Change only these
test function names; leave their assertions and implementations unchanged.
- Around line 205-246: Expand the affected WebDAV tests to cover every
zero-valued field validated by DavReportLimits::is_valid, including the
maximum_xml_depth boundary, and verify InvalidLimits maps to
INTERNAL_SERVER_ERROR through report_plan_error_response. Extend version-control
planning tests around plan_version_control_request to reject Version and
Unsupported immutable targets while preserving CheckedIn’s AlreadyControlled
result. Add expand-property failure cases for ResourceLimitExceeded,
PropertyLimitExceeded, InvalidLimits, and each MultiStatus error mapping in the
existing response-mapping test.

In `@crates/aster_forge_webdav/tests/xml_response.rs`:
- Around line 5-8: 在 tests/deltav.rs 的 version_tree_response 测试区域补回 XML
转义覆盖,新增针对 version_tree_response 的测试,向 DavVersionProperty::text 和
DavVersionProperty::hrefs 传入包含 <、&、引号及控制字符的文本与 href,并断言响应只包含对应的 XML
转义结果而不包含未转义内容。保留现有协议属性顺序测试,不重复改动其覆盖范围。
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b0b7ab4-61f0-488b-aeea-7a2f90aaf419

📥 Commits

Reviewing files that changed from the base of the PR and between 1fc991f and e6f5574.

📒 Files selected for processing (19)
  • crates/aster_forge_webdav/src/capability.rs
  • crates/aster_forge_webdav/src/deltav.rs
  • crates/aster_forge_webdav/src/lib.rs
  • crates/aster_forge_webdav/src/mutation.rs
  • crates/aster_forge_webdav/src/put.rs
  • crates/aster_forge_webdav/src/request.rs
  • crates/aster_forge_webdav/src/xml.rs
  • crates/aster_forge_webdav/src/xml_response.rs
  • crates/aster_forge_webdav/tests/actix.rs
  • crates/aster_forge_webdav/tests/capability.rs
  • crates/aster_forge_webdav/tests/deltav.rs
  • crates/aster_forge_webdav/tests/mutation.rs
  • crates/aster_forge_webdav/tests/preference.rs
  • crates/aster_forge_webdav/tests/property.rs
  • crates/aster_forge_webdav/tests/put.rs
  • crates/aster_forge_webdav/tests/response.rs
  • crates/aster_forge_webdav/tests/xml.rs
  • crates/aster_forge_webdav/tests/xml_response.rs
  • docs/crates/aster_forge_webdav.md

Comment thread crates/aster_forge_webdav/src/capability.rs
Comment thread crates/aster_forge_webdav/src/capability.rs Outdated
Comment thread crates/aster_forge_webdav/src/deltav.rs Outdated
Comment thread crates/aster_forge_webdav/src/deltav.rs
Comment thread crates/aster_forge_webdav/src/deltav.rs
Comment thread crates/aster_forge_webdav/src/lib.rs
Comment thread crates/aster_forge_webdav/src/mutation.rs
Comment thread crates/aster_forge_webdav/src/xml.rs
Add typed REPORT parsing, bounded version-tree and expand-property composition, versioning resource facts, VERSION-CONTROL transaction planning, mutation preconditions, one-snapshot discovery consistency, Actix coverage, and complete boundary tests.\n\nCloses #30
@AptS-1547
AptS-1547 force-pushed the feat/webdav-rfc3253-deltav-core branch from e6f5574 to 063fc6a Compare August 6, 2026 18:41
@AptS-1547

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
crates/aster_forge_webdav/tests/capability.rs (2)

933-947: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

AutoCheckoutLockWithoutApplicableMode 少测了 DavAutoVersion::None 这条路。

现在只测了 CheckoutCheckin。但 None 同样会落到 capability.rs 第 1159 行的分支:state == CheckedOut 时第 1142 行的 auto_version != None 为假,不会提前拦截,于是 auto_checkout_lock 校验放行到模式检查。

这条路径是 auto_checkout_lockauto_version 之间最容易被误改的耦合点。补一个用例即可。

💚 建议补充用例
     assert_eq!(
         plan_capabilities(invalid_mode),
         Err(DavCapabilityPlanError::AutoCheckoutLockWithoutApplicableMode)
     );
+
+    let mut no_mode = declaration(DavResourceState::File, &locking_methods);
+    no_mode.locking = DavLockingCapability::Class2;
+    no_mode.extensions = DavExtensionSet::from_packages(&[DavExtensionPackage::VersionControl]);
+    no_mode.versioning = DavVersioningCapabilities {
+        state: DavVersioningState::CheckedOut,
+        auto_version: DavAutoVersion::None,
+        write_locked: true,
+        auto_checkout_lock: true,
+        allow_version_delete: false,
+    };
+    assert_eq!(
+        plan_capabilities(no_mode),
+        Err(DavCapabilityPlanError::AutoCheckoutLockWithoutApplicableMode)
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aster_forge_webdav/tests/capability.rs` around lines 933 - 947, 在
capability 测试中补充覆盖 DavAutoVersion::None 的用例,使用现有 invalid_mode 配置并将 auto_version
设为 None,保持 state 为 CheckedOut、auto_checkout_lock 为 true 以及无适用模式的条件不变。断言
plan_capabilities(invalid_mode) 仍返回 AutoCheckoutLockWithoutApplicableMode,并保留现有
CheckoutCheckin 用例。

438-453: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

versioning_resource 的硬编码资源列表会与 descriptor 脱钩。

第 438-441 行手写了 File | Collection | MountRoot。第 445 行已经用 descriptor().resources.contains(resource) 过滤过一次。这两处描述同一件事,但一个是常量、一个是数据源。

后果不是现在坏,是以后坏:DavVersionControlExtensionresources 一旦加进 Principal,第 445 行会放行,第 446 行会拦下,于是这个资源状态的 VersionControl 组合永远不进测试。你小子改了 descriptor 还以为测试覆盖了。

直接从 descriptor 推导即可。

♻️ 可选重构:从 descriptor 推导
-        let versioning_resource = matches!(
-            resource,
-            DavResourceState::File | DavResourceState::Collection | DavResourceState::MountRoot
-        );
+        let versioning_resource = DavExtensionPackage::VersionControl
+            .descriptor()
+            .resources
+            .contains(resource);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aster_forge_webdav/tests/capability.rs` around lines 438 - 453, Remove
the hard-coded resource list used to compute versioning_resource and derive it
from the VersionControl package descriptor instead. Update the filtering around
DavExtensionPackage::VersionControl so any resource declared by that descriptor
receives the version-control combination, keeping the existing prerequisite and
package filtering behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/aster_forge_xml/src/syntax.rs`:
- Around line 77-83: Update is_valid_xml_namespace_name and
validate_namespace_binding to perform complete URI-reference validation,
rejecting incomplete or invalid percent-encoded sequences such as urn:bad%,
urn:bad%2, urn:bad%ZZ, and urn:%0G rather than only applying character
blacklists. Preserve the existing empty-value exception used to remove the
default namespace, and add regression coverage for at least urn:bad% and
urn:bad%2 so reader and writer paths inherit the corrected behavior.

In `@docs/crates/aster_forge_webdav.md`:
- Line 385: 补充该 DeltaV 语义说明中的 RFC 3253 引用,覆盖 PUT、PROPPATCH、DELETE、COPY、MOVE 和
UNLOCK 对应的 Section 3.9、3.10–3.16,确保引用范围覆盖 planner 合同;或者将表述收窄为仅涵盖
VERSION-CONTROL、version-tree 和 expand-property。
- Around line 117-119: 更新文档中 `expand-property` 的描述:将“只接受 DTD 声明的 nested
`DAV:property`”改为“只接受符合 RFC grammar 的 nested `DAV:property`”,明确 RFC grammar
描述的是请求结构而非客户端必须携带 DTD,并保留后文对 DTD/ENTITY 的拒绝行为。

---

Nitpick comments:
In `@crates/aster_forge_webdav/tests/capability.rs`:
- Around line 933-947: 在 capability 测试中补充覆盖 DavAutoVersion::None 的用例,使用现有
invalid_mode 配置并将 auto_version 设为 None,保持 state 为 CheckedOut、auto_checkout_lock
为 true 以及无适用模式的条件不变。断言 plan_capabilities(invalid_mode) 仍返回
AutoCheckoutLockWithoutApplicableMode,并保留现有 CheckoutCheckin 用例。
- Around line 438-453: Remove the hard-coded resource list used to compute
versioning_resource and derive it from the VersionControl package descriptor
instead. Update the filtering around DavExtensionPackage::VersionControl so any
resource declared by that descriptor receives the version-control combination,
keeping the existing prerequisite and package filtering behavior unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c74a5020-4499-4f71-bcab-316d7997429d

📥 Commits

Reviewing files that changed from the base of the PR and between 1fc991f and f414eb0.

📒 Files selected for processing (22)
  • crates/aster_forge_webdav/src/capability.rs
  • crates/aster_forge_webdav/src/deltav.rs
  • crates/aster_forge_webdav/src/lib.rs
  • crates/aster_forge_webdav/src/put.rs
  • crates/aster_forge_webdav/src/request.rs
  • crates/aster_forge_webdav/src/xml.rs
  • crates/aster_forge_webdav/src/xml_response.rs
  • crates/aster_forge_webdav/tests/actix.rs
  • crates/aster_forge_webdav/tests/capability.rs
  • crates/aster_forge_webdav/tests/deltav.rs
  • crates/aster_forge_webdav/tests/preference.rs
  • crates/aster_forge_webdav/tests/property.rs
  • crates/aster_forge_webdav/tests/put.rs
  • crates/aster_forge_webdav/tests/response.rs
  • crates/aster_forge_webdav/tests/xml.rs
  • crates/aster_forge_webdav/tests/xml_response.rs
  • crates/aster_forge_xml/src/lib.rs
  • crates/aster_forge_xml/src/syntax.rs
  • crates/aster_forge_xml/src/writer.rs
  • crates/aster_forge_xml/tests/writer.rs
  • crates/aster_forge_xml/tests/xml.rs
  • docs/crates/aster_forge_webdav.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • crates/aster_forge_webdav/tests/actix.rs
  • crates/aster_forge_webdav/tests/property.rs
  • crates/aster_forge_webdav/tests/xml_response.rs
  • crates/aster_forge_webdav/src/request.rs
  • crates/aster_forge_webdav/tests/preference.rs
  • crates/aster_forge_webdav/src/put.rs
  • crates/aster_forge_webdav/tests/put.rs
  • crates/aster_forge_webdav/tests/response.rs
  • crates/aster_forge_webdav/tests/xml.rs
  • crates/aster_forge_webdav/src/lib.rs
  • crates/aster_forge_webdav/src/xml.rs
  • crates/aster_forge_webdav/src/deltav.rs

Comment thread crates/aster_forge_xml/src/syntax.rs
Comment thread docs/crates/aster_forge_webdav.md
Comment thread docs/crates/aster_forge_webdav.md Outdated
@AptS-1547

Copy link
Copy Markdown
Member Author

Latest review nitpicks are also addressed in 574207d:

  • AutoCheckoutLockWithoutApplicableMode now covers both CheckoutCheckin and None.
  • maximum-compatible package tests derive VersionControl applicability from the package descriptor instead of a duplicated resource list.

Focused tests, both WebDAV feature sets, strict Clippy, workspace check, and workspace tests all pass.

@AptS-1547
AptS-1547 merged commit 291b244 into master Aug 7, 2026
6 checks passed
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.91069% with 45 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/aster_forge_webdav/src/deltav.rs 91.83% 41 Missing ⚠️
crates/aster_forge_webdav/src/capability.rs 95.65% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@AptS-1547
AptS-1547 deleted the feat/webdav-rfc3253-deltav-core branch August 17, 2026 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(webdav): complete RFC 3253 core DeltaV protocol contracts

1 participant