Skip to content

Commit 9d25f7a

Browse files
committed
rewrite tests
1 parent b6676c4 commit 9d25f7a

File tree

2 files changed

+171
-80
lines changed

2 files changed

+171
-80
lines changed

packages/compass-global-writes/src/store/index.spec.ts

Lines changed: 148 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ import {
55
fetchClusterShardingData,
66
createShardKey,
77
type CreateShardKeyData,
8+
TEST_POLLING_INTERVAL,
89
} from './reducer';
910
import sinon from 'sinon';
1011
import type {
1112
AutomationAgentDeploymentStatusApiResponse,
13+
AutomationAgentProcess,
1214
ClusterDetailsApiResponse,
1315
ManagedNamespace,
1416
ShardZoneMapping,
1517
} from '../services/atlas-global-writes-service';
16-
import { waitFor } from '@mongodb-js/testing-library-compass';
18+
import { wait, waitFor } from '@mongodb-js/testing-library-compass';
19+
import Sinon from 'sinon';
1720

1821
const DB = 'test';
1922
const COLL = 'coll';
@@ -28,22 +31,24 @@ const clusterDetails: ClusterDetailsApiResponse = {
2831
replicationSpecList: [],
2932
};
3033

34+
const shardKeyData: CreateShardKeyData = {
35+
customShardKey: 'secondary',
36+
isCustomShardKeyHashed: true,
37+
isShardKeyUnique: true,
38+
numInitialChunks: 1,
39+
presplitHashedZones: true,
40+
};
41+
3142
const managedNamespace: ManagedNamespace = {
3243
db: DB,
3344
collection: COLL,
34-
customShardKey: 'secondary',
35-
isCustomShardKeyHashed: false,
36-
isShardKeyUnique: false,
37-
numInitialChunks: null,
38-
presplitHashedZones: false,
45+
...shardKeyData,
3946
};
4047

41-
const shardKeyData: CreateShardKeyData = {
42-
customShardKey: 'test',
43-
isCustomShardKeyHashed: true,
44-
isShardKeyUnique: false,
45-
numInitialChunks: 1,
46-
presplitHashedZones: true,
48+
const failedShardingProcess: AutomationAgentProcess = {
49+
statusType: 'ERROR',
50+
workingOnShort: 'ShardingCollections',
51+
errorText: `Failed to shard ${NS}`,
4752
};
4853

4954
function createAuthFetchResponse<
@@ -57,7 +62,81 @@ function createAuthFetchResponse<
5762
};
5863
}
5964

60-
function createStore(atlasService: any = {}): GlobalWritesStore {
65+
function createStore({
66+
isNamespaceManaged = () => false,
67+
hasShardingError = () => false,
68+
hasShardKey = () => false,
69+
failsOnShardingRequest = () => false,
70+
authenticatedFetchStub,
71+
}:
72+
| {
73+
isNamespaceManaged?: () => boolean;
74+
hasShardingError?: () => boolean;
75+
hasShardKey?: () => boolean;
76+
failsOnShardingRequest?: () => boolean;
77+
authenticatedFetchStub?: never;
78+
}
79+
| {
80+
isNamespaceManaged?: never;
81+
hasShardingError?: never;
82+
hasShardKey?: () => boolean;
83+
failsOnShardingRequest?: never;
84+
authenticatedFetchStub?: () => void;
85+
} = {}): GlobalWritesStore {
86+
const atlasService = {
87+
authenticatedFetch: (uri: string) => {
88+
if (uri.includes(`/geoSharding`) && failsOnShardingRequest()) {
89+
return Promise.reject(new Error('Failed to shard'));
90+
}
91+
92+
if (uri.includes('/clusters/')) {
93+
return createAuthFetchResponse({
94+
...clusterDetails,
95+
geoSharding: {
96+
...clusterDetails.geoSharding,
97+
managedNamespaces: isNamespaceManaged() ? [managedNamespace] : [],
98+
},
99+
});
100+
}
101+
102+
if (uri.includes('/deploymentStatus/')) {
103+
return createAuthFetchResponse({
104+
automationStatus: {
105+
processes: hasShardingError() ? [failedShardingProcess] : [],
106+
},
107+
});
108+
}
109+
110+
return createAuthFetchResponse({});
111+
},
112+
automationAgentRequest: (_meta: unknown, type: string) => ({
113+
_id: '123',
114+
requestType: type,
115+
}),
116+
automationAgentAwait: (_meta: unknown, type: string) => {
117+
if (type === 'getShardKey') {
118+
return {
119+
response: hasShardKey()
120+
? [
121+
{
122+
key: {
123+
location: 'range',
124+
secondary: shardKeyData.isCustomShardKeyHashed
125+
? 'hashed'
126+
: 'range',
127+
},
128+
unique: true,
129+
},
130+
]
131+
: [],
132+
};
133+
}
134+
},
135+
} as any;
136+
137+
if (authenticatedFetchStub)
138+
atlasService.authenticatedFetch = authenticatedFetchStub;
139+
61140
return setupStore(
62141
{
63142
namespace: NS,
@@ -76,29 +155,59 @@ describe('GlobalWritesStore Store', function () {
76155
});
77156

78157
context('scenarios', function () {
79-
it('not managed -> sharding', async function () {
158+
it('not managed -> sharding -> success', async function () {
159+
let mockShardKey = false;
160+
const hasShardKey = Sinon.fake(() => mockShardKey);
161+
// initial state === unsharded
80162
const store = createStore({
81-
authenticatedFetch: () => createAuthFetchResponse(clusterDetails),
163+
hasShardKey,
82164
});
83165
await store.dispatch(fetchClusterShardingData());
84166
expect(store.getState().status).to.equal('UNSHARDED');
85167
expect(store.getState().managedNamespace).to.equal(undefined);
86168

169+
// user requests sharding
87170
const promise = store.dispatch(createShardKey(shardKeyData));
88171
expect(store.getState().status).to.equal('SUBMITTING_FOR_SHARDING');
89172
await promise;
90173
expect(store.getState().status).to.equal('SHARDING');
174+
175+
// sharding ends with a shardKey
176+
mockShardKey = true;
177+
await wait(TEST_POLLING_INTERVAL);
178+
await waitFor(() => {
179+
expect(store.getState().status).to.equal('SHARD_KEY_CORRECT');
180+
});
91181
});
92182

93-
it('not managed -> failed sharding attempt', async function () {
183+
it('not managed -> sharding -> sharding error', async function () {
184+
let mockFailure = false;
185+
const hasShardingError = Sinon.fake(() => mockFailure);
186+
// initial state === unsharded
94187
const store = createStore({
95-
authenticatedFetch: (uri: string) => {
96-
if (uri.includes('/geoSharding')) {
97-
return Promise.reject(new Error('Failed to shard'));
98-
}
188+
hasShardingError,
189+
});
190+
await store.dispatch(fetchClusterShardingData());
191+
expect(store.getState().status).to.equal('UNSHARDED');
192+
expect(store.getState().managedNamespace).to.equal(undefined);
99193

100-
return createAuthFetchResponse(clusterDetails);
101-
},
194+
// user requests sharding
195+
const promise = store.dispatch(createShardKey(shardKeyData));
196+
expect(store.getState().status).to.equal('SUBMITTING_FOR_SHARDING');
197+
await promise;
198+
expect(store.getState().status).to.equal('SHARDING');
199+
200+
// sharding ends with an error
201+
mockFailure = true;
202+
await wait(TEST_POLLING_INTERVAL);
203+
await waitFor(() => {
204+
expect(store.getState().status).to.equal('SHARDING_ERROR');
205+
});
206+
});
207+
208+
it('not managed -> failed sharding attempt', async function () {
209+
const store = createStore({
210+
failsOnShardingRequest: () => true,
102211
});
103212
await store.dispatch(fetchClusterShardingData());
104213
expect(store.getState().status).to.equal('UNSHARDED');
@@ -110,48 +219,10 @@ describe('GlobalWritesStore Store', function () {
110219
expect(store.getState().status).to.equal('UNSHARDED');
111220
});
112221

113-
it('when the namespace is managed', async function () {
222+
it('when the namespace is managed and has a valid shard key', async function () {
114223
const store = createStore({
115-
authenticatedFetch: (uri: string) => {
116-
if (uri.includes('/clusters/')) {
117-
return createAuthFetchResponse({
118-
...clusterDetails,
119-
geoSharding: {
120-
...clusterDetails.geoSharding,
121-
managedNamespaces: [managedNamespace],
122-
},
123-
});
124-
}
125-
126-
if (uri.includes('/deploymentStatus/')) {
127-
return createAuthFetchResponse({
128-
automationStatus: {
129-
processes: [],
130-
},
131-
});
132-
}
133-
134-
return createAuthFetchResponse({});
135-
},
136-
automationAgentRequest: (_meta: unknown, type: string) => ({
137-
_id: '123',
138-
requestType: type,
139-
}),
140-
automationAgentAwait: (_meta: unknown, type: string) => {
141-
if (type === 'getShardKey') {
142-
return {
143-
response: [
144-
{
145-
key: {
146-
location: 'HASHED',
147-
secondary: 'HASHED',
148-
},
149-
unique: false,
150-
},
151-
],
152-
};
153-
}
154-
},
224+
isNamespaceManaged: () => true,
225+
hasShardKey: () => true,
155226
});
156227
await store.dispatch(fetchClusterShardingData());
157228
await waitFor(() => {
@@ -160,12 +231,24 @@ describe('GlobalWritesStore Store', function () {
160231
});
161232
});
162233

234+
it('when the namespace is managed but has a sharding error', async function () {
235+
const store = createStore({
236+
isNamespaceManaged: () => true,
237+
hasShardingError: () => true,
238+
});
239+
await store.dispatch(fetchClusterShardingData());
240+
await waitFor(() => {
241+
expect(store.getState().status).to.equal('SHARDING_ERROR');
242+
expect(store.getState().managedNamespace).to.equal(managedNamespace);
243+
});
244+
});
245+
163246
it('sends correct data to the server when creating a shard key', async function () {
164247
const alreadyManagedNamespaces = [
165248
{
166249
db: 'test',
167250
collection: 'one',
168-
customShardKey: 'test',
251+
customShardKey: 'secondary',
169252
isCustomShardKeyHashed: true,
170253
isShardKeyUnique: false,
171254
numInitialChunks: 1,
@@ -196,7 +279,7 @@ describe('GlobalWritesStore Store', function () {
196279
.resolves();
197280

198281
const store = createStore({
199-
authenticatedFetch: fetchStub,
282+
authenticatedFetchStub: fetchStub,
200283
});
201284

202285
await store.dispatch(createShardKey(shardKeyData));

packages/compass-global-writes/src/store/reducer.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import type { GlobalWritesThunkAction } from '.';
33
import { openToast, showConfirmation } from '@mongodb-js/compass-components';
44
import type { ManagedNamespace } from '../services/atlas-global-writes-service';
55

6-
export const POLLING_INTERVAL = 5000;
6+
const POLLING_INTERVAL = 5000;
7+
export const TEST_POLLING_INTERVAL = 1;
78

89
export function isAction<A extends Action>(
910
action: Action,
@@ -585,7 +586,7 @@ export const cancelSharding = ():
585586
const { namespace, status } = getState();
586587

587588
if (status === ShardingStatuses.SHARDING) {
588-
stopPollingForShardKey(getState);
589+
dispatch(stopPollingForShardKey());
589590
}
590591
dispatch({
591592
type: GlobalWritesActionTypes.CancellingShardingStarted,
@@ -628,30 +629,38 @@ const setNamespaceBeingSharded = (
628629
type: GlobalWritesActionTypes.SubmittingForShardingFinished,
629630
managedNamespace,
630631
});
631-
dispatch(startPollingForShardKey());
632+
dispatch(pollForShardKey());
632633
};
633634
};
634635

635-
const startPollingForShardKey = (): GlobalWritesThunkAction<
636+
const pollForShardKey = (): GlobalWritesThunkAction<
636637
void,
637638
NextPollingTimeoutSetAction
638639
> => {
639640
return (dispatch) => {
640-
console.log('START POLLING');
641-
const timeout = setTimeout(() => {
642-
void dispatch(fetchNamespaceShardKey());
643-
}, POLLING_INTERVAL);
641+
const timeout = setTimeout(
642+
() => {
643+
void dispatch(fetchNamespaceShardKey());
644+
},
645+
process.env.NODE_ENV !== 'test' ? POLLING_INTERVAL : TEST_POLLING_INTERVAL
646+
);
644647
dispatch({
645648
type: GlobalWritesActionTypes.NextPollingTimeoutSet,
646649
timeout,
647650
});
648651
};
649652
};
650653

651-
const stopPollingForShardKey = (getState: () => RootState) => {
652-
const { pollingTimeout } = getState();
653-
if (!pollingTimeout) return;
654-
clearTimeout(pollingTimeout);
654+
const stopPollingForShardKey = (): GlobalWritesThunkAction<
655+
void,
656+
NextPollingTimeoutClearedAction
657+
> => {
658+
return (dispatch, getState) => {
659+
const { pollingTimeout } = getState();
660+
if (!pollingTimeout) return;
661+
clearTimeout(pollingTimeout);
662+
dispatch({ type: GlobalWritesActionTypes.NextPollingTimeoutCleared });
663+
};
655664
};
656665

657666
export const fetchNamespaceShardKey = (): GlobalWritesThunkAction<
@@ -663,7 +672,6 @@ export const fetchNamespaceShardKey = (): GlobalWritesThunkAction<
663672
getState,
664673
{ atlasGlobalWritesService, logger, connectionInfoRef }
665674
) => {
666-
console.log('FETCHING KEY');
667675
const { namespace, status } = getState();
668676

669677
try {
@@ -674,7 +682,7 @@ export const fetchNamespaceShardKey = (): GlobalWritesThunkAction<
674682

675683
if (shardingError) {
676684
if (status === ShardingStatuses.SHARDING) {
677-
stopPollingForShardKey(getState);
685+
dispatch(stopPollingForShardKey());
678686
}
679687
dispatch({
680688
type: GlobalWritesActionTypes.NamespaceShardingErrorFetched,
@@ -689,7 +697,7 @@ export const fetchNamespaceShardKey = (): GlobalWritesThunkAction<
689697
}
690698

691699
if (status === ShardingStatuses.SHARDING) {
692-
stopPollingForShardKey(getState);
700+
dispatch(stopPollingForShardKey());
693701
}
694702
dispatch({
695703
type: GlobalWritesActionTypes.NamespaceShardKeyFetched,

0 commit comments

Comments
 (0)