Skip to content

Commit dbda813

Browse files
scottn12anthony-murphy-agent
authored andcommitted
test(counter): Add Fuzz Testing for SharedCounter DDS (microsoft#25759)
## Description This PR adds fuzz testing for the `SharedCounter` DDS. Additionally, it exports `baseCounterModel` to be integrated into the local server stress test suite in a future PR. ## Misc [AB#51462](https://dev.azure.com/fluidframework/235294da-091d-4c29-84fc-cdfc3d90890b/_workitems/edit/51462)
1 parent 32b8fb0 commit dbda813

File tree

8 files changed

+366
-4
lines changed

8 files changed

+366
-4
lines changed

packages/dds/counter/package.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@
4242
"types": "./dist/index.d.ts",
4343
"default": "./dist/index.js"
4444
}
45+
},
46+
"./internal/test": {
47+
"allow-ff-test-exports": {
48+
"import": {
49+
"types": "./lib/test/index.d.ts",
50+
"default": "./lib/test/index.js"
51+
},
52+
"require": {
53+
"types": "./dist/test/index.d.ts",
54+
"default": "./dist/test/index.js"
55+
}
56+
}
4557
}
4658
},
4759
"main": "lib/index.js",
@@ -62,7 +74,7 @@
6274
"build:test": "npm run build:test:esm && npm run build:test:cjs",
6375
"build:test:cjs": "fluid-tsc commonjs --project ./src/test/tsconfig.cjs.json",
6476
"build:test:esm": "tsc --project ./src/test/tsconfig.json",
65-
"check:are-the-types-wrong": "attw --pack .",
77+
"check:are-the-types-wrong": "attw --pack . --exclude-entrypoints ./internal/test",
6678
"check:biome": "biome check .",
6779
"check:exports": "concurrently \"npm:check:exports:*\"",
6880
"check:exports:bundle-release-tags": "api-extractor run --config api-extractor/api-extractor-lint-bundle.json",
@@ -127,6 +139,8 @@
127139
"@arethetypeswrong/cli": "^0.17.1",
128140
"@biomejs/biome": "~1.9.3",
129141
"@fluid-internal/mocha-test-setup": "workspace:~",
142+
"@fluid-private/stochastic-test-utils": "workspace:~",
143+
"@fluid-private/test-dds-utils": "workspace:~",
130144
"@fluid-tools/build-cli": "^0.58.3",
131145
"@fluidframework/build-common": "^2.0.3",
132146
"@fluidframework/build-tools": "^0.58.3",

packages/dds/counter/src/counter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import type { ISharedCounter, ISharedCounterEvents } from "./interfaces.js";
2828
/**
2929
* Describes the operation (op) format for incrementing the {@link SharedCounter}.
3030
*/
31-
interface IIncrementOperation {
31+
export interface IIncrementOperation {
3232
type: "increment";
3333
incrementAmount: number;
3434
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { createDDSFuzzSuite } from "@fluid-private/test-dds-utils";
7+
import { FlushMode } from "@fluidframework/runtime-definitions/internal";
8+
9+
import { baseCounterModel, defaultOptions } from "./fuzzUtils.js";
10+
11+
describe("Counter fuzz testing", () => {
12+
createDDSFuzzSuite(baseCounterModel, {
13+
...defaultOptions,
14+
// TODO: Enable rollback in AB#44705
15+
rollbackProbability: 0,
16+
// Uncomment this line to replay a specific seed:
17+
// replay: 0,
18+
// This can be useful for quickly minimizing failure json while attempting to root-cause a failure.
19+
});
20+
});
21+
22+
describe("Counter fuzz testing with rebasing", () => {
23+
createDDSFuzzSuite(baseCounterModel, {
24+
...defaultOptions,
25+
containerRuntimeOptions: {
26+
flushMode: FlushMode.TurnBased,
27+
enableGroupedBatching: true,
28+
},
29+
rebaseProbability: 0.15,
30+
// TODO: Enable rollback in AB#44705
31+
rollbackProbability: 0,
32+
// Uncomment this line to replay a specific seed:
33+
// replay: 0,
34+
// This can be useful for quickly minimizing failure json while attempting to root-cause a failure.
35+
});
36+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
// Problem:
7+
// - `__dirname` is not defined in ESM
8+
// - `import.meta.url` is not defined in CJS
9+
// Solution:
10+
// - Export '__dirname' from a .cjs file in the same directory.
11+
//
12+
// Note that *.cjs files are always CommonJS, but can be imported from ESM.
13+
// eslint-disable-next-line jsdoc/require-jsdoc
14+
export const _dirname = __dirname;
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { strict as assert } from "node:assert";
7+
import * as path from "node:path";
8+
9+
import type {
10+
AsyncGenerator as Generator,
11+
Reducer,
12+
} from "@fluid-private/stochastic-test-utils";
13+
import {
14+
combineReducers,
15+
createWeightedAsyncGenerator as createWeightedGenerator,
16+
takeAsync as take,
17+
} from "@fluid-private/stochastic-test-utils";
18+
import type {
19+
DDSFuzzModel,
20+
DDSFuzzSuiteOptions,
21+
DDSFuzzTestState,
22+
} from "@fluid-private/test-dds-utils";
23+
24+
import type { IIncrementOperation } from "../counter.js";
25+
import { CounterFactory } from "../counterFactory.js";
26+
import type { ISharedCounter } from "../interfaces.js";
27+
28+
import { _dirname } from "./dirname.cjs";
29+
30+
/**
31+
* Default options for Counter fuzz testing
32+
*/
33+
export const defaultOptions: Partial<DDSFuzzSuiteOptions> = {
34+
validationStrategy: { type: "fixedInterval", interval: 10 },
35+
clientJoinOptions: {
36+
maxNumberOfClients: 6,
37+
clientAddProbability: 0.05,
38+
stashableClientProbability: 0.2,
39+
},
40+
defaultTestCount: 100,
41+
saveFailures: { directory: path.join(_dirname, "../../src/test/results") },
42+
};
43+
44+
type FuzzTestState = DDSFuzzTestState<CounterFactory>;
45+
46+
/**
47+
* Represents Counter operation types for fuzz testing
48+
*/
49+
export type CounterOperation = IIncrementOperation;
50+
51+
function makeOperationGenerator(): Generator<CounterOperation, FuzzTestState> {
52+
async function increment(state: FuzzTestState): Promise<CounterOperation> {
53+
return {
54+
type: "increment",
55+
incrementAmount: state.random.integer(-10, 10),
56+
};
57+
}
58+
59+
const clientBaseOperationGenerator = createWeightedGenerator<
60+
CounterOperation,
61+
FuzzTestState
62+
>([[increment, 1]]);
63+
64+
return async (state: FuzzTestState) =>
65+
clientBaseOperationGenerator({
66+
...state,
67+
});
68+
}
69+
70+
function makeReducer(): Reducer<CounterOperation, FuzzTestState> {
71+
const reducer = combineReducers<CounterOperation, FuzzTestState>({
72+
increment: ({ client }, { incrementAmount }) => {
73+
client.channel.increment(incrementAmount);
74+
},
75+
});
76+
return reducer;
77+
}
78+
79+
function assertEqualCounters(a: ISharedCounter, b: ISharedCounter): void {
80+
assert.equal(a.value, b.value, `Counter values do not match: ${a.value} !== ${b.value}`);
81+
}
82+
83+
/**
84+
* Base fuzz model for Counter
85+
*/
86+
export const baseCounterModel: DDSFuzzModel<CounterFactory, CounterOperation, FuzzTestState> =
87+
{
88+
workloadName: "default configuration",
89+
generatorFactory: () => take(100, makeOperationGenerator()),
90+
reducer: makeReducer(),
91+
validateConsistency: (a, b) => assertEqualCounters(a.channel, b.channel),
92+
factory: new CounterFactory(),
93+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
export { baseCounterModel } from "./fuzzUtils.js";

packages/dds/counter/src/test/tsconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
"rootDir": "./",
55
"outDir": "../../lib/test",
66
"types": ["mocha", "node"],
7+
"declaration": true,
8+
"declarationMap": true,
79
},
810
"include": ["./**/*"],
911
"references": [

0 commit comments

Comments
 (0)