Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5923ea3
fix(container-loader): dispose SerializedStateManager instances in te…
alexvy86 Mar 5, 2026
3731e73
fix(container-runtime): dispose ContainerRuntime instances in tests t…
alexvy86 Mar 5, 2026
00020af
fix(tree): remove --exit flag by properly disposing TestTreeProvider …
alexvy86 Mar 6, 2026
5257835
fix(local-server-tests): properly dispose LocalDeltaConnectionServer …
alexvy86 Mar 6, 2026
6661ee7
fix(fluid-telemetry,react): remove --exit flag by cleaning up Applica…
alexvy86 Mar 6, 2026
da562c9
fix(inventory-app): remove --exit flag by setting up JSDOM before Qui…
alexvy86 Mar 6, 2026
fadbe1e
fix(experimental): remove --exit flag from dds/tree and property-dds …
alexvy86 Mar 6, 2026
3dd5255
fix(dds): remove --exit flag from memory/benchmark test mocharcs
alexvy86 Mar 6, 2026
7b4ab5d
fix(test-end-to-end-tests): remove --exit flag by fixing LocalServerT…
alexvy86 Mar 6, 2026
fa80b89
fix: remove --exit flag from webflow, odsp-client, and snapshots pack…
alexvy86 Mar 6, 2026
63ff049
fix(experimental,e2e-tests): remove --exit flag from PropertyDDS and …
alexvy86 Mar 6, 2026
02b75e2
Merge remote-tracking branch 'upstream/main' into ralph-removes-mocha…
alexvy86 Mar 6, 2026
d906f29
Remove file
alexvy86 Mar 6, 2026
a6d6b67
Update investigation file
alexvy86 Mar 6, 2026
7d844b0
Formatting
alexvy86 Mar 6, 2026
387e188
fix(dds/tree,fluid-telemetry): fix lint errors in dispose and stopPol…
alexvy86 Mar 6, 2026
3d11ab7
fix(local-server-tests): fix timer leaks so tests exit without --exit…
alexvy86 Mar 6, 2026
a687f40
fix(property-dds): dispose containers in afterEach to prevent timer l…
alexvy86 Mar 7, 2026
8f4a2bd
fix(local-server-stress-tests): remove --exit flag as cleanup is alre…
alexvy86 Mar 7, 2026
b150f37
docs: update investigation notes with container disposal pattern
alexvy86 Mar 7, 2026
3eece80
fix(fluid-telemetry): clear heartbeat interval on container dispose t…
alexvy86 Mar 7, 2026
7d916d0
fix(react,fluid-static): fix timer leaks so tests exit without --exit…
alexvy86 Mar 7, 2026
2d8515b
docs: update investigation notes with react/fluid-static timer leak f…
alexvy86 Mar 7, 2026
fd01a03
More fixes
alexvy86 Mar 7, 2026
57bec76
fix(dds/tree,container-runtime): fix timer leaks from summarizer cont…
alexvy86 Mar 8, 2026
dcc90fc
fix(test/snapshots): fix timer leaks so tests exit without --exit flag
alexvy86 Mar 8, 2026
804a1cb
docs: update investigation notes for packages/test/snapshots (root ca…
alexvy86 Mar 8, 2026
498b329
Formatting
alexvy86 Mar 9, 2026
02bf0f0
Fix missing async
alexvy86 Mar 9, 2026
c79462d
fix(test-end-to-end-tests,container-loader,container-runtime): fix re…
alexvy86 Mar 9, 2026
bdb7f9e
Formatting
alexvy86 Mar 10, 2026
7f3979d
fix(mocha-test-setup,test-end-to-end-tests): fix timer/socket leaks s…
alexvy86 Mar 10, 2026
edd661f
docs: update investigation notes with root causes 16 (setTimeout patc…
alexvy86 Mar 10, 2026
83c1324
Fix build
alexvy86 Mar 10, 2026
e3c13ae
Fix build
alexvy86 Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
391 changes: 391 additions & 0 deletions TODO/investigation.md

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions TODO/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## Completed

[x] Fix property-dds tests hanging: added opProcessingController.reset() and objProvider.reset()
in afterEach hooks to dispose containers (clears GC sessionExpiryTimer).
Root cause: deltaConnectionServer.close() alone doesn't dispose containers.

[x] Remove --exit from local-server-stress-tests: harness already properly disposes
containers and closes server in finally block.

[x] Fix examples/data-objects/webflow - removed esm-loader-css from mocharc, changed layout.spec.ts
to import htmlFormatter directly (avoids CSS import chain), and fixed describeCompat/describeWithVersions
to call provider.reset() before driver.dispose() in cleanup hook.

[x] Fix experimental/dds/tree - SummaryManager.dispose() not closing summarizer container,
and Summarizer.runCore() leaving dangling Promise.race() 2-minute timeout timers (root causes 8, 9)

[x] Fix packages/framework/client-logger/fluid-telemetry - ApplicationInsights stopPollingInternalLogs

[x] Fix packages/framework/react - already clean

[x] Fix packages/service-clients/odsp-client - already clean

[x] Fix packages/test/test-end-to-end-tests: two-part fix:
1. patch global.setTimeout in mocha-test-setup/mochaHooks.ts to unref() timers >10s
(handles N-1 compat timer hangs from Summarizer.runCore() Promise.race() timers)
2. fix compression.spec.ts describeInstallVersions block: create versionedProvider ONCE
in before/after hooks instead of per-test in beforeEach/afterEach (fixes socket leak)

## Remaining

- tools/test-tools/.mocharc.cjs: Standalone package (own pnpm-workspace.yaml, not part of main
monorepo). Tests appear to be trivially clean (just spawnSync) but can't verify without
separate install. Low priority.
3 changes: 0 additions & 3 deletions examples/data-objects/inventory-app/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
const getFluidTestMochaConfig = require("@fluid-internal/mocha-test-setup/mocharc-common");

const config = getFluidTestMochaConfig(__dirname);
// TODO: figure out why this package needs the --exit flag, tests might not be cleaning up correctly after themselves.
// AB#7856
config.exit = true;

// Set up JSDOM before Quill is imported (Quill requires document at import time)
config["node-option"] ??= [];
Expand Down
3 changes: 0 additions & 3 deletions examples/data-objects/table-document/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,4 @@
const getFluidTestMochaConfig = require("@fluid-internal/mocha-test-setup/mocharc-common");

const config = getFluidTestMochaConfig(__dirname);
// TODO: figure out why this package needs the --exit flag, tests might not be cleaning up correctly after themselves
// AB#7856
config.exit = true;
module.exports = config;
4 changes: 0 additions & 4 deletions examples/data-objects/webflow/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,4 @@ const packageDir = __dirname;

const getFluidTestMochaConfig = require("@fluid-private/test-version-utils/mocharc-common");
const config = getFluidTestMochaConfig(packageDir);
config["node-option"].push("experimental-loader=esm-loader-css");
// TODO: figure out why this package needs the --exit flag, tests might not be cleaning up correctly after themselves
// AB#7856
config.exit = true;
module.exports = config;
5 changes: 4 additions & 1 deletion examples/data-objects/webflow/src/test/layout.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ import {
} from "@fluidframework/test-utils/internal";

import { FlowDocument } from "../document/index.js";
import { htmlFormatter } from "../index.js";
// Import htmlFormatter directly to avoid loading webflowView.tsx which imports CSS files.
// Using the main index.js would transitively import CSS via host/webflowView.tsx,
// requiring --experimental-loader=esm-loader-css which prevents clean process exit in Node.js 20.
import { htmlFormatter } from "../html/formatters.js";
import { Layout } from "../view/layout.js";

interface ISnapshotNode {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"R": "spec",
"recursive": true,
"timeout": "180s",
"exit": true,
"full-trace": true,
"spec": ["test/setup.js", "test/**/*.spec.js"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@
const getFluidTestMochaConfig = require("@fluid-internal/mocha-test-setup/mocharc-common");

const config = getFluidTestMochaConfig(__dirname);
config.exit = true;
module.exports = config;
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ interface withSummarizer extends Result {

describe("PropertyDDS summarizer", () => {
let objProvider: ITestObjectProvider;
let driver: LocalServerTestDriver;
const propertyDdsId = "PropertyTree";
const USERS = "users";
const getClient = async (withSummarizer = false, load = false) => {
Expand Down Expand Up @@ -97,7 +98,7 @@ describe("PropertyDDS summarizer", () => {
};

beforeEach(async () => {
const driver = new LocalServerTestDriver();
driver = new LocalServerTestDriver();
const registry = [[propertyDdsId, new PropertyTreeFactory()]] as ChannelFactoryRegistry;

objProvider = new TestObjectProvider(
Expand All @@ -111,6 +112,11 @@ describe("PropertyDDS summarizer", () => {
);
});

afterEach(async () => {
objProvider.reset();
await driver.dispose();
});

it("Scenario 1", async function () {
/**
* This test produces a scenario where a summarizer creates a summary with an empty remoteChanges array.
Expand Down Expand Up @@ -401,6 +407,11 @@ function executePerPropertyTreeType(
sharedPropertyTree2 = await dataObject2.getSharedObject(propertyDdsId);
});

afterEach(async () => {
opProcessingController.reset();
await deltaConnectionServer.close();
});

describe("APIs", () => {
it("Can create a PropertyTree", () => {
expect(sharedPropertyTree1).to.not.be.equal(undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,11 @@ describe("PropertyDDS", () => {
await setupContainers();
});

afterEach(async () => {
opProcessingController.reset();
await deltaConnectionServer.close();
});

rebaseTests();
});

Expand All @@ -1143,6 +1148,11 @@ describe("PropertyDDS", () => {
await setupContainers(false);
});

afterEach(async () => {
opProcessingController.reset();
await deltaConnectionServer.close();
});

rebaseTests();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"R": "spec",
"recursive": true,
"timeout": "180s",
"exit": true,
"full-trace": true,
"spec": ["test/setup.js", "test/**/*.spec.js"]
}
1 change: 0 additions & 1 deletion experimental/dds/tree/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@
const getFluidTestMochaConfig = require('@fluid-internal/mocha-test-setup/mocharc-common');

const config = getFluidTestMochaConfig(__dirname);
config.exit = true;
module.exports = config;
16 changes: 12 additions & 4 deletions experimental/dds/tree/src/test/utilities/TestUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,13 @@ export interface LocalServerSharedTreeTestingOptions {
}

const testObjectProviders: TestObjectProvider[] = [];
afterEach(() => {
afterEach(async () => {
for (const provider of testObjectProviders) {
provider.reset();
try {
provider.reset();
} finally {
await provider.driver.dispose?.();
}
}
testObjectProviders.length = 0;
});
Expand Down Expand Up @@ -369,10 +373,14 @@ export async function setUpLocalServerTestSharedTree(

if (testObjectProvider !== undefined) {
provider = testObjectProvider;
const driver = new LocalServerTestDriver();
const loader = makeTestLoader(provider);
// Once ILoaderOptions is specificable, this should use `provider.loadTestContainer` instead.
container = await loader.resolve({ url: await driver.createContainerUrl(treeId), headers }, pendingLocalState);
// Use the provider's existing driver to get the URL rather than creating a new LocalServerTestDriver
// (which would create an unused LocalDeltaConnectionServer with a running DeliLambda timer).
container = await loader.resolve(
{ url: await provider.driver.createContainerUrl(treeId), headers },
pendingLocalState
);
await waitContainerToCatchUp(container);
} else {
const driver = new LocalServerTestDriver();
Expand Down
1 change: 0 additions & 1 deletion packages/dds/map/src/test/memory/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
*/

module.exports = {
"exit": true,
"fgrep": ["@Benchmark", "@MemoryUsage"],
"node-option": ["expose-gc", "gc-global", "unhandled-rejections=strict"], // without leading "--"
"recursive": true,
Expand Down
1 change: 0 additions & 1 deletion packages/dds/matrix/src/test/memory/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
*/

module.exports = {
"exit": true,
"fgrep": ["@Benchmark", "@MemoryUsage"],
"node-option": ["expose-gc", "gc-global", "unhandled-rejections=strict"], // without leading "--"
"recursive": true,
Expand Down
1 change: 0 additions & 1 deletion packages/dds/sequence/src/test/memory/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
*/

module.exports = {
"exit": true,
"fgrep": ["@Benchmark", "@MemoryUsage"],
"node-option": ["expose-gc", "gc-global", "unhandled-rejections=strict"], // without leading "--"
"recursive": true,
Expand Down
4 changes: 0 additions & 4 deletions packages/dds/tree/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,4 @@ const config = getFluidTestMochaConfig(
[],
process.argv.includes("--emulateProduction") ? "PROD" : undefined,
);
// TODO: figure out why this package needs the --exit flag, tests might not be cleaning up correctly after themselves
// In this package, tests which use `TestTreeProvider.create` cause this issue, but there might be other cases as well.
// AB#7856
config.exit = true;
module.exports = config;
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ async function getIIDCompressor(): Promise<IIdCompressor> {
runtime: IFluidDataStoreRuntime;
}
).runtime;
return runtime.idCompressor ?? fail("Expected IIdCompressor to be present in runtime");
const compressor =
runtime.idCompressor ?? fail("Expected IIdCompressor to be present in runtime");
await provider.dispose();
return compressor;
Comment on lines +33 to +36
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

getIIDCompressor() calls provider.dispose() without awaiting. If TestTreeProvider.dispose is updated to be async (to properly await driver.dispose / server shutdown), this helper should await disposal (ideally in a finally) to ensure local server resources are always released.

Copilot uses AI. Check for mistakes.
}

describe("Node Identifier", () => {
Expand Down
Loading
Loading