Skip to content

Commit 53fc944

Browse files
ChumpChiefanthony-murphy-agent
authored andcommitted
Update blob E2E tests for payloadPending and new getPendingBlobs() support (microsoft#25710)
1 parent 4122367 commit 53fc944

File tree

3 files changed

+84
-204
lines changed

3 files changed

+84
-204
lines changed

packages/test/test-end-to-end-tests/src/test/blobsisAttached.spec.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,6 @@ for (const createBlobPayloadPending of [undefined, true] as const) {
176176
assert.strictEqual(pendingState?.pendingAttachmentBlobs, undefined);
177177
});
178178

179-
// ADO#44999: Update for placeholder pending blob creation and getPendingLocalState
180-
// Need to determine if the "attached and acked" tests remain relevant after bookkeeping is updated
181179
it("removes pending blob after attached and acked", async function () {
182180
const testString = "this is a test string";
183181
const testKey = "a blob";
@@ -194,9 +192,6 @@ for (const createBlobPayloadPending of [undefined, true] as const) {
194192
) {
195193
// The payloadShared event is emitted after the blobAttach op is acked,
196194
// so if we await all of them we expect to see no pending blobs.
197-
// NOTE: Without awaiting here, the test would call getPendingLocalState before
198-
// the blobAttach op is sent - this would result in a bad pass (because this test
199-
// intends to test the behavior after the blobAttach op is acked).
200195
await new Promise<void>((resolve) => {
201196
blobHandle.events.on("payloadShared", resolve);
202197
});
@@ -207,8 +202,6 @@ for (const createBlobPayloadPending of [undefined, true] as const) {
207202
assert.strictEqual(pendingState?.pendingAttachmentBlobs, undefined);
208203
});
209204

210-
// ADO#44999: Update for placeholder pending blob creation and getPendingLocalState
211-
// Need to determine if the "attached and acked" tests remain relevant after bookkeeping is updated
212205
it("removes multiple pending blobs after attached and acked", async function () {
213206
const dataStore1 = (await container.getEntryPoint()) as ITestFluidObject;
214207
const map = await dataStore1.getSharedObject<ISharedMap>(mapId);

packages/test/test-end-to-end-tests/src/test/offline/blobHandles.spec.ts

Lines changed: 0 additions & 155 deletions
This file was deleted.

packages/test/test-end-to-end-tests/src/test/offline/stashedOps.spec.ts

Lines changed: 84 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ const assertIntervals = (
125125
assert.deepEqual(actualPos, expected, "intervals are not as expected");
126126
};
127127

128+
// Helper function just for validating that we've captured the pending state we expect
129+
const pendingBlobsFromPendingState = (pendingState: string): Record<string, any> => {
130+
const pendingStateParsed = JSON.parse(pendingState);
131+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
132+
return pendingStateParsed.pendingRuntimeState.pendingAttachmentBlobs;
133+
};
134+
128135
// Introduced in 0.37
129136
// REVIEW: enable compat testing
130137
describeCompat("stashed ops", "NoCompat", (getTestObjectProvider, apis) => {
@@ -157,6 +164,9 @@ describeCompat("stashed ops", "NoCompat", (getTestObjectProvider, apis) => {
157164
},
158165
},
159166
enableRuntimeIdCompressor: "on",
167+
// Enable createBlobPayloadPending, otherwise there will be no pending blobs to test.
168+
explicitSchemaControl: true,
169+
createBlobPayloadPending: true,
160170
},
161171
loaderProps: {
162172
configProvider: configProvider({
@@ -1628,27 +1638,36 @@ describeCompat("stashed ops", "NoCompat", (getTestObjectProvider, apis) => {
16281638
assert.strictEqual(bufferToString(handleGet2, "utf8"), "blob contents");
16291639
});
16301640

1631-
// ADO#44999: The following scenarios are possible with payload pending, but will function differently.
1632-
// The in-flight blob upload will need to be completable after loading with the pending state, but
1633-
// we will expect the customer to have already stored the blob handle prior to calling getPendingState.
1634-
1635-
it.skip("close while uploading blob", async function () {
1641+
it("close while uploading blob", async function () {
16361642
const dataStore = (await container1.getEntryPoint()) as ITestFluidObject;
16371643
const map = await dataStore.getSharedObject<ISharedMap>(mapId);
16381644
await provider.ensureSynchronized();
16391645

1640-
const blobP = dataStore.runtime.uploadBlob(stringToBuffer("blob contents", "utf8"));
1641-
// TODO: This portion was using closeAndGetPendingLocalState - using getPendingLocalState instead to allow compilation
1642-
// const pendingOpsP = container1.closeAndGetPendingLocalState?.();
1643-
const pendingOpsP = container1.getPendingLocalState();
1644-
const handle = await blobP;
1646+
const handle = await dataStore.runtime.uploadBlob(stringToBuffer("blob contents", "utf8"));
16451647
map.set("blob handle", handle);
1646-
const pendingOps = await pendingOpsP;
1648+
const pendingState = await container1.getPendingLocalState();
1649+
container1.close();
1650+
1651+
// In practice, the upload that was triggered by setting the handle in the map probably didn't complete before
1652+
// the getPendingLocalState() call was able to record the pending state, but maybe it could happen with the local
1653+
// service if microtasks shift around in the future. Verify we are testing the scenario we think we are testing.
1654+
const pendingBlobsRecord = pendingBlobsFromPendingState(pendingState);
1655+
const pendingBlob = Object.values(pendingBlobsRecord)[0];
1656+
assert(pendingBlob !== undefined, "Expect a pending blob in pending state");
1657+
assert.strictEqual(
1658+
pendingBlob.state,
1659+
"localOnly",
1660+
"Expect the upload hasn't completed yet",
1661+
);
16471662

1648-
const container2 = await loader.resolve({ url }, pendingOps);
1663+
const container2 = await loader.resolve({ url }, pendingState);
16491664
const dataStore2 = (await container2.getEntryPoint()) as ITestFluidObject;
16501665
const map2 = await dataStore2.getSharedObject<ISharedMap>(mapId);
16511666

1667+
// TODO: We've not yet decided where to expose sharePendingBlobs(), so for now casting and reaching.
1668+
// Replace with calling the proper API when available.
1669+
// Share the pending blob so container1 will be able to find it.
1670+
await (dataStore2.context.containerRuntime as any).blobManager.sharePendingBlobs();
16521671
await provider.ensureSynchronized();
16531672
assert.strictEqual(
16541673
bufferToString(await map1.get("blob handle").get(), "utf8"),
@@ -1660,25 +1679,44 @@ describeCompat("stashed ops", "NoCompat", (getTestObjectProvider, apis) => {
16601679
);
16611680
});
16621681

1663-
it.skip("close while uploading multiple blob", async function () {
1682+
it("close while uploading multiple blob", async function () {
16641683
const dataStore = (await container1.getEntryPoint()) as ITestFluidObject;
16651684
const map = await dataStore.getSharedObject<ISharedMap>(mapId);
16661685
await provider.ensureSynchronized();
16671686

1668-
const blobP1 = dataStore.runtime.uploadBlob(stringToBuffer("blob contents 1", "utf8"));
1669-
const blobP2 = dataStore.runtime.uploadBlob(stringToBuffer("blob contents 2", "utf8"));
1670-
const blobP3 = dataStore.runtime.uploadBlob(stringToBuffer("blob contents 3", "utf8"));
1671-
// TODO: This portion was using closeAndGetPendingLocalState - using getPendingLocalState instead to allow compilation
1672-
// const pendingOpsP = container1.closeAndGetPendingLocalState?.();
1673-
const pendingOpsP = container1.getPendingLocalState();
1674-
map.set("blob handle 1", await blobP1);
1675-
map.set("blob handle 2", await blobP2);
1676-
map.set("blob handle 3", await blobP3);
1677-
const pendingOps = await pendingOpsP;
1687+
const handle1 = await dataStore.runtime.uploadBlob(
1688+
stringToBuffer("blob contents 1", "utf8"),
1689+
);
1690+
const handle2 = await dataStore.runtime.uploadBlob(
1691+
stringToBuffer("blob contents 2", "utf8"),
1692+
);
1693+
const handle3 = await dataStore.runtime.uploadBlob(
1694+
stringToBuffer("blob contents 3", "utf8"),
1695+
);
1696+
map.set("blob handle 1", handle1);
1697+
map.set("blob handle 2", handle2);
1698+
map.set("blob handle 3", handle3);
1699+
const pendingState = await container1.getPendingLocalState();
1700+
container1.close();
1701+
1702+
// In practice, the uploads triggered by setting the handles in the map probably didn't complete before
1703+
// the getPendingLocalState() call was able to record the pending state, but maybe it could happen with the local
1704+
// service if microtasks shift around in the future. Verify we are testing the scenario we think we are testing.
1705+
const pendingBlobsRecord = pendingBlobsFromPendingState(pendingState);
1706+
const pendingBlobs = Object.values(pendingBlobsRecord);
1707+
assert.strictEqual(pendingBlobs?.length, 3, "Expect 3 pending blobs in pending state");
1708+
assert(
1709+
pendingBlobs.every((pendingBlob) => pendingBlob.state === "localOnly"),
1710+
"Expect none of the uploads have completed yet",
1711+
);
16781712

1679-
const container2 = await loader.resolve({ url }, pendingOps);
1713+
const container2 = await loader.resolve({ url }, pendingState);
16801714
const dataStore2 = (await container2.getEntryPoint()) as ITestFluidObject;
16811715
const map2 = await dataStore2.getSharedObject<ISharedMap>(mapId);
1716+
// TODO: We've not yet decided where to expose sharePendingBlobs(), so for now casting and reaching.
1717+
// Replace with calling the proper API when available.
1718+
// Share the pending blob so container1 will be able to find it.
1719+
await (dataStore2.context.containerRuntime as any).blobManager.sharePendingBlobs();
16821720
await provider.ensureSynchronized();
16831721
for (let i = 1; i <= 3; i++) {
16841722
assert.strictEqual(
@@ -1692,26 +1730,24 @@ describeCompat("stashed ops", "NoCompat", (getTestObjectProvider, apis) => {
16921730
}
16931731
});
16941732

1695-
it.skip("load offline from stashed ops with pending blob", async function () {
1733+
it("load offline from stashed ops with pending blob", async function () {
16961734
const container = await loadContainerOffline(testContainerConfig, provider, { url });
16971735
const dataStore = (await container.container.getEntryPoint()) as ITestFluidObject;
16981736
const map = await dataStore.getSharedObject<ISharedMap>(mapId);
16991737

17001738
// Call uploadBlob() while offline to get local ID handle, and generate an op referencing it
1701-
const handleP = dataStore.runtime.uploadBlob(stringToBuffer("blob contents 1", "utf8"));
1702-
// TODO: This portion was using closeAndGetPendingLocalState - using getPendingLocalState instead to allow compilation
1703-
// const stashedChangesP = container.container.closeAndGetPendingLocalState?.();
1704-
const stashedChangesP = container.container.getPendingLocalState();
1705-
const handle = await handleP;
1739+
const handle = await dataStore.runtime.uploadBlob(
1740+
stringToBuffer("blob contents 1", "utf8"),
1741+
);
17061742
map.set("blob handle 1", handle);
1707-
1708-
const stashedChanges = await stashedChangesP;
1743+
const pendingState = await container.container.getPendingLocalState();
1744+
container.container.close();
17091745

17101746
const container3 = await loadContainerOffline(
17111747
testContainerConfig,
17121748
provider,
17131749
{ url },
1714-
stashedChanges,
1750+
pendingState,
17151751
);
17161752
const dataStore3 = (await container3.container.getEntryPoint()) as ITestFluidObject;
17171753
const map3 = await dataStore3.getSharedObject<ISharedMap>(mapId);
@@ -1723,6 +1759,10 @@ describeCompat("stashed ops", "NoCompat", (getTestObjectProvider, apis) => {
17231759
);
17241760
container3.connect();
17251761
await waitForContainerConnection(container3.container);
1762+
// TODO: We've not yet decided where to expose sharePendingBlobs(), so for now casting and reaching.
1763+
// Replace with calling the proper API when available.
1764+
// Share the pending blob so container1 will be able to find it.
1765+
await (dataStore3.context.containerRuntime as any).blobManager.sharePendingBlobs();
17261766
await provider.ensureSynchronized();
17271767

17281768
assert.strictEqual(
@@ -1735,25 +1775,27 @@ describeCompat("stashed ops", "NoCompat", (getTestObjectProvider, apis) => {
17351775
);
17361776
});
17371777

1738-
it.skip("stashed changes with blobs", async function () {
1778+
it("stashed changes with blobs", async function () {
17391779
const container = await loadContainerOffline(testContainerConfig, provider, { url });
17401780
const dataStore = (await container.container.getEntryPoint()) as ITestFluidObject;
17411781
const map = await dataStore.getSharedObject<ISharedMap>(mapId);
17421782

17431783
// Call uploadBlob() while offline to get local ID handle, and generate an op referencing it
1744-
const handleP = dataStore.runtime.uploadBlob(stringToBuffer("blob contents 1", "utf8"));
1745-
// TODO: This portion was using closeAndGetPendingLocalState - using getPendingLocalState instead to allow compilation
1746-
// const stashedChangesP = container.container.closeAndGetPendingLocalState?.();
1747-
const stashedChangesP = container.container.getPendingLocalState();
1748-
const handle = await handleP;
1784+
const handle = await dataStore.runtime.uploadBlob(
1785+
stringToBuffer("blob contents 1", "utf8"),
1786+
);
17491787
map.set("blob handle 1", handle);
1788+
const pendingState = await container.container.getPendingLocalState();
1789+
container.container.close();
17501790

1751-
const stashedChanges = await stashedChangesP;
1752-
1753-
const container3 = await loader.resolve({ url }, stashedChanges);
1791+
const container3 = await loader.resolve({ url }, pendingState);
17541792
const dataStore3 = (await container3.getEntryPoint()) as ITestFluidObject;
17551793
const map3 = await dataStore3.getSharedObject<ISharedMap>(mapId);
17561794

1795+
// TODO: We've not yet decided where to expose sharePendingBlobs(), so for now casting and reaching.
1796+
// Replace with calling the proper API when available.
1797+
// Share the pending blob so container1 will be able to find it.
1798+
await (dataStore3.context.containerRuntime as any).blobManager.sharePendingBlobs();
17571799
await provider.ensureSynchronized();
17581800

17591801
// Blob is uploaded and accessible by all clients

0 commit comments

Comments
 (0)