Skip to content

Commit 662cf63

Browse files
committed
Probability
1 parent 49d7b1c commit 662cf63

File tree

2 files changed

+183
-3
lines changed

2 files changed

+183
-3
lines changed

packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.spec.ts

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ import { expect } from 'chai';
22
import { generateScript, type FieldMapping } from './script-generation-utils';
33

44
describe('Script Generation', () => {
5-
const createFieldMapping = (fakerMethod: string): FieldMapping => ({
5+
const createFieldMapping = (
6+
fakerMethod: string,
7+
probability?: number
8+
): FieldMapping => ({
69
mongoType: 'String',
710
fakerMethod,
811
fakerArgs: [],
12+
...(probability !== undefined && { probability }),
913
});
1014

1115
describe('Basic field generation', () => {
@@ -671,4 +675,168 @@ describe('Script Generation', () => {
671675
}
672676
});
673677
});
678+
679+
describe('Probability Handling', () => {
680+
it('should generate normal faker call for probability 1.0', () => {
681+
const schema = {
682+
name: createFieldMapping('person.fullName', 1.0),
683+
};
684+
685+
const result = generateScript(schema, {
686+
databaseName: 'testdb',
687+
collectionName: 'users',
688+
documentCount: 1,
689+
});
690+
691+
expect(result.success).to.equal(true);
692+
if (result.success) {
693+
expect(result.script).to.contain('faker.person.fullName()');
694+
expect(result.script).not.to.contain('Math.random()');
695+
}
696+
});
697+
698+
it('should generate normal faker call when probability is undefined', () => {
699+
const schema = {
700+
name: createFieldMapping('person.fullName'), // No probability specified
701+
};
702+
703+
const result = generateScript(schema, {
704+
databaseName: 'testdb',
705+
collectionName: 'users',
706+
documentCount: 1,
707+
});
708+
709+
expect(result.success).to.equal(true);
710+
if (result.success) {
711+
expect(result.script).to.contain('faker.person.fullName()');
712+
expect(result.script).not.to.contain('Math.random()');
713+
}
714+
});
715+
716+
it('should use probabilistic rendering when probability < 1.0', () => {
717+
const schema = {
718+
optionalField: createFieldMapping('lorem.word', 0.7),
719+
};
720+
721+
const result = generateScript(schema, {
722+
databaseName: 'testdb',
723+
collectionName: 'posts',
724+
documentCount: 1,
725+
});
726+
727+
expect(result.success).to.equal(true);
728+
if (result.success) {
729+
expect(result.script).to.contain(
730+
'...(Math.random() < 0.7 ? { optionalField: faker.lorem.word() } : {})'
731+
);
732+
}
733+
});
734+
735+
it('should handle zero probability', () => {
736+
const schema = {
737+
rareField: createFieldMapping('lorem.word', 0),
738+
};
739+
740+
const result = generateScript(schema, {
741+
databaseName: 'testdb',
742+
collectionName: 'posts',
743+
documentCount: 1,
744+
});
745+
746+
expect(result.success).to.equal(true);
747+
if (result.success) {
748+
expect(result.script).to.contain(
749+
'...(Math.random() < 0 ? { rareField: faker.lorem.word() } : {})'
750+
);
751+
}
752+
});
753+
754+
it('should handle mixed probability fields', () => {
755+
const schema = {
756+
alwaysPresent: createFieldMapping('person.fullName', 1.0),
757+
sometimesPresent: createFieldMapping('internet.email', 0.8),
758+
rarelyPresent: createFieldMapping('phone.number', 0.2),
759+
defaultProbability: createFieldMapping('lorem.word'), // undefined = 1.0
760+
};
761+
762+
const result = generateScript(schema, {
763+
databaseName: 'testdb',
764+
collectionName: 'users',
765+
documentCount: 1,
766+
});
767+
768+
expect(result.success).to.equal(true);
769+
if (result.success) {
770+
// Always present (probability 1.0)
771+
expect(result.script).to.contain('faker.person.fullName()');
772+
expect(result.script).not.to.contain(
773+
'Math.random() < 1 ? { alwaysPresent:'
774+
);
775+
776+
// Sometimes present (probability 0.8)
777+
expect(result.script).to.contain(
778+
'...(Math.random() < 0.8 ? { sometimesPresent: faker.internet.email() } : {})'
779+
);
780+
781+
// Rarely present (probability 0.2)
782+
expect(result.script).to.contain(
783+
'...(Math.random() < 0.2 ? { rarelyPresent: faker.phone.number() } : {})'
784+
);
785+
786+
// Default probability (undefined = 1.0)
787+
expect(result.script).to.contain('faker.lorem.word()');
788+
expect(result.script).not.to.contain(
789+
'Math.random() < 1 ? { defaultProbability:'
790+
);
791+
}
792+
});
793+
794+
it('should handle probability with faker arguments', () => {
795+
const schema = {
796+
conditionalAge: {
797+
mongoType: 'number',
798+
fakerMethod: 'number.int',
799+
fakerArgs: [18, 65],
800+
probability: 0.9,
801+
},
802+
};
803+
804+
const result = generateScript(schema, {
805+
databaseName: 'testdb',
806+
collectionName: 'users',
807+
documentCount: 1,
808+
});
809+
810+
expect(result.success).to.equal(true);
811+
if (result.success) {
812+
expect(result.script).to.contain(
813+
'...(Math.random() < 0.9 ? { conditionalAge: faker.number.int(18, 65) } : {})'
814+
);
815+
}
816+
});
817+
818+
it('should handle probability with unrecognized fields', () => {
819+
const schema = {
820+
unknownField: {
821+
mongoType: 'string',
822+
fakerMethod: 'unrecognized',
823+
fakerArgs: [],
824+
probability: 0.5,
825+
},
826+
};
827+
828+
const result = generateScript(schema, {
829+
databaseName: 'testdb',
830+
collectionName: 'test',
831+
documentCount: 1,
832+
});
833+
834+
expect(result.success).to.equal(true);
835+
if (result.success) {
836+
expect(result.script).to.contain(
837+
'...(Math.random() < 0.5 ? { unknownField: faker.lorem.word() } : {})'
838+
);
839+
}
840+
});
841+
});
674842
});

packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export interface FieldMapping {
44
mongoType: string;
55
fakerMethod: string;
66
fakerArgs: FakerArg[];
7+
probability?: number; // 0.0 - 1.0 frequency of field (defaults to 1.0)
78
}
89

910
// Hierarchical array length map that mirrors document structure
@@ -299,8 +300,19 @@ function generateDocumentCode(
299300
for (const [fieldName, value] of Object.entries(structure)) {
300301
if ('mongoType' in value) {
301302
// It's a field mapping
302-
const fakerCall = generateFakerCall(value as FieldMapping);
303-
rootLevelFields.push(`${fieldIndent}${fieldName}: ${fakerCall}`);
303+
const mapping = value as FieldMapping;
304+
const fakerCall = generateFakerCall(mapping);
305+
const probability = mapping.probability ?? 1.0;
306+
307+
if (probability < 1.0) {
308+
// Use Math.random for conditional field inclusion
309+
rootLevelFields.push(
310+
`${fieldIndent}...(Math.random() < ${probability} ? { ${fieldName}: ${fakerCall} } : {})`
311+
);
312+
} else {
313+
// Normal field inclusion
314+
rootLevelFields.push(`${fieldIndent}${fieldName}: ${fakerCall}`);
315+
}
304316
} else if ('type' in value && value.type === 'array') {
305317
// It's an array
306318
const nestedArrayLengthMap =

0 commit comments

Comments
 (0)