Skip to content

Commit 5d39dee

Browse files
committed
unit test reducer transitions for fakerSchemaGeneration state
1 parent b91a1b6 commit 5d39dee

File tree

2 files changed

+320
-4
lines changed

2 files changed

+320
-4
lines changed

packages/compass-collection/src/modules/collection-tab.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,18 +155,18 @@ interface MockDataGeneratorPreviousButtonClickedAction {
155155
type: CollectionActions.MockDataGeneratorPreviousButtonClicked;
156156
}
157157

158-
interface FakerMappingGenerationStartedAction {
158+
export interface FakerMappingGenerationStartedAction {
159159
type: CollectionActions.FakerMappingGenerationStarted;
160160
requestId: string;
161161
}
162162

163-
interface FakerMappingGenerationCompletedAction {
163+
export interface FakerMappingGenerationCompletedAction {
164164
type: CollectionActions.FakerMappingGenerationCompleted;
165165
fakerSchema: MockDataSchemaResponse;
166166
requestId: string;
167167
}
168168

169-
interface FakerMappingGenerationFailedAction {
169+
export interface FakerMappingGenerationFailedAction {
170170
type: CollectionActions.FakerMappingGenerationFailed;
171171
error: string;
172172
requestId: string;
@@ -370,12 +370,30 @@ const reducer: Reducer<CollectionState, Action> = (
370370
};
371371
}
372372

373+
// todo: reset `fakerSchemaGeneration` state when modal flow restarts
374+
373375
if (
374376
isAction<FakerMappingGenerationStartedAction>(
375377
action,
376378
CollectionActions.FakerMappingGenerationStarted
377379
)
378380
) {
381+
if (
382+
state.mockDataGenerator.currentStep !==
383+
MockDataGeneratorStep.SCHEMA_CONFIRMATION
384+
) {
385+
return state;
386+
}
387+
388+
if (
389+
state.fakerSchemaGeneration.status ===
390+
MOCK_DATA_GENERATOR_REQUEST_GENERATING ||
391+
state.fakerSchemaGeneration.status ===
392+
MOCK_DATA_GENERATOR_REQUEST_COMPLETED
393+
) {
394+
return state;
395+
}
396+
379397
return {
380398
...state,
381399
fakerSchemaGeneration: {
@@ -391,6 +409,13 @@ const reducer: Reducer<CollectionState, Action> = (
391409
CollectionActions.FakerMappingGenerationCompleted
392410
)
393411
) {
412+
if (
413+
state.fakerSchemaGeneration.status !==
414+
MOCK_DATA_GENERATOR_REQUEST_GENERATING
415+
) {
416+
return state;
417+
}
418+
394419
return {
395420
...state,
396421
fakerSchemaGeneration: {
@@ -407,6 +432,13 @@ const reducer: Reducer<CollectionState, Action> = (
407432
CollectionActions.FakerMappingGenerationFailed
408433
)
409434
) {
435+
if (
436+
state.fakerSchemaGeneration.status !==
437+
MOCK_DATA_GENERATOR_REQUEST_GENERATING
438+
) {
439+
return state;
440+
}
441+
410442
return {
411443
...state,
412444
fakerSchemaGeneration: {

packages/compass-collection/src/stores/collection-tab.spec.ts

Lines changed: 285 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import type { CollectionTabOptions } from './collection-tab';
2+
import type {
3+
CollectionState,
4+
FakerMappingGenerationStartedAction,
5+
FakerMappingGenerationCompletedAction,
6+
FakerMappingGenerationFailedAction,
7+
} from '../modules/collection-tab';
28
import { activatePlugin } from './collection-tab';
3-
import { selectTab } from '../modules/collection-tab';
9+
import {
10+
selectTab,
11+
default as collectionTabReducer,
12+
} from '../modules/collection-tab';
413
import * as collectionTabModule from '../modules/collection-tab';
514
import { waitFor } from '@mongodb-js/testing-library-compass';
615
import Sinon from 'sinon';
@@ -16,10 +25,15 @@ import { type CollectionMetadata } from 'mongodb-collection-model';
1625
import {
1726
SCHEMA_ANALYSIS_STATE_COMPLETE,
1827
SCHEMA_ANALYSIS_STATE_INITIAL,
28+
SchemaAnalysisState,
1929
} from '../schema-analysis-types';
2030
import {
31+
MOCK_DATA_GENERATOR_REQUEST_COMPLETED,
2132
MOCK_DATA_GENERATOR_REQUEST_GENERATING,
2233
MOCK_DATA_GENERATOR_REQUEST_IDLE,
34+
MOCK_DATA_GENERATOR_REQUEST_ERROR,
35+
MockDataGeneratorState,
36+
MockDataGeneratorStep,
2337
} from '../components/mock-data-generator-modal/types';
2438
import { CollectionActions } from '../modules/collection-tab';
2539
import { type MockDataSchemaResponse } from '@mongodb-js/compass-generative-ai';
@@ -478,4 +492,274 @@ describe('Collection Tab Content store', function () {
478492
expect(logger.debug).to.have.been.calledOnce;
479493
});
480494
});
495+
496+
describe('reducer handles fakerSchemaGeneration state transitions', function () {
497+
const baseState: CollectionState = {
498+
workspaceTabId: 'test_tab_id',
499+
namespace: 'test_db.test_collection',
500+
metadata: null,
501+
schemaAnalysis: { status: SCHEMA_ANALYSIS_STATE_INITIAL },
502+
mockDataGenerator: {
503+
isModalOpen: false,
504+
currentStep: MockDataGeneratorStep.AI_DISCLAIMER,
505+
},
506+
fakerSchemaGeneration: { status: MOCK_DATA_GENERATOR_REQUEST_IDLE },
507+
};
508+
509+
const completeSchemaState: SchemaAnalysisState = {
510+
status: SCHEMA_ANALYSIS_STATE_COMPLETE,
511+
processedSchema: {
512+
name: {
513+
type: 'String' as const,
514+
probability: 1.0,
515+
sample_values: ['John', 'Jane'],
516+
},
517+
},
518+
sampleDocument: { name: 'John' },
519+
schemaMetadata: { maxNestingDepth: 1, validationRules: null },
520+
};
521+
522+
const fakerMappingGenerationStartedAction: FakerMappingGenerationStartedAction =
523+
{
524+
type: CollectionActions.FakerMappingGenerationStarted,
525+
requestId: 'some_request_id',
526+
};
527+
528+
describe('on FakerMappingGenerationStarted', function () {
529+
it('should not change state when analyis is incomplete; or schema generation request in progress or completed', function () {
530+
const noOpStates: CollectionState[] = [
531+
{ ...baseState },
532+
{
533+
...baseState,
534+
schemaAnalysis: completeSchemaState,
535+
mockDataGenerator: {
536+
isModalOpen: false,
537+
currentStep: MockDataGeneratorStep.SCHEMA_CONFIRMATION,
538+
},
539+
fakerSchemaGeneration: {
540+
status: MOCK_DATA_GENERATOR_REQUEST_GENERATING,
541+
requestId: 'existing_id',
542+
},
543+
},
544+
{
545+
...baseState,
546+
schemaAnalysis: completeSchemaState,
547+
mockDataGenerator: {
548+
isModalOpen: false,
549+
currentStep: MockDataGeneratorStep.SCHEMA_CONFIRMATION,
550+
},
551+
fakerSchemaGeneration: {
552+
status: MOCK_DATA_GENERATOR_REQUEST_COMPLETED,
553+
fakerSchema: {
554+
content: {
555+
fields: [
556+
{
557+
fieldPath: 'name',
558+
probability: 1.0,
559+
mongoType: 'string' as const,
560+
fakerMethod: 'person.firstName',
561+
fakerArgs: [],
562+
isArray: false,
563+
},
564+
],
565+
},
566+
},
567+
requestId: 'existing_id',
568+
},
569+
},
570+
];
571+
572+
noOpStates.forEach((state) => {
573+
const action = fakerMappingGenerationStartedAction;
574+
expect(collectionTabReducer(state, action)).to.deep.equal(state);
575+
});
576+
});
577+
578+
it('should update status to generating when conditions are met', function () {
579+
const state: CollectionState = {
580+
...baseState,
581+
schemaAnalysis: completeSchemaState,
582+
mockDataGenerator: {
583+
isModalOpen: false,
584+
currentStep: MockDataGeneratorStep.SCHEMA_CONFIRMATION,
585+
},
586+
fakerSchemaGeneration: { status: MOCK_DATA_GENERATOR_REQUEST_IDLE },
587+
};
588+
const action = fakerMappingGenerationStartedAction;
589+
const newState = collectionTabReducer(state, action);
590+
591+
expect(newState.fakerSchemaGeneration).to.deep.equal({
592+
status: MOCK_DATA_GENERATOR_REQUEST_GENERATING,
593+
requestId: 'some_request_id',
594+
});
595+
596+
// Ensure other parts of state are unchanged
597+
expect(newState.schemaAnalysis).to.deep.equal(state.schemaAnalysis);
598+
expect(newState.mockDataGenerator).to.deep.equal(
599+
state.mockDataGenerator
600+
);
601+
});
602+
});
603+
604+
describe('on FakerMappingGenerationCompleted', function () {
605+
const fakerMappingGenerationCompletedAction: FakerMappingGenerationCompletedAction =
606+
{
607+
type: CollectionActions.FakerMappingGenerationCompleted,
608+
fakerSchema: {
609+
content: {
610+
fields: [
611+
{
612+
fieldPath: 'name',
613+
probability: 1.0,
614+
mongoType: 'string' as const,
615+
fakerMethod: 'person.firstName',
616+
fakerArgs: [],
617+
isArray: false,
618+
},
619+
],
620+
},
621+
},
622+
requestId: 'test_request_id',
623+
};
624+
625+
it('should not transition to completed if in idle, completed, or error state', function () {
626+
const noOpStates: MockDataGeneratorState[] = [
627+
{ status: MOCK_DATA_GENERATOR_REQUEST_IDLE },
628+
{
629+
status: MOCK_DATA_GENERATOR_REQUEST_COMPLETED,
630+
fakerSchema: {
631+
content: {
632+
fields: [
633+
{
634+
fieldPath: 'name',
635+
probability: 1.0,
636+
mongoType: 'string' as const,
637+
fakerMethod: 'person.firstName',
638+
fakerArgs: [],
639+
isArray: false,
640+
},
641+
],
642+
},
643+
},
644+
requestId: 'existing_id',
645+
},
646+
{
647+
status: MOCK_DATA_GENERATOR_REQUEST_ERROR,
648+
error: 'Some error',
649+
requestId: 'error_request_id',
650+
},
651+
];
652+
653+
noOpStates.forEach((fakerSchemaGeneration) => {
654+
const state: CollectionState = {
655+
...baseState,
656+
fakerSchemaGeneration,
657+
};
658+
const action = fakerMappingGenerationCompletedAction;
659+
660+
expect(collectionTabReducer(state, action)).to.deep.equal(state);
661+
});
662+
});
663+
664+
it('should update status to completed when generation is in progress', function () {
665+
const state: CollectionState = {
666+
...baseState,
667+
fakerSchemaGeneration: {
668+
status: MOCK_DATA_GENERATOR_REQUEST_GENERATING,
669+
requestId: 'generating_request_id',
670+
},
671+
};
672+
const action = fakerMappingGenerationCompletedAction;
673+
const newState = collectionTabReducer(state, action);
674+
675+
expect(newState.fakerSchemaGeneration).to.deep.equal({
676+
status: MOCK_DATA_GENERATOR_REQUEST_COMPLETED,
677+
fakerSchema: action.fakerSchema,
678+
requestId: action.requestId,
679+
});
680+
681+
// Ensure other parts of state are unchanged
682+
expect(newState.schemaAnalysis).to.deep.equal(state.schemaAnalysis);
683+
expect(newState.mockDataGenerator).to.deep.equal(
684+
state.mockDataGenerator
685+
);
686+
expect(newState.namespace).to.deep.equal(state.namespace);
687+
expect(newState.metadata).to.deep.equal(state.metadata);
688+
});
689+
});
690+
691+
describe('on FakerMappingGenerationFailed', function () {
692+
const fakerMappingGenerationFailedAction: FakerMappingGenerationFailedAction =
693+
{
694+
type: CollectionActions.FakerMappingGenerationFailed,
695+
error: 'Generation failed',
696+
requestId: 'test_request_id',
697+
};
698+
699+
it('should not transition to error if in idle, completed, or error state', function () {
700+
const noOpStates: MockDataGeneratorState[] = [
701+
{ status: MOCK_DATA_GENERATOR_REQUEST_IDLE },
702+
{
703+
status: MOCK_DATA_GENERATOR_REQUEST_COMPLETED,
704+
fakerSchema: {
705+
content: {
706+
fields: [
707+
{
708+
fieldPath: 'name',
709+
probability: 1.0,
710+
mongoType: 'string' as const,
711+
fakerMethod: 'person.firstName',
712+
fakerArgs: [],
713+
isArray: false,
714+
},
715+
],
716+
},
717+
},
718+
requestId: 'existing_id',
719+
},
720+
{
721+
status: MOCK_DATA_GENERATOR_REQUEST_ERROR,
722+
error: 'Previous error',
723+
requestId: 'error_request_id',
724+
},
725+
];
726+
727+
noOpStates.forEach((fakerSchemaGeneration) => {
728+
const state: CollectionState = {
729+
...baseState,
730+
fakerSchemaGeneration,
731+
};
732+
const action = fakerMappingGenerationFailedAction;
733+
734+
expect(collectionTabReducer(state, action)).to.deep.equal(state);
735+
});
736+
});
737+
738+
it('should update status to error when generation is in progress', function () {
739+
const state: CollectionState = {
740+
...baseState,
741+
fakerSchemaGeneration: {
742+
status: MOCK_DATA_GENERATOR_REQUEST_GENERATING,
743+
requestId: 'generating_request_id',
744+
},
745+
};
746+
const action = fakerMappingGenerationFailedAction;
747+
const newState = collectionTabReducer(state, action);
748+
749+
expect(newState.fakerSchemaGeneration).to.deep.equal({
750+
status: MOCK_DATA_GENERATOR_REQUEST_ERROR,
751+
error: action.error,
752+
requestId: action.requestId,
753+
});
754+
755+
// Ensure other parts of state are unchanged
756+
expect(newState.schemaAnalysis).to.deep.equal(state.schemaAnalysis);
757+
expect(newState.mockDataGenerator).to.deep.equal(
758+
state.mockDataGenerator
759+
);
760+
expect(newState.namespace).to.deep.equal(state.namespace);
761+
expect(newState.metadata).to.deep.equal(state.metadata);
762+
});
763+
});
764+
});
481765
});

0 commit comments

Comments
 (0)