test(tree): add e2e regression test for stale incremental summary handles; fix convertRegistry to preserve custom-configured channel factories#27041
Conversation
There was a problem hiding this comment.
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
convertRegistryby 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
typeToOldFactoryhas 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/validatingoldFactoryin 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(); |
There was a problem hiding this comment.
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.
| const currentDefaultFactoryCtors: Map<string, unknown> = new Map(); | |
| const currentDefaultFactoryCtors: Map<string, Function> = new Map<string, Function>(); |
There was a problem hiding this comment.
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]); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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— fixconvertRegistryfor custom-configured factoriesconvertRegistrypreviously 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 viaconfiguredSharedTree(options)would be silently swapped for a plainSharedTreedefault, stripping options likeForestTypeOptimizedandtreeEncodeType.The fix introduces
currentDefaultFactoryCtors, aMap<string, unknown>from DDS type string to the constructor of the current version's default factory. BecausemakeSharedObjectKindcreates a new class perSharedObjectKind, a factory produced with custom options has a distinct constructor.convertRegistrynow 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 viamakeSharedObjectKind, not just SharedTree.summarizeIncrementalSharedTree.spec.ts— e2e regression testThe 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 asummaryPathpointing to the old parent. On the next summary those paths resolve toundefined, causingCannot read properties of undefined (reading 'trees').The test requires
configuredSharedTreewithForestTypeOptimized(which preserves chunk object identity — the prerequisite for incremental handles to be produced at all). Two cases are covered: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.