|
1 | | -import { analyzeDocuments } from '../../src'; |
2 | 1 | import Ajv2020 from 'ajv/dist/2020'; |
3 | 2 | import assert from 'assert'; |
4 | | -import { ObjectId, Int32, Double, EJSON } from 'bson'; |
| 3 | +import { |
| 4 | + Double, |
| 5 | + Int32, |
| 6 | + ObjectId, |
| 7 | + EJSON |
| 8 | +} from 'bson'; |
5 | 9 | import { MongoClient, type Db } from 'mongodb'; |
6 | 10 | import { mochaTestServer } from '@mongodb-js/compass-test-server'; |
7 | 11 |
|
| 12 | +import { allValidBSONTypesWithEdgeCasesDoc } from '../all-bson-types-fixture'; |
| 13 | +import { analyzeDocuments } from '../../src'; |
| 14 | + |
8 | 15 | const bsonDocuments = [{ |
9 | 16 | _id: new ObjectId('67863e82fb817085a6b0ebad'), |
10 | 17 | title: 'My book', |
@@ -47,74 +54,156 @@ describe('Documents -> Generate schema -> Validate Documents against the schema' |
47 | 54 | }); |
48 | 55 | }); |
49 | 56 |
|
50 | | -describe('Documents -> Generate schema -> Use schema in validation rule in MongoDB -> Validate documents against the schema', function() { |
| 57 | +describe('With a MongoDB Cluster', function() { |
51 | 58 | let client: MongoClient; |
52 | 59 | let db: Db; |
53 | 60 | const cluster = mochaTestServer(); |
54 | 61 |
|
55 | 62 | before(async function() { |
56 | | - // Create the schema validation rule. |
57 | | - const analyzedDocuments = await analyzeDocuments(bsonDocuments); |
58 | | - const schema = await analyzedDocuments.getMongoDBJsonSchema(); |
59 | | - const validationRule = { |
60 | | - $jsonSchema: schema |
61 | | - }; |
62 | | - |
63 | 63 | // Connect to the mongodb instance. |
64 | 64 | const connectionString = cluster().connectionString; |
65 | 65 | client = new MongoClient(connectionString); |
66 | 66 | await client.connect(); |
67 | 67 | db = client.db('test'); |
68 | | - |
69 | | - // Create a collection with the schema validation in Compass. |
70 | | - await db.createCollection('books', { |
71 | | - validator: validationRule |
72 | | - }); |
73 | 68 | }); |
| 69 | + |
74 | 70 | after(async function() { |
75 | 71 | await client?.close(); |
76 | 72 | }); |
77 | 73 |
|
78 | | - it('allows inserting valid documents', async function() { |
79 | | - await db.collection('books').insertMany(bsonDocuments); |
80 | | - }); |
| 74 | + describe('Documents -> Generate basic schema -> Use schema in validation rule in MongoDB -> Validate documents against the schema', function() { |
| 75 | + before(async function() { |
| 76 | + // Create the schema validation rule. |
| 77 | + const analyzedDocuments = await analyzeDocuments(bsonDocuments); |
| 78 | + const schema = await analyzedDocuments.getMongoDBJsonSchema(); |
| 79 | + const validationRule = { |
| 80 | + $jsonSchema: schema |
| 81 | + }; |
| 82 | + |
| 83 | + // Create a collection with the schema validation. |
| 84 | + await db.createCollection('books', { |
| 85 | + validator: validationRule |
| 86 | + }); |
| 87 | + }); |
| 88 | + |
| 89 | + it('allows inserting valid documents', async function() { |
| 90 | + await db.collection('books').insertMany(bsonDocuments); |
| 91 | + }); |
| 92 | + |
| 93 | + it('prevents inserting invalid documents', async function() { |
| 94 | + const invalidDocs = [{ |
| 95 | + _id: new ObjectId('67863e82fb817085a6b0ebba'), |
| 96 | + title: 'Pineapple 1', |
| 97 | + year: new Int32(1983), |
| 98 | + genres: [ |
| 99 | + 'crimi', |
| 100 | + 'comedy', |
| 101 | + { |
| 102 | + short: 'scifi', |
| 103 | + long: 'science fiction' |
| 104 | + } |
| 105 | + ], |
| 106 | + number: 'an invalid string' |
| 107 | + }, { |
| 108 | + _id: new ObjectId('67863eacfb817085a6b0ebbb'), |
| 109 | + title: 'Pineapple 2', |
| 110 | + year: 'year a string' |
| 111 | + }, { |
| 112 | + _id: new ObjectId('67863eacfb817085a6b0ebbc'), |
| 113 | + title: 123, |
| 114 | + year: new Int32('1999') |
| 115 | + }, { |
| 116 | + _id: new ObjectId('67863eacfb817085a6b0ebbc'), |
| 117 | + title: 'No year' |
| 118 | + }]; |
81 | 119 |
|
82 | | - it('prevents inserting invalid documents', async function() { |
83 | | - const invalidDocs = [{ |
84 | | - _id: new ObjectId('67863e82fb817085a6b0ebba'), |
85 | | - title: 'Pineapple 1', |
86 | | - year: new Int32(1983), |
87 | | - genres: [ |
88 | | - 'crimi', |
89 | | - 'comedy', |
90 | | - { |
91 | | - short: 'scifi', |
92 | | - long: 'science fiction' |
| 120 | + for (const doc of invalidDocs) { |
| 121 | + try { |
| 122 | + await db.collection('books').insertOne(doc); |
| 123 | + |
| 124 | + throw new Error('This should not be reached'); |
| 125 | + } catch (e: any) { |
| 126 | + const expectedMessage = 'Document failed validation'; |
| 127 | + assert.ok(e.message.includes(expectedMessage), `Expected error ${e.message} message to include "${expectedMessage}", doc: ${doc._id}`); |
93 | 128 | } |
94 | | - ], |
95 | | - number: 'an invalid string' |
96 | | - }, { |
97 | | - _id: new ObjectId('67863eacfb817085a6b0ebbb'), |
98 | | - title: 'Pineapple 2', |
99 | | - year: 'year a string' |
100 | | - }, { |
101 | | - _id: new ObjectId('67863eacfb817085a6b0ebbc'), |
102 | | - title: 123, |
103 | | - year: new Int32('1999') |
104 | | - }, { |
105 | | - _id: new ObjectId('67863eacfb817085a6b0ebbc'), |
106 | | - title: 'No year' |
107 | | - }]; |
108 | | - |
109 | | - for (const doc of invalidDocs) { |
| 129 | + } |
| 130 | + }); |
| 131 | + }); |
| 132 | + |
| 133 | + describe('[All Types] Documents -> Generate basic schema -> Use schema in validation rule in MongoDB -> Validate documents against the schema', function() { |
| 134 | + const allTypesCollection = 'allTypes'; |
| 135 | + |
| 136 | + before(async function() { |
| 137 | + await db.collection(allTypesCollection).insertOne(allValidBSONTypesWithEdgeCasesDoc); |
| 138 | + const docsFromCollection = await db.collection(allTypesCollection).find({}, { promoteValues: false }).toArray(); |
| 139 | + |
| 140 | + // Create the schema validation rule. |
| 141 | + const analyzedDocuments = await analyzeDocuments(docsFromCollection); |
| 142 | + const schema = await analyzedDocuments.getMongoDBJsonSchema(); |
| 143 | + const validationRule = { |
| 144 | + $jsonSchema: schema |
| 145 | + }; |
| 146 | + // Update the collection with the schema validation. |
| 147 | + await db.command({ |
| 148 | + collMod: allTypesCollection, |
| 149 | + validator: validationRule |
| 150 | + }); |
| 151 | + }); |
| 152 | + |
| 153 | + it('allows inserting valid documents (does not error)', async function() { |
| 154 | + const docs = [{ |
| 155 | + ...allValidBSONTypesWithEdgeCasesDoc, |
| 156 | + _id: new ObjectId() |
| 157 | + }, { |
| 158 | + ...allValidBSONTypesWithEdgeCasesDoc, |
| 159 | + _id: new ObjectId() |
| 160 | + }]; |
| 161 | + |
110 | 162 | try { |
111 | | - await db.collection('books').insertOne(doc); |
| 163 | + await db.collection(allTypesCollection).insertMany(docs); |
| 164 | + } catch (err) { |
| 165 | + console.error('Error inserting documents', EJSON.stringify(err, undefined, 2)); |
| 166 | + throw err; |
| 167 | + } |
| 168 | + }); |
| 169 | + |
| 170 | + it('prevents inserting invalid documents', async function() { |
| 171 | + const invalidDocs = [{ |
| 172 | + _id: new ObjectId('67863e82fb817085a6b0ebba'), |
| 173 | + title: 'Pineapple 1', |
| 174 | + year: new Int32(1983), |
| 175 | + genres: [ |
| 176 | + 'crimi', |
| 177 | + 'comedy', |
| 178 | + { |
| 179 | + short: 'scifi', |
| 180 | + long: 'science fiction' |
| 181 | + } |
| 182 | + ], |
| 183 | + number: 'an invalid string' |
| 184 | + }, { |
| 185 | + _id: new ObjectId('67863eacfb817085a6b0ebbb'), |
| 186 | + title: 'Pineapple 2', |
| 187 | + year: 'year a string' |
| 188 | + }, { |
| 189 | + _id: new ObjectId('67863eacfb817085a6b0ebbc'), |
| 190 | + title: 123, |
| 191 | + year: new Int32('1999') |
| 192 | + }, { |
| 193 | + _id: new ObjectId('67863eacfb817085a6b0ebbc'), |
| 194 | + title: 'No year' |
| 195 | + }]; |
| 196 | + |
| 197 | + for (const doc of invalidDocs) { |
| 198 | + try { |
| 199 | + await db.collection(allTypesCollection).insertOne(doc); |
112 | 200 |
|
113 | | - throw new Error('This should not be reached'); |
114 | | - } catch (e: any) { |
115 | | - const expectedMessage = 'Document failed validation'; |
116 | | - assert.ok(e.message.includes(expectedMessage), `Expected error ${e.message} message to include "${expectedMessage}", doc: ${doc._id}`); |
| 201 | + throw new Error('This should not be reached'); |
| 202 | + } catch (e: any) { |
| 203 | + const expectedMessage = 'Document failed validation'; |
| 204 | + assert.ok(e.message.includes(expectedMessage), `Expected error ${e.message} message to include "${expectedMessage}", doc: ${doc._id}`); |
| 205 | + } |
117 | 206 | } |
118 | | - } |
| 207 | + }); |
119 | 208 | }); |
120 | 209 | }); |
0 commit comments