Skip to content

test(tree): add e2e regression test for stale incremental summary handles; fix convertRegistry to preserve custom-configured channel factories#27041

Open
justus-camp-microsoft wants to merge 7 commits intomicrosoft:mainfrom
justus-camp-microsoft:compat_tree_factory
Open

test(tree): add e2e regression test for stale incremental summary handles; fix convertRegistry to preserve custom-configured channel factories#27041
justus-camp-microsoft wants to merge 7 commits intomicrosoft:mainfrom
justus-camp-microsoft:compat_tree_factory

Conversation

@justus-camp-microsoft
Copy link
Copy Markdown

Description

Adds an end-to-end regression test for the stale incremental summary handle path bug fixed in #26990, and fixes the compat test infrastructure that was silently dropping the custom configuration the test depends on.

compatUtils.ts — fix convertRegistry for custom-configured factories

convertRegistry previously replaced every user-provided channel factory with the compat-version default for that DDS type, unconditionally. This drops any custom configuration — in particular, a factory created via configuredSharedTree(options) would be silently swapped for a plain SharedTree default, stripping options like ForestTypeOptimized and treeEncodeType.

The fix introduces currentDefaultFactoryCtors, a Map<string, unknown> from DDS type string to the constructor of the current version's default factory. Because makeSharedObjectKind creates a new class per SharedObjectKind, a factory produced with custom options has a distinct constructor. convertRegistry now checks constructor identity: if a factory's constructor matches the known default, it is swapped for the compat version as before; otherwise it is preserved as-is. This is general-purpose — it applies to any DDS built via makeSharedObjectKind, not just SharedTree.

summarizeIncrementalSharedTree.spec.ts — e2e regression test

The stale handle path bug manifests when a parent incremental chunk is re-encoded (getting a new referenceId), but child chunks promoted to handles still hold a summaryPath pointing to the old parent. On the next summary those paths resolve to undefined, causing Cannot read properties of undefined (reading 'trees').

The test requires configuredSharedTree with ForestTypeOptimized (which preserves chunk object identity — the prerequisite for incremental handles to be produced at all). Two cases are covered:

  • Parent chunk changes causing child handles to go stale
  • Depth-0 changes causing copy-propagation of tracking entries, after which a subsequent parent re-encode must still resolve the copied handles

Reviewer Guidance

The review process is outlined on this wiki page.

This PR is a companion to #26990 and should be merged after it — without the fix the two tests intentionally fail, confirming they catch the bug.

Copilot AI review requested due to automatic review settings April 14, 2026 21:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds an E2E regression test covering stale incremental summary handle paths for SharedTree, and fixes compat test infrastructure so custom-configured DDS factories (e.g., configuredSharedTree(...)) aren’t silently replaced by defaults.

Changes:

  • Preserve custom-configured channel factories in convertRegistry by swapping only known default factories to compat equivalents.
  • Add E2E regression coverage for incremental summary handle path staleness across multiple summaries and copy-propagation scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/test/test-version-utils/src/compatUtils.ts Adjusts registry conversion to keep non-default (custom-configured) factories intact.
packages/test/test-end-to-end-tests/src/test/summarization/summarizeIncrementalSharedTree.spec.ts Adds E2E regression tests for incremental summary handle path correctness across summaries.
Comments suppressed due to low confidence (1)

packages/test/test-version-utils/src/compatUtils.ts:1

  • The code throws when typeToOldFactory has no entry even if the factory is custom and would be preserved as-is. This makes “preserve custom-configured factory” ineffective for any factory types that aren’t present in the compat map. Fix by only looking up/validating oldFactory in the branch that actually performs the conversion-to-compat (i.e., after confirming the constructor matches the known default).
/*!

* check in {@link convertRegistry} uses this to let those factories pass through
* without being swapped to the compat version.
*/
const currentDefaultFactoryCtors: Map<string, unknown> = new Map();
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

Map<string, unknown> makes the later identity comparison (factory.constructor === currentDefaultCtor) less type-safe and easier to misuse. Consider typing the map values as Function (or a more specific constructor type) so the intent is explicit and the comparison is checked by TypeScript.

Suggested change
const currentDefaultFactoryCtors: Map<string, unknown> = new Map();
const currentDefaultFactoryCtors: Map<string, Function> = new Map<string, Function>();

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I think unknown is better here

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
oldRegistry.push([key, oldFactory]);
} else {
// Custom-configured factory — preserve as-is.
oldRegistry.push([key, factory]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should work fine. However, the disadvantage here is that any test using a custom factory won't be testing compat. This is problematic because the test authors may assume that since its running in different compat modes, they are getting coverage but they are actually not (this has happened in the past).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should remove this workaround altogether and use the pattern where the dds factory is retrieved from the apis: CompatApis that is provided as part of the describeCompat block.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Most of the tests have already been converted to use the above pattern. However, there may be some left so updating them, removing the registry conversation should be an alternative.
It would also be great if we can instead add validation that the version of the factory matches the current compat version the test is running in so that tests don't accidentally use the non-comat APIs.

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.

3 participants