From af34b3fc8b12334406cd5de694330e483433f090 Mon Sep 17 00:00:00 2001 From: Dmytro Sonin Date: Tue, 29 Oct 2024 12:24:52 +0200 Subject: [PATCH 01/11] keep native errors for umanaged collections --- package/collection2/main.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/package/collection2/main.js b/package/collection2/main.js index d97041e..4602b5a 100644 --- a/package/collection2/main.js +++ b/package/collection2/main.js @@ -228,13 +228,18 @@ Mongo.Collection.prototype.attachSchema = function c2AttachSchema(ss, options) { _super.isCalledFromAsync = true; return Promise.resolve(_super.apply(this, args)); } catch (err) { - const addValidationErrorsPropName = - typeof validationContext.addValidationErrors === 'function' - ? 'addValidationErrors' + if (this._c2) { + const addValidationErrorsPropName = + typeof validationContext.addValidationErrors === 'function' + ? 'addValidationErrors' : 'addInvalidKeys'; parsingServerError([err], validationContext, addValidationErrorsPropName); const error = getErrorObject(validationContext, err.message, err.code); return Promise.reject(error); + } else { + // do not change error if collection isn't being validated by collection2 + return Promise.reject(err); + } } } else { return _super.apply(this, args); @@ -250,12 +255,17 @@ Mongo.Collection.prototype.attachSchema = function c2AttachSchema(ss, options) { try { return await _super.apply(this, args); } catch (err) { + if (this._c2) { const addValidationErrorsPropName = typeof validationContext.addValidationErrors === 'function' ? 'addValidationErrors' : 'addInvalidKeys'; parsingServerError([err], validationContext, addValidationErrorsPropName); throw getErrorObject(validationContext, err.message, err.code); + } else { + // do not change error if collection isn't being validated by collection2 + throw err; + } } }; } From e5cb830d0f7f73e00a557246a51abb0e416954c8 Mon Sep 17 00:00:00 2001 From: Dmytro Sonin Date: Tue, 29 Oct 2024 13:10:43 +0200 Subject: [PATCH 02/11] packagify fork --- package/collection2/.versions => .versions | 0 .../collection2.js => collection2.js | 0 package/collection2/dynamic.js => dynamic.js | 0 package/collection2/lib.js => lib.js | 0 package/collection2/main.js => main.js | 0 package/collection2/package.js => package.js | 2 +- package/collection2/static.js => static.js | 0 tests/.meteor/.finished-upgraders | 19 - tests/.meteor/.gitignore | 1 - tests/.meteor/.id | 7 - tests/.meteor/packages | 28 - tests/.meteor/platforms | 2 - tests/.meteor/release | 1 - tests/.meteor/versions | 69 - tests/autoValue.tests.js | 110 - tests/books.tests.js | 736 ----- tests/clean.tests.js | 273 -- tests/collection2.tests.js | 693 ----- tests/context.tests.js | 104 - tests/default.tests.js | 84 - tests/helper.js | 39 - tests/multi.tests.js | 563 ---- tests/package-lock.json | 2472 ----------------- tests/package.json | 18 - 24 files changed, 1 insertion(+), 5220 deletions(-) rename package/collection2/.versions => .versions (100%) rename package/collection2/collection2.js => collection2.js (100%) rename package/collection2/dynamic.js => dynamic.js (100%) rename package/collection2/lib.js => lib.js (100%) rename package/collection2/main.js => main.js (100%) rename package/collection2/package.js => package.js (96%) rename package/collection2/static.js => static.js (100%) delete mode 100644 tests/.meteor/.finished-upgraders delete mode 100644 tests/.meteor/.gitignore delete mode 100644 tests/.meteor/.id delete mode 100644 tests/.meteor/packages delete mode 100644 tests/.meteor/platforms delete mode 100644 tests/.meteor/release delete mode 100644 tests/.meteor/versions delete mode 100644 tests/autoValue.tests.js delete mode 100644 tests/books.tests.js delete mode 100644 tests/clean.tests.js delete mode 100644 tests/collection2.tests.js delete mode 100644 tests/context.tests.js delete mode 100644 tests/default.tests.js delete mode 100644 tests/helper.js delete mode 100644 tests/multi.tests.js delete mode 100644 tests/package-lock.json delete mode 100644 tests/package.json diff --git a/package/collection2/.versions b/.versions similarity index 100% rename from package/collection2/.versions rename to .versions diff --git a/package/collection2/collection2.js b/collection2.js similarity index 100% rename from package/collection2/collection2.js rename to collection2.js diff --git a/package/collection2/dynamic.js b/dynamic.js similarity index 100% rename from package/collection2/dynamic.js rename to dynamic.js diff --git a/package/collection2/lib.js b/lib.js similarity index 100% rename from package/collection2/lib.js rename to lib.js diff --git a/package/collection2/main.js b/main.js similarity index 100% rename from package/collection2/main.js rename to main.js diff --git a/package/collection2/package.js b/package.js similarity index 96% rename from package/collection2/package.js rename to package.js index 3826f7e..a1a51e7 100644 --- a/package/collection2/package.js +++ b/package.js @@ -5,7 +5,7 @@ Package.describe({ summary: 'Automatic validation of Meteor Mongo insert and update operations on the client and server', version: '4.0.3', - documentation: '../../README.md', + documentation: 'README.md', git: 'https://github.com/aldeed/meteor-collection2.git' }); diff --git a/package/collection2/static.js b/static.js similarity index 100% rename from package/collection2/static.js rename to static.js diff --git a/tests/.meteor/.finished-upgraders b/tests/.meteor/.finished-upgraders deleted file mode 100644 index c07b6ff..0000000 --- a/tests/.meteor/.finished-upgraders +++ /dev/null @@ -1,19 +0,0 @@ -# This file contains information which helps Meteor properly upgrade your -# app when you run 'meteor update'. You should check it into version control -# with your project. - -notices-for-0.9.0 -notices-for-0.9.1 -0.9.4-platform-file -notices-for-facebook-graph-api-2 -1.2.0-standard-minifiers-package -1.2.0-meteor-platform-split -1.2.0-cordova-changes -1.2.0-breaking-changes -1.3.0-split-minifiers-package -1.4.0-remove-old-dev-bundle-link -1.4.1-add-shell-server-package -1.4.3-split-account-service-packages -1.5-add-dynamic-import-package -1.7-split-underscore-from-meteor-base -1.8.3-split-jquery-from-blaze diff --git a/tests/.meteor/.gitignore b/tests/.meteor/.gitignore deleted file mode 100644 index 4083037..0000000 --- a/tests/.meteor/.gitignore +++ /dev/null @@ -1 +0,0 @@ -local diff --git a/tests/.meteor/.id b/tests/.meteor/.id deleted file mode 100644 index d4ab15b..0000000 --- a/tests/.meteor/.id +++ /dev/null @@ -1,7 +0,0 @@ -# This file contains a token that is unique to your project. -# Check it into your repository along with the rest of this directory. -# It can be used for purposes such as: -# - ensuring you don't accidentally deploy one app on top of another -# - providing package authors with aggregated statistics - -1nydo23oqnttp22v79u diff --git a/tests/.meteor/packages b/tests/.meteor/packages deleted file mode 100644 index 9911a6e..0000000 --- a/tests/.meteor/packages +++ /dev/null @@ -1,28 +0,0 @@ -# Meteor packages used by this project, one per line. -# Check this file (and the other files in this directory) into your repository. -# -# 'meteor add' and 'meteor remove' will edit this file for you, -# but you can also edit it by hand. - -meteor-base@1.5.2-rc300.4 # Packages every Meteor app needs to have -mongo@2.0.0-rc300.4 # The database Meteor supports right now -reactive-var@1.0.13-rc300.4 # Reactive variable for tracker -jquery # Helpful client-side library -tracker@1.3.4-rc300.4 # Meteor's client-side reactive programming library - -standard-minifier-css@1.9.3-rc300.4 # CSS minifier run for production mode -standard-minifier-js@3.0.0-rc300.4 # JS minifier run for production mode -es5-shim@4.8.1-rc300.4 # ECMAScript 5 compatibility for older browsers. -ecmascript@0.16.9-rc300.4 # Enable ECMAScript2015+ syntax in app code -shell-server@0.6.0-rc300.4 # Server-side component of the `meteor shell` command - -autopublish@1.0.8-rc300.4 # Publish all data to the clients (for prototyping) -insecure@1.0.8-rc300.4 # Allow all DB writes from clients (for prototyping) - -underscore@1.6.2-rc300.4 -dynamic-import@0.7.4-rc300.4 - -aldeed:simple-schema@2.0.0-beta300.0 -aldeed:collection2@4.0.2-beta.2 -meteortesting:mocha@3.1.0-beta300.0 -meteortesting:mocha-core@8.3.0-beta300.0 diff --git a/tests/.meteor/platforms b/tests/.meteor/platforms deleted file mode 100644 index efeba1b..0000000 --- a/tests/.meteor/platforms +++ /dev/null @@ -1,2 +0,0 @@ -server -browser diff --git a/tests/.meteor/release b/tests/.meteor/release deleted file mode 100644 index 443afb1..0000000 --- a/tests/.meteor/release +++ /dev/null @@ -1 +0,0 @@ -METEOR@3.0-rc.4 diff --git a/tests/.meteor/versions b/tests/.meteor/versions deleted file mode 100644 index 9cd5540..0000000 --- a/tests/.meteor/versions +++ /dev/null @@ -1,69 +0,0 @@ -aldeed:collection2@4.0.2 -aldeed:simple-schema@2.0.0-beta300.0 -allow-deny@2.0.0-rc300.4 -autopublish@1.0.8-rc300.4 -autoupdate@2.0.0-rc300.4 -babel-compiler@7.11.0-rc300.4 -babel-runtime@1.5.2-rc300.4 -base64@1.0.13-rc300.4 -binary-heap@1.0.12-rc300.4 -boilerplate-generator@2.0.0-rc300.4 -callback-hook@1.6.0-rc300.4 -check@1.4.2-rc300.4 -core-runtime@1.0.0-rc300.4 -ddp@1.4.2-rc300.4 -ddp-client@3.0.0-rc300.4 -ddp-common@1.4.1-rc300.4 -ddp-server@3.0.0-rc300.4 -diff-sequence@1.1.3-rc300.4 -dynamic-import@0.7.4-rc300.4 -ecmascript@0.16.9-rc300.4 -ecmascript-runtime@0.8.2-rc300.4 -ecmascript-runtime-client@0.12.2-rc300.4 -ecmascript-runtime-server@0.11.1-rc300.4 -ejson@1.1.4-rc300.4 -es5-shim@4.8.1-rc300.4 -facts-base@1.0.2-rc300.4 -fetch@0.1.5-rc300.4 -geojson-utils@1.0.12-rc300.4 -hot-code-push@1.0.5-rc300.4 -http@1.0.1 -id-map@1.2.0-rc300.4 -insecure@1.0.8-rc300.4 -inter-process-messaging@0.1.2-rc300.4 -jquery@3.0.0 -logging@1.3.5-rc300.4 -meteor@2.0.0-rc300.4 -meteor-base@1.5.2-rc300.4 -meteortesting:browser-tests@1.6.0-beta300.0 -meteortesting:mocha@3.1.0-beta300.0 -meteortesting:mocha-core@8.3.1-beta300.0 -minifier-css@2.0.0-rc300.4 -minifier-js@3.0.0-rc300.4 -minimongo@2.0.0-rc300.4 -modern-browsers@0.1.11-rc300.4 -modules@0.20.1-rc300.4 -modules-runtime@0.13.2-rc300.4 -mongo@2.0.0-rc300.4 -mongo-decimal@0.1.4-beta300.7 -mongo-dev-server@1.1.1-rc300.4 -mongo-id@1.0.9-rc300.4 -npm-mongo@4.16.2-rc300.4 -ordered-dict@1.2.0-rc300.4 -promise@1.0.0-rc300.4 -raix:eventemitter@1.0.0 -random@1.2.2-rc300.4 -react-fast-refresh@0.2.9-rc300.4 -reactive-var@1.0.13-rc300.4 -reload@1.3.2-rc300.4 -retry@1.1.1-rc300.4 -routepolicy@1.1.2-rc300.4 -shell-server@0.6.0-rc300.4 -socket-stream-client@0.5.3-rc300.4 -standard-minifier-css@1.9.3-rc300.4 -standard-minifier-js@3.0.0-rc300.4 -tracker@1.3.4-rc300.4 -typescript@5.4.3-rc300.4 -underscore@1.6.2-rc300.4 -webapp@2.0.0-rc300.4 -webapp-hashing@1.1.2-rc300.4 diff --git a/tests/autoValue.tests.js b/tests/autoValue.tests.js deleted file mode 100644 index 8a10950..0000000 --- a/tests/autoValue.tests.js +++ /dev/null @@ -1,110 +0,0 @@ -/* eslint-env mocha */ -import 'meteor/aldeed:collection2/static'; -import { Meteor } from 'meteor/meteor'; -import expect from 'expect'; -import { Mongo } from 'meteor/mongo'; -import SimpleSchema from "meteor/aldeed:simple-schema"; -import { callMongoMethod } from './helper'; - -const collection = new Mongo.Collection('autoValueTestCollection'); -const localCollection = new Mongo.Collection('autoValueTestLocalCollection', { - connection: null -}); - -[collection, localCollection].forEach((c) => { - c.attachSchema( - new SimpleSchema({ - clientAV: { - type: SimpleSchema.Integer, - optional: true, - autoValue() { - if (Meteor.isServer) return; - return (this.value || 0) + 1; - } - }, - serverAV: { - type: SimpleSchema.Integer, - optional: true, - autoValue() { - if (Meteor.isClient) return; - return (this.value || 0) + 1; - } - } - }) - ); -}); - -if (Meteor.isClient) { - describe('autoValue on client', function () { - it('for client insert, autoValues should be added on the server only (added to only a validated clone of the doc on client)', function (done) { - collection.insert({}, (error, id) => { - if (error) { - done(error); - return; - } - const doc = collection.findOne(id); - expect(doc.clientAV).toBe(undefined); - expect(doc.serverAV).toBe(1); - done(); - }); - }); - - it('runs function once for LocalCollection', function (done) { - localCollection.insert({}, (error, id) => { - if (error) { - done(error); - return; - } - const doc = localCollection.findOne(id); - expect(doc.clientAV).toBe(1); - expect(doc.serverAV).toBe(undefined); - done(); - }); - }); - - it('with getAutoValues false, does not run function for LocalCollection', function (done) { - localCollection.insert({}, { getAutoValues: false }, (error, id) => { - if (error) { - done(error); - return; - } - const doc = localCollection.findOne(id); - expect(doc.clientAV).toBe(undefined); - expect(doc.serverAV).toBe(undefined); - done(); - }); - }); - }); -} - -if (Meteor.isServer) { - describe('autoValue on server', function () { - it('runs function once', async function () { - const id = await callMongoMethod(collection, 'insert', [{}]); - const doc = await callMongoMethod(collection, 'findOne', [id]); - expect(doc.clientAV).toBe(undefined); - expect(doc.serverAV).toBe(1); - }); - - it('with getAutoValues false, does not run function', async function () { - const id = await callMongoMethod(collection, 'insert', [{}, { getAutoValues: false }]); - const doc = await callMongoMethod(collection, 'findOne', [id]); - expect(doc.clientAV).toBe(undefined); - expect(doc.serverAV).toBe(undefined); - }); - - it('runs function once for LocalCollection', async function () { - const id = await callMongoMethod(localCollection, 'insert', [{}]); - const doc = await callMongoMethod(localCollection, 'findOne', [id]); - expect(doc.clientAV).toBe(undefined); - expect(doc.serverAV).toBe(1); - }); - - it('with getAutoValues false, does not run function for LocalCollection', async function () { - const id = await callMongoMethod(localCollection, 'insert', [{}, { getAutoValues: false }]); - const doc = await callMongoMethod(localCollection, 'findOne', [id]); - expect(doc.clientAV).toBe(undefined); - expect(doc.serverAV).toBe(undefined); - }); - }); -} diff --git a/tests/books.tests.js b/tests/books.tests.js deleted file mode 100644 index 370cb08..0000000 --- a/tests/books.tests.js +++ /dev/null @@ -1,736 +0,0 @@ -import expect from 'expect'; -import { Mongo } from 'meteor/mongo'; -import SimpleSchema from "meteor/aldeed:simple-schema"; -import { Meteor } from 'meteor/meteor'; -import { _ } from 'meteor/underscore'; -import { callMeteorFetch, callMongoMethod } from './helper'; - -/* global describe, it, beforeEach */ - -const booksSchema = new SimpleSchema({ - title: { - type: String, - label: 'Title', - max: 200 - }, - author: { - type: String, - label: 'Author' - }, - copies: { - type: SimpleSchema.Integer, - label: 'Number of copies', - min: 0 - }, - lastCheckedOut: { - type: Date, - label: 'Last date this book was checked out', - optional: true - }, - summary: { - type: String, - label: 'Brief summary', - optional: true, - max: 1000 - }, - isbn: { - type: String, - label: 'ISBN', - optional: true - }, - field1: { - type: String, - optional: true - }, - field2: { - type: String, - optional: true - }, - createdAt: { - type: Date, - optional: true - }, - updatedAt: { - type: Date, - optional: true - } -}); - -const books = new Mongo.Collection('books'); -books.attachSchema(booksSchema); - -const upsertTest = new Mongo.Collection('upsertTest'); -upsertTest.attachSchema( - new SimpleSchema({ - _id: { type: String }, - foo: { type: Number } - }) -); - -export default function addBooksTests() { - describe('insert', function () { - beforeEach(async function () { - for (const book of await callMeteorFetch(books, {})) { - await callMongoMethod(books, 'remove', [book._id]); - } - }); - - if (Meteor.isClient) { - it('required', function (done) { - callMongoMethod(books, 'insert', [ - { - title: 'Ulysses', - author: 'James Joyce' - } - ]) - .then((result) => { - done(new Error('should not get here')); - }) - .catch((error) => { - // The insert will fail, error will be set, - expect(!!error).toBe(true); - // The list of errors is available by calling books.simpleSchema().namedContext().validationErrors() - const validationErrors = books.simpleSchema().namedContext().validationErrors(); - - expect(validationErrors.length).toBe(1); - const key = validationErrors[0] || {}; - expect(key.name).toBe('copies'); - expect(key.type).toBe('required'); - done(); - }); - }); - - it('validate false', function (done) { - const title = 'Validate False Client'; - - callMongoMethod(books, 'insert', [ - { - title, - author: 'James Joyce' - }, - { - validate: false, - validationContext: 'validateFalse' - } - ]) - .then(() => { - done(new Error('should not get here')); - }) - .catch(async (error) => { - const validationErrors = books - .simpleSchema() - .namedContext('validateFalse') - .validationErrors(); - - // When validated: false on the client, - // we should still get a validation error and validationErrors back from the server - expect(!!error).toBe(true); - // There should be an `invalidKeys` property on the error, too - expect(error.invalidKeys.length).toBe(1); - // expect(!!result).toBe(false) - expect(validationErrors.length).toBe(1); - - const insertedBook = await callMongoMethod(books, 'findOne', [{ title }]); - expect(!!insertedBook).toBe(false); - - // do a good one to set up update test - callMongoMethod(books, 'insert', [ - { - title: title + ' 2', - author: 'James Joyce', - copies: 1 - }, - { - validate: false, - validationContext: 'validateFalse2' - } - ]).then(async (newId) => { - const validationErrors = books - .simpleSchema() - .namedContext('validateFalse2') - .validationErrors(); - - expect(!!newId).toBe(true); - expect(validationErrors.length).toBe(0); - - const insertedBook = await callMongoMethod(books, 'findOne', [ - { title: title + ' 2' } - ]); - expect(!!insertedBook).toBe(true); - - callMongoMethod(books, 'update', [ - { - _id: newId - }, - { - $set: { - copies: 'Yes Please' - } - }, - { - validate: false, - validationContext: 'validateFalse3' - } - ]) - .then(() => { - done(new Error('should not get here')); - }) - .catch(async (error) => { - const validationErrors = books - .simpleSchema() - .namedContext('validateFalse3') - .validationErrors(); - - // When validated: false on the client, - // we should still get a validation error and invalidKeys from the server - expect(!!error).toBe(true); - // There should be an `invalidKeys` property on the error, too - expect(error.invalidKeys.length).toBe(1); - // expect(!!result).toBe(false) - expect(validationErrors.length).toBe(1); - - const updatedBook = await callMongoMethod(books, 'findOne', [newId]); - expect(!!updatedBook).toBe(true); - // copies should still be 1 because our new value failed validation on the server - expect(updatedBook.copies).toBe(1); - - // now try a good one - callMongoMethod(books, 'update', [ - { - _id: newId - }, - { - $set: { - copies: 3 - } - }, - { - validate: false, - validationContext: 'validateFalse4' - } - ]).then(async (result) => { - const validationErrors = books - .simpleSchema() - .namedContext('validateFalse4') - .validationErrors(); - expect(result).toBe(1); - expect(validationErrors.length).toBe(0); - - const updatedBook = await callMongoMethod(books, 'findOne', [newId]); - expect(!!updatedBook).toBe(true); - // copies should be changed because we used a valid value - expect(updatedBook.copies).toBe(3); - done(); - }); - }); - }); - }); - }); - } - - if (Meteor.isServer) { - it('required 1 on server', function (done) { - callMongoMethod(books, 'insert', [ - { - title: 'Ulysses', - author: 'James Joyce' - } - ]) - .then((result) => { - done('should not get here'); - }) - .catch((error) => { - // The insert will fail, error will be set, - expect(!!error).toBe(true); - // and result will be false because "copies" is required. - // TODO expect(result).toBe(false); - // The list of errors is available - // by calling books.simpleSchema().namedContext().validationErrors() - const validationErrors = books.simpleSchema().namedContext().validationErrors(); - expect(validationErrors.length).toBe(1); - - const key = validationErrors[0] || {}; - expect(key.name).toBe('copies'); - expect(key.type).toBe('required'); - done(); - }); - }); - - it('required 2 on server', async function () { - const title = 'Validate False Server'; - - let error; - let newId; - let result; - // do a good one to set up update test - try { - newId = await callMongoMethod(books, 'insert', [ - { - title: title + ' 2', - author: 'James Joyce', - copies: 1 - }, - { - validate: false, - validationContext: 'validateFalse2' - } - ]); - } catch (e) { - error = e; - } - - let validationErrors = books - .simpleSchema() - .namedContext('validateFalse2') - .validationErrors(); - - expect(!!error).toBe(false); - expect(!!newId).toBe(true); - expect(validationErrors.length).toBe(0); - - const insertedBook = await callMongoMethod(books, 'findOne', [{ title: title + ' 2' }]); - expect(!!insertedBook).toBe(true); - - try { - result = await callMongoMethod(books, 'update', [ - { - _id: newId - }, - { - $set: { - copies: 'Yes Please' - } - }, - { - validate: false, - validationContext: 'validateFalse3' - } - ]); - error = null; - } catch (e) { - error = e; - } - - let updatedBook; - validationErrors = books.simpleSchema().namedContext('validateFalse3').validationErrors(); - - // When validated: false on the server, validation should be skipped - expect(!!error).toBe(false); - expect(!!result).toBe(true); - expect(validationErrors.length).toBe(0); - - updatedBook = await callMongoMethod(books, 'findOne', [newId]); - - expect(!!updatedBook).toBe(true); - // copies should be changed despite being invalid because we skipped validation on the server - expect(updatedBook.copies).toBe('Yes Please'); - - // now try a good one - try { - result = await callMongoMethod(books, 'update', [ - { - _id: newId - }, - { - $set: { - copies: 3 - } - }, - { - validate: false, - validationContext: 'validateFalse4' - } - ]); - error = null; - } catch (e) { - error = e; - } - - validationErrors = books.simpleSchema().namedContext('validateFalse4').validationErrors(); - expect(!!error).toBe(false); - expect(result).toBe(1); - expect(validationErrors.length).toBe(0); - - updatedBook = await callMongoMethod(books, 'findOne', [newId]); - expect(!!updatedBook).toBe(true); - // copies should be changed because we used a valid value - expect(updatedBook.copies).toBe(3); - }); - - it('no validation when calling underlying _collection on the server', function (done) { - callMongoMethod(books._collection, 'insert', [ - { - title: 'Ulysses', - author: 'James Joyce', - copies: 1, - updatedAt: new Date() - } - ]) - .then(() => { - done(); - }) - .catch(done); - }); - } - }); - - if (Meteor.isServer) { - describe('upsert', function () { - function getCallback(done) { - return (result) => { - expect(result.numberAffected).toBe(1); - - const validationErrors = books.simpleSchema().namedContext().validationErrors(); - expect(validationErrors.length).toBe(0); - - done(); - }; - } - - function getUpdateCallback(done) { - return (result) => { - expect(result).toBe(1); - - const validationErrors = books.simpleSchema().namedContext().validationErrors(); - expect(validationErrors.length).toBe(0); - - done(); - }; - } - - function getErrorCallback(done) { - return (error) => { - expect(!!error).toBe(true); - // expect(!!result).toBe(false) - - const validationErrors = books.simpleSchema().namedContext().validationErrors(); - expect(validationErrors.length).toBe(1); - - done(); - }; - } - - it('valid', function (done) { - callMongoMethod(books, 'upsert', [ - { - title: 'Ulysses', - author: 'James Joyce' - }, - { - $set: { - title: 'Ulysses', - author: 'James Joyce', - copies: 1 - } - } - ]) - .then(getCallback(done)) - .catch(done); - }); - - it('upsert as update should update entity by _id - valid', function (done) { - callMongoMethod(books, 'insert', [{ title: 'new', author: 'author new', copies: 2 }]) - .then((id) => { - return callMongoMethod(books, 'upsert', [ - { - _id: id - }, - { - $set: { - title: 'Ulysses', - author: 'James Joyce', - copies: 1 - } - } - ]); - }) - .then(getCallback(done)) - .catch(done); - }); - - it('upsert as update - valid', function (done) { - callMongoMethod(books, 'update', [ - { - title: 'Ulysses', - author: 'James Joyce' - }, - { - $set: { - title: 'Ulysses', - author: 'James Joyce', - copies: 1 - } - }, - { - upsert: true - } - ]) - .then(getUpdateCallback(done)) - .catch(done); - }); - - it('upsert as update with $and', function (done) { - callMongoMethod(books, 'update', [ - { - $and: [{ title: 'Ulysses' }, { author: 'James Joyce' }] - }, - { - $set: { - title: 'Ulysses', - author: 'James Joyce', - copies: 1 - } - }, - { - upsert: true - } - ]) - .then(getUpdateCallback(done)) - .catch(done); - }); - - it('upsert - invalid', function (done) { - callMongoMethod(books, 'upsert', [ - { - title: 'Ulysses', - author: 'James Joyce' - }, - { - $set: { - copies: -1 - } - } - ]) - .then(() => done(new Error('should not get here'))) - .catch(getErrorCallback(done)); - }); - - it('upsert as update - invalid', function (done) { - callMongoMethod(books, 'update', [ - { - title: 'Ulysses', - author: 'James Joyce' - }, - { - $set: { - copies: -1 - } - }, - { - upsert: true - } - ]) - .then(() => done(new Error('should not get here'))) - .catch(getErrorCallback(done)); - }); - - it('upsert - valid with selector', function (done) { - callMongoMethod(books, 'upsert', [ - { - title: 'Ulysses', - author: 'James Joyce' - }, - { - $set: { - copies: 1 - } - } - ]) - .then(getCallback(done)) - .catch(done); - }); - - it('upsert as update - valid with selector', function (done) { - callMongoMethod(books, 'update', [ - { - title: 'Ulysses', - author: 'James Joyce' - }, - { - $set: { - copies: 1 - } - }, - { - upsert: true - } - ]) - .then(getUpdateCallback(done)) - .catch(done); - }); - }); - } - - if (Meteor.isServer) { - it('validate false', function (done) { - const title = 'Validate False Server'; - let insertedBook, error, newId; - - callMongoMethod(books, 'insert', [ - { - title, - author: 'James Joyce' - }, - { - validate: false, - validationContext: 'validateFalse' - } - ]) - .then(async (result) => { - const validationErrors = books - .simpleSchema() - .namedContext('validateFalse') - .validationErrors(); - - // When validated: false on the server, validation should be skipped - expect(!!error).toBe(false); - expect(!!result).toBe(true); - expect(validationErrors.length).toBe(0); - - insertedBook = await callMongoMethod(books, 'findOne', [{ title }]); - expect(!!insertedBook).toBe(true); - - return callMongoMethod(books, 'insert', [ - { - title: title + ' 2', - author: 'James Joyce', - copies: 1 - }, - { - validate: false, - validationContext: 'validateFalse2' - } - ]); - }) - .then((_newId) => { - newId = _newId; - - const validationErrors = books - .simpleSchema() - .namedContext('validateFalse2') - .validationErrors(); - - expect(!!newId).toBe(true); - expect(validationErrors.length).toBe(0); - - return callMongoMethod(books, 'findOne', [{ title: title + ' 2' }]); - }) - .then((insertedBook) => { - expect(!!insertedBook).toBe(true); - - return callMongoMethod(books, 'update', [ - { - _id: newId - }, - { - $set: { - copies: 'Yes Please' - } - }, - { - validate: false, - validationContext: 'validateFalse3' - } - ]); - }) - .then((result) => { - const validationErrors = books - .simpleSchema() - .namedContext('validateFalse3') - .validationErrors(); - - // When validated: false on the server, validation should be skipped - expect(!!result).toBe(true); - expect(validationErrors.length).toBe(0); - return callMongoMethod(books, 'findOne', [newId]); - }) - .then((updatedBook) => { - expect(!!updatedBook).toBe(true); - // copies should be changed despite being invalid because we skipped validation on the server - expect(updatedBook.copies).toBe('Yes Please'); - - return callMongoMethod(books, 'update', [ - { - _id: newId - }, - { - $set: { - copies: 3 - } - }, - { - validate: false, - validationContext: 'validateFalse4' - } - ]); - }) - .then((result) => { - const validationErrors = books - .simpleSchema() - .namedContext('validateFalse4') - .validationErrors(); - expect(result).toBe(1); - expect(validationErrors.length).toBe(0); - - return callMongoMethod(books, 'findOne', [newId]); - }) - .then((updatedBook) => { - expect(!!updatedBook).toBe(true); - // copies should be changed because we used a valid value - expect(updatedBook.copies).toBe(3); - done(); - }) - .catch(done); - }); - } - - if (Meteor.isServer) { - it('bypassCollection2 5', async function () { - const id = await callMongoMethod(books, 'insert', [{}, { bypassCollection2: true }]); - - await callMongoMethod(books, 'update', [ - id, - { $set: { copies: 2 } }, - { bypassCollection2: true } - ]); - }); - - it('everything filtered out', async function () { - try { - await callMongoMethod(upsertTest, 'update', [ - { _id: '123' }, - { - $set: { - boo: 1 - } - } - ]); - } catch (e) { - expect(e.message).toBe( - 'After filtering out keys not in the schema, your modifier is now empty' - ); - } - }); - - it('upsert works with schema that allows _id', async function () { - await callMongoMethod(upsertTest, 'remove', [{}]); - - const upsertTestId = await callMongoMethod(upsertTest, 'insert', [{ foo: 1 }]); - - await callMongoMethod(upsertTest, 'update', [ - { _id: upsertTestId }, - { - $set: { - foo: 2 - } - }, - { - upsert: true - } - ]); - - const doc = await callMongoMethod(upsertTest, 'findOne', [upsertTestId]); - expect(doc.foo).toBe(2); - }); - } -} diff --git a/tests/clean.tests.js b/tests/clean.tests.js deleted file mode 100644 index c2d8f2c..0000000 --- a/tests/clean.tests.js +++ /dev/null @@ -1,273 +0,0 @@ -import expect from 'expect'; -import { Mongo } from 'meteor/mongo'; -import SimpleSchema from "meteor/aldeed:simple-schema"; -import { Meteor } from 'meteor/meteor'; -import { callMongoMethod } from './helper'; - -/* global describe it */ - -let collection; - -if (Meteor.isClient) { - collection = new Mongo.Collection('cleanTests', { connection: null }); -} else { - collection = new Mongo.Collection('cleanTests'); -} - -describe('clean options', function () { - describe('filter', function () { - it('keeps default schema clean options', function (done) { - const schema = new SimpleSchema( - { - name: String - }, - { - clean: { - filter: false - } - } - ); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: 'name', bad: 'prop' }]) - .then(() => { - done(new Error('Should not have inserted')); - }) - .catch(() => { - done(); - }); - }); - - it('keeps operation clean options', function (done) { - const schema = new SimpleSchema( - { - name: String - }, - { - clean: { - filter: true - } - } - ); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: 'name', bad: 'prop' }, { filter: false }]) - .then(() => { - done(new Error('Should not have inserted')); - }) - .catch(() => { - done(); - }); - }); - - it('has clean option on by default', function (done) { - const schema = new SimpleSchema({ name: String }); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: 'name', bad: 'prop' }]) - .then(() => { - done(); - }) - .catch((err) => { - done(err); - }); - }); - }); - - describe('autoConvert', function () { - it('keeps default schema clean options', function (done) { - const schema = new SimpleSchema( - { - name: String - }, - { - clean: { - autoConvert: false - } - } - ); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: 1 }]) - .then(() => { - done(new Error('Should not have inserted')); - }) - .catch(() => { - done(); - }); - }); - - it('keeps operation clean options', function (done) { - const schema = new SimpleSchema( - { - name: String - }, - { - clean: { - autoConvert: true - } - } - ); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: 1 }, { autoConvert: false }]) - .then(() => { - done(new Error('Should not have inserted')); - }) - .catch(() => { - done(); - }); - }); - - it('has clean option on by default', function (done) { - const schema = new SimpleSchema({ name: String }); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: 1 }]) - .then(() => { - done(); - }) - .catch((err) => { - done(err); - }); - }); - }); - - describe('removeEmptyStrings', function () { - it('keeps default schema clean options', function (done) { - const schema = new SimpleSchema( - { - name: String, - other: Number - }, - { - clean: { - removeEmptyStrings: false - } - } - ); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: '', other: 1 }]) - .then(() => { - done(); - }) - .catch((err) => { - done(err); - }); - }); - - it('keeps operation clean options', function (done) { - const schema = new SimpleSchema( - { - name: String, - other: Number - }, - { - clean: { - removeEmptyStrings: true - } - } - ); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: '', other: 1 }, { removeEmptyStrings: false }]) - .then(() => { - done(); - }) - .catch((err) => { - done(err); - }); - }); - - it('has clean option on by default', function (done) { - const schema = new SimpleSchema({ name: String, other: Number }); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: '', other: 1 }]) - .then(() => { - done(new Error('Should not have inserted')); - }) - .catch(() => { - done(); - }); - }); - }); - - describe('trimStrings', function () { - it('keeps default schema clean options', function (done) { - const schema = new SimpleSchema( - { - name: String - }, - { - clean: { - trimStrings: false - } - } - ); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: ' foo ' }]) - .then(async (_id) => { - const data = await callMongoMethod(collection, 'findOne', [_id]); - expect(data).toEqual({ _id, name: ' foo ' }); - done(); - }) - .catch((err) => { - done(err); - }); - }); - - it('keeps operation clean options', function (done) { - const schema = new SimpleSchema( - { - name: String - }, - { - clean: { - trimStrings: true - } - } - ); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: ' foo ' }, { trimStrings: false }]) - .then(async (_id) => { - const data = await callMongoMethod(collection, 'findOne', [_id]); - expect(data).toEqual({ _id, name: ' foo ' }); - done(); - }) - .catch((err) => { - done(err); - }); - }); - - it('has clean option on by default', function (done) { - const schema = new SimpleSchema({ name: String }); - - collection.attachSchema(schema, { replace: true }); - - callMongoMethod(collection, 'insert', [{ name: ' foo ' }]) - .then(async (_id) => { - const data = await callMongoMethod(collection, 'findOne', [_id]); - expect(data).toEqual({ _id, name: 'foo' }); - done(); - }) - .catch((err) => { - done(err); - }); - }); - }); -}); diff --git a/tests/collection2.tests.js b/tests/collection2.tests.js deleted file mode 100644 index 5767697..0000000 --- a/tests/collection2.tests.js +++ /dev/null @@ -1,693 +0,0 @@ -import expect from 'expect'; -import { Mongo } from 'meteor/mongo'; -import SimpleSchema from 'meteor/aldeed:simple-schema'; -import addMultiTests from './multi.tests.js'; -import addBooksTests from './books.tests.js'; -import addContextTests from './context.tests.js'; -import addDefaultValuesTests from './default.tests.js'; -import { Meteor } from 'meteor/meteor'; -import { callMongoMethod } from './helper'; - -/* global describe, it */ - -describe('collection2', function () { - it('attach and get simpleSchema for normal collection', function () { - const mc = new Mongo.Collection('mc', Meteor.isClient ? { connection: null } : undefined); - - mc.attachSchema( - new SimpleSchema({ - foo: { type: String } - }) - ); - - expect(mc.simpleSchema() instanceof SimpleSchema).toBe(true); - }); - - it('attach and get simpleSchema for local collection', function () { - const mc = new Mongo.Collection(null); - - mc.attachSchema( - new SimpleSchema({ - foo: { type: String } - }) - ); - - expect(mc.simpleSchema() instanceof SimpleSchema).toBe(true); - }); - - it('handles prototype-less objects', async function () { - const prototypelessTest = new Mongo.Collection( - 'prototypelessTest', - Meteor.isClient ? { connection: null } : undefined - ); - - prototypelessTest.attachSchema( - new SimpleSchema({ - foo: { - type: String - } - }) - ); - - const prototypelessObject = Object.create(null); - prototypelessObject.foo = 'bar'; - - await callMongoMethod(prototypelessTest, 'insert', [prototypelessObject]); - }); - - if (Meteor.isServer) { - // https://github.com/aldeed/meteor-collection2/issues/243 - it('upsert runs autoValue only once', async function () { - const upsertAutoValueTest = new Mongo.Collection( - 'upsertAutoValueTest', - Meteor.isClient ? { connection: null } : undefined - ); - let times = 0; - - upsertAutoValueTest.attachSchema( - new SimpleSchema({ - foo: { - type: String - }, - av: { - type: String, - autoValue() { - times++; - return 'test'; - } - } - }) - ); - - await callMongoMethod(upsertAutoValueTest, 'remove', [{}]); - - await callMongoMethod(upsertAutoValueTest, 'upsert', [ - { - foo: 'bar' - }, - { - $set: { - av: 'abc' - } - } - ]); - expect(times).toBe(1); - }); - - // https://forums.meteor.com/t/simpl-schema-update-error-while-using-lte-operator-when-calling-update-by-the-field-of-type-date/50414/3 - it('upsert can handle query operators in the selector', async function () { - const upsertQueryOperatorsTest = new Mongo.Collection( - 'upsertQueryOperatorsTest', - Meteor.isClient ? { connection: null } : undefined - ); - - upsertQueryOperatorsTest.attachSchema( - new SimpleSchema({ - foo: { - type: Date, - optional: true - }, - bar: Number, - baz: Number - }) - ); - - await callMongoMethod(upsertQueryOperatorsTest, 'remove', [{}]); - const oneDayInMs = 1000 * 60 * 60 * 24; - const yesterday = new Date(Date.now() - oneDayInMs); - const tomorrow = new Date(Date.now() + oneDayInMs); - - const { numberAffected, insertedId } = await callMongoMethod( - upsertQueryOperatorsTest, - 'upsert', - [ - { - foo: { $gte: yesterday, $lte: tomorrow } - }, - { - $set: { - bar: 2 - }, - $inc: { - baz: 4 - } - } - ] - ); - - expect(numberAffected).toBe(1); - - const doc = await callMongoMethod(upsertQueryOperatorsTest, 'findOne', []); - expect(insertedId).toBe(doc._id); - expect(doc.bar).toBe(2); - expect(doc.baz).toBe(4); - }); - - it('upsert with schema can handle query operator which contains undefined or null', async function () { - const upsertQueryOperatorUndefinedTest = new Mongo.Collection( - 'upsertQueryOperatorUndefinedTest', - Meteor.isClient ? { connection: null } : undefined - ); - - upsertQueryOperatorUndefinedTest.attachSchema( - new SimpleSchema({ - foo: { - type: String, - optional: true - }, - bar: Number, - baz: Number - }) - ); - - // Let's try for undefined. - await callMongoMethod(upsertQueryOperatorUndefinedTest, 'remove', [{}]); - - const result = await callMongoMethod(upsertQueryOperatorUndefinedTest, 'upsert', [ - { - foo: undefined - }, - { - $set: { - bar: 2 - }, - $inc: { - baz: 4 - } - } - ]); - - expect(result.numberAffected).toBe(1); - - const doc = await callMongoMethod(upsertQueryOperatorUndefinedTest, 'findOne', []); - expect(result.insertedId).toBe(doc._id); - expect(doc.foo).toBe(undefined); - expect(doc.bar).toBe(2); - expect(doc.baz).toBe(4); - - // Let's try for null. - - await callMongoMethod(upsertQueryOperatorUndefinedTest, 'remove', [{}]); - - const result2 = await callMongoMethod(upsertQueryOperatorUndefinedTest, 'upsert', [ - { - foo: null - }, - { - $set: { - bar: 2 - }, - $inc: { - baz: 4 - } - } - ]); - - expect(result2.numberAffected).toBe(1); - - const doc2 = await callMongoMethod(upsertQueryOperatorUndefinedTest, 'findOne', []); - expect(result2.insertedId).toBe(doc2._id); - expect(doc2.foo).toBe(null); - expect(doc2.bar).toBe(2); - expect(doc2.baz).toBe(4); - }); - - it('upsert with schema can handle query operator "eq" correctly in the selector when property is left out in $set or $setOnInsert', async function () { - const upsertQueryOperatorEqTest = new Mongo.Collection( - 'upsertQueryOperatorEqTest', - Meteor.isClient ? { connection: null } : undefined - ); - - upsertQueryOperatorEqTest.attachSchema( - new SimpleSchema({ - foo: String, - bar: Number, - baz: Number - }) - ); - - await callMongoMethod(upsertQueryOperatorEqTest, 'remove', [{}]); - - const result = await callMongoMethod(upsertQueryOperatorEqTest, 'upsert', [ - { - foo: { $eq: 'test' } - }, - { - $set: { - bar: 2 - }, - $inc: { - baz: 4 - } - } - ]); - - expect(result.numberAffected).toBe(1); - - const doc = await callMongoMethod(upsertQueryOperatorEqTest, 'findOne', []); - expect(result.insertedId).toBe(doc._id); - expect(doc.foo).toBe('test'); - expect(doc.bar).toBe(2); - expect(doc.baz).toBe(4); - }); - - it('upsert with schema can handle query operator "in" with one element correctly in the selector when property is left out in $set or $setOnInsert', async function () { - const upsertQueryOperatorInSingleTest = new Mongo.Collection( - 'upsertQueryOperatorInSingleTest', - Meteor.isClient ? { connection: null } : undefined - ); - - upsertQueryOperatorInSingleTest.attachSchema( - new SimpleSchema({ - foo: String, - bar: Number, - baz: Number - }) - ); - - await callMongoMethod(upsertQueryOperatorInSingleTest, 'remove', [{}]); - - const result = await callMongoMethod(upsertQueryOperatorInSingleTest, 'upsert', [ - { - foo: { $in: ['test'] } - }, - { - $set: { - bar: 2 - }, - $inc: { - baz: 4 - } - } - ]); - - expect(result.numberAffected).toBe(1); - - const doc = await callMongoMethod(upsertQueryOperatorInSingleTest, 'findOne', []); - expect(result.insertedId).toBe(doc._id); - expect(doc.foo).toBe('test'); - expect(doc.bar).toBe(2); - expect(doc.baz).toBe(4); - }); - - it('upsert with schema can handle query operator "in" with multiple elements correctly in the selector when property is left out in $set or $setOnInsert', async function () { - const upsertQueryOperatorInMultiTest = new Mongo.Collection( - 'upsertQueryOperatorInMultiTest', - Meteor.isClient ? { connection: null } : undefined - ); - - upsertQueryOperatorInMultiTest.attachSchema( - new SimpleSchema({ - foo: { - type: String, - optional: true - }, - bar: Number, - baz: Number - }) - ); - - await callMongoMethod(upsertQueryOperatorInMultiTest, 'remove', [{}]); - - const result = await callMongoMethod(upsertQueryOperatorInMultiTest, 'upsert', [ - { - foo: { $in: ['test', 'test2'] } - }, - { - $set: { - bar: 2 - }, - $inc: { - baz: 4 - } - } - ]); - - expect(result.numberAffected).toBe(1); - - const doc = await callMongoMethod(upsertQueryOperatorInMultiTest, 'findOne', []); - expect(result.insertedId).toBe(doc._id); - expect(doc.foo).toBe(undefined); - expect(doc.bar).toBe(2); - expect(doc.baz).toBe(4); - }); - - // https://github.com/Meteor-Community-Packages/meteor-collection2/issues/408 - it('upsert with schema can handle nested objects correctly', async function () { - const upsertQueryOperatorNestedObject = new Mongo.Collection( - 'upsertQueryOperatorNestedObject', - Meteor.isClient ? { connection: null } : undefined - ); - - upsertQueryOperatorNestedObject.attachSchema( - new SimpleSchema({ - foo: { - type: new SimpleSchema({ - bar: { - type: String - }, - baz: { - type: String - } - }) - }, - test: { - type: Date - } - }) - ); - - await callMongoMethod(upsertQueryOperatorNestedObject, 'remove', [{}]); - - const testDateValue = new Date(); - - const result = await callMongoMethod(upsertQueryOperatorNestedObject, 'upsert', [ - { - test: '1' - }, - { - $set: { - foo: { - bar: '1', - baz: '2' - }, - test: testDateValue - } - } - ]); - - expect(result.numberAffected).toBe(1); - - const doc = await callMongoMethod(upsertQueryOperatorNestedObject, 'findOne', [ - { - _id: result.insertedId - } - ]); - - expect(result.insertedId).toBe(doc._id); - expect(doc.foo.bar).toBe('1'); - expect(doc.foo.baz).toBe('2'); - expect(doc.test).toEqual(testDateValue); - }); - - it('upsert with schema can handle query operator "$and" including inner nested selectors correctly when properties is left out in $set or $setOnInsert', async function () { - const upsertQueryOperatorAndTest = new Mongo.Collection( - 'upsertQueryOperatorAndTest', - Meteor.isClient ? { connection: null } : undefined - ); - - upsertQueryOperatorAndTest.attachSchema( - new SimpleSchema({ - foo: String, - test1: String, - test2: String, - bar: Number, - baz: Number - }) - ); - - await callMongoMethod(upsertQueryOperatorAndTest, 'remove', [{}]); - - const result = await callMongoMethod(upsertQueryOperatorAndTest, 'upsert', [ - { - foo: 'test', - $and: [{ test1: 'abc' }, { $and: [{ test2: { $in: ['abc'] } }] }] - }, - { - $set: { - bar: 2 - }, - $inc: { - baz: 4 - } - } - ]); - - expect(result.numberAffected).toBe(1); - - const doc = await callMongoMethod(upsertQueryOperatorAndTest, 'findOne', []); - expect(result.insertedId).toBe(doc._id); - expect(doc.foo).toBe('test'); - expect(doc.test1).toBe('abc'); - expect(doc.test2).toBe('abc'); - expect(doc.bar).toBe(2); - expect(doc.baz).toBe(4); - }); - } - - it('no errors when using a schemaless collection', async function () { - const noSchemaCollection = new Mongo.Collection('noSchema', { - transform(doc) { - doc.userFoo = 'userBar'; - return doc; - }, - connection: Meteor.isClient ? null : undefined - }); - - const newId = await callMongoMethod(noSchemaCollection, 'insert', [ - { - a: 1, - b: 2 - } - ]); - - expect(!!newId).toBe(true); - - const doc = await callMongoMethod(noSchemaCollection, 'findOne', [newId]); - expect(doc instanceof Object).toBe(true); - expect(doc.userFoo).toBe('userBar'); - - await callMongoMethod(noSchemaCollection, 'update', [ - { - _id: newId - }, - { - $set: { - a: 3, - b: 4 - } - } - ]); - }); - - it('empty strings are removed but we can override', async function () { - const RESSchema = new SimpleSchema({ - foo: { type: String }, - bar: { type: String, optional: true } - }); - - const RES = new Mongo.Collection('RES', Meteor.isClient ? { connection: null } : undefined); - RES.attachSchema(RESSchema); - - // Remove empty strings (default) - const newId1 = await callMongoMethod(RES, 'insert', [ - { - foo: 'foo', - bar: '' - } - ]); - expect(typeof newId1).toBe('string'); - - const doc = await callMongoMethod(RES, 'findOne', [newId1]); - expect(doc instanceof Object).toBe(true); - expect(doc.bar).toBe(undefined); - - // Don't remove empty strings - const newId2 = await callMongoMethod(RES, 'insert', [ - { - foo: 'foo', - bar: '' - }, - { - removeEmptyStrings: false - } - ]); - - expect(typeof newId2).toBe('string'); - - const doc2 = await callMongoMethod(RES, 'findOne', [newId2]); - expect(doc2 instanceof Object).toBe(true); - expect(doc2.bar).toBe(''); - - // Don't remove empty strings for an update either - const result = await callMongoMethod(RES, 'update', [ - { - _id: newId1 - }, - { - $set: { - bar: '' - } - }, - { - removeEmptyStrings: false - } - ]); - - expect(result).toBe(1); - const doc3 = await callMongoMethod(RES, 'findOne', [newId1]); - expect(doc3 instanceof Object).toBe(true); - expect(doc3.bar).toBe(''); - }); - - it('extending a schema after attaching it, collection2 validation respects the extension', async function () { - const schema = new SimpleSchema({ - foo: String - }); - - const collection = new Mongo.Collection( - 'ExtendAfterAttach', - Meteor.isClient ? { connection: null } : undefined - ); - collection.attachSchema(schema); - - try { - await callMongoMethod(collection, 'insert', [ - { - foo: 'foo', - bar: 'bar' - }, - { - filter: false - } - ]); - } catch (error) { - expect(error.invalidKeys[0].name).toBe('bar'); - - schema.extend({ - bar: String - }); - - await callMongoMethod(collection, 'insert', [ - { - foo: 'foo', - bar: 'bar' - }, - { - filter: false - } - ]); - - return; - } - - throw new Error('should not get here'); - }); - - it('extending a schema with a selector after attaching it, collection2 validation respects the extension', async () => { - const schema = new SimpleSchema({ - foo: String - }); - - const collection = new Mongo.Collection( - 'ExtendAfterAttach2', - Meteor.isClient ? { connection: null } : undefined - ); - collection.attachSchema(schema, { selector: { foo: 'foo' } }); - - try { - await callMongoMethod(collection, 'insert', [ - { - foo: 'foo', - bar: 'bar' - }, - { - filter: false - } - ]); - } catch (error) { - expect(error.invalidKeys[0].name).toBe('bar'); - - schema.extend({ - bar: String - }); - - await callMongoMethod(collection, 'insert', [ - { - foo: 'foo', - bar: 'bar' - }, - { - filter: false - } - ]); - } - }); - - it('pick or omit schema fields when options are provided', async function () { - const collectionSchema = new SimpleSchema({ - foo: { type: String }, - bar: { type: String, optional: true } - }); - - const collection = new Mongo.Collection( - 'pickOrOmit', - Meteor.isClient ? { connection: null } : undefined - ); - collection.attachSchema(collectionSchema); - - // Test error from including both pick and omit 2 - let errorThrown = false; - - try { - await callMongoMethod(collection, 'insert', [ - { foo: 'foo', bar: '' }, - { pick: ['foo'], omit: ['foo'] } - ]); - } catch (error) { - expect(error.message).toBe('pick and omit options are mutually exclusive'); - errorThrown = true; - } - - expect(errorThrown).toBe(true); // should have thrown error - - // Omit required field 'foo' - const newId2 = await callMongoMethod(collection, 'insert', [ - { bar: 'test' }, - { omit: ['foo'] } - ]); - - expect(typeof newId2).toBe('string'); - - const doc = await callMongoMethod(collection, 'findOne', [newId2]); - expect(doc instanceof Object).toBe(true); - expect(doc.foo).toBe(undefined); - expect(doc.bar).toBe('test'); - - // Pick only 'foo' - const result = await callMongoMethod(collection, 'update', [ - { _id: newId2 }, - { $set: { foo: 'test', bar: 'changed' } }, - { pick: ['foo'] } - ]); - - expect(result).toBe(1); - - const doc2 = await callMongoMethod(collection, 'findOne', [newId2]); - expect(doc2 instanceof Object).toBe(true); - expect(doc2.foo).toBe('test'); - expect(doc2.bar).toBe('test'); - }); - - it('pins code when throwing out an error', async function () { - if (Meteor.isServer) { - const testCollection = new Mongo.Collection('duplicate_code_collection'); - await testCollection.createIndexAsync({ name: 1 }, { unique: true }); - // remove any inserts on previous re-runs to avoid test failing - await testCollection.removeAsync({}); - // first insert - await testCollection.insertAsync({ name: 'foo' }); - try { - // second insert, throws out error - await testCollection.insertAsync({ name: 'foo' }); - } catch (e) { - expect(e.code).toBe(11000); - } - } - }); - - addBooksTests(); - addContextTests(); - addDefaultValuesTests(); - addMultiTests(); -}); diff --git a/tests/context.tests.js b/tests/context.tests.js deleted file mode 100644 index 6576feb..0000000 --- a/tests/context.tests.js +++ /dev/null @@ -1,104 +0,0 @@ -import expect from 'expect'; -import { Mongo } from 'meteor/mongo'; -import SimpleSchema from "meteor/aldeed:simple-schema"; -import { Meteor } from 'meteor/meteor'; -import { callMongoMethod } from './helper'; - -/* global it */ - -const contextCheckSchema = new SimpleSchema({ - foo: { - type: String, - optional: true - }, - context: { - type: Object, - optional: true, - defaultValue: {} - }, - 'context.userId': { - type: String, - optional: true, - autoValue() { - return this.userId; - } - }, - 'context.isFromTrustedCode': { - type: Boolean, - optional: true, - autoValue() { - return this.isFromTrustedCode; - } - }, - 'context.isInsert': { - type: Boolean, - optional: true, - autoValue() { - return this.isInsert; - } - }, - 'context.isUpdate': { - type: Boolean, - optional: true, - autoValue() { - return this.isUpdate; - } - }, - 'context.docId': { - type: String, - optional: true, - autoValue() { - return this.docId; - } - } -}); - -const contextCheck = new Mongo.Collection('contextCheck'); -contextCheck.attachSchema(contextCheckSchema); - -export default function addContextTests() { - it('AutoValue Context', async function () { - const testId = await callMongoMethod(contextCheck, 'insert', [{}]); - - let ctx = await callMongoMethod(contextCheck, 'findOne', [testId]); - expect(ctx.context.isInsert).toBe(true); - expect(ctx.context.isUpdate).toBe(false); - expect(ctx.context.userId).toBe(null); - expect(ctx.context.docId).toBe(undefined); - expect(ctx.context.isFromTrustedCode).toBe(!Meteor.isClient); - - await callMongoMethod(contextCheck, 'update', [ - { - _id: testId - }, - { - $set: { - context: {}, - foo: 'bar' - } - } - ]); - - ctx = await callMongoMethod(contextCheck, 'findOne', [testId]); - expect(ctx.foo).toBe('bar'); - expect(ctx.context.isUpdate).toBe(true); - expect(ctx.context.isInsert).toBe(false); - expect(ctx.context.userId).toBe(null); - expect(ctx.context.docId).toBe(testId); - expect(ctx.context.isFromTrustedCode).toBe(!Meteor.isClient); - - // make sure docId works with `_id` direct, too - await callMongoMethod(contextCheck, 'update', [ - testId, - { - $set: { - context: {}, - foo: 'bar' - } - } - ]); - - ctx = await callMongoMethod(contextCheck, 'findOne', [testId]); - expect(ctx.context.docId).toBe(testId); - }); -} diff --git a/tests/default.tests.js b/tests/default.tests.js deleted file mode 100644 index 412b3e7..0000000 --- a/tests/default.tests.js +++ /dev/null @@ -1,84 +0,0 @@ -import expect from 'expect'; -import { Mongo } from 'meteor/mongo'; -import SimpleSchema from "meteor/aldeed:simple-schema"; -import { Meteor } from 'meteor/meteor'; -import { callMongoMethod } from './helper'; - -/* global it */ - -const defaultValuesSchema = new SimpleSchema({ - bool1: { - type: Boolean, - defaultValue: false - } -}); - -const defaultValues = new Mongo.Collection('dv'); -defaultValues.attachSchema(defaultValuesSchema); -global.defaultValues = defaultValues; - -export default function addDefaultValuesTests() { - if (Meteor.isServer) { - it('defaultValues', function (done) { - let p; - - // Base case - callMongoMethod(defaultValues, 'insert', [{}]) - .then(async (testId1) => { - p = await callMongoMethod(defaultValues, 'findOne', [testId1]); - expect(p.bool1).toBe(false); - - // Ensure that default values do not mess with inserts and updates of the field - callMongoMethod(defaultValues, 'insert', [ - { - bool1: true - } - ]) - .then(async (testId2) => { - p = await callMongoMethod(defaultValues, 'findOne', [testId2]); - expect(p.bool1).toBe(true); - - callMongoMethod(defaultValues, 'update', [ - testId1, - { - $set: { - bool1: true - } - } - ]) - .then(async () => { - p = await callMongoMethod(defaultValues, 'findOne', [testId1]); - expect(p.bool1).toBe(true); - done(); - }) - .catch(done); - }) - .catch(done); - }) - .catch(done); - }); - } else { - it('defaultValues', async function () { - // Base case - const testId1 = await callMongoMethod(defaultValues, 'insert', [{}]); - let p = await callMongoMethod(defaultValues, 'findOne', [testId1]); - expect(p.bool1).toBe(false); - - // Ensure that default values do not mess with inserts and updates of the field - const testId2 = await callMongoMethod(defaultValues, 'insert', [{ bool1: true }]); - p = await callMongoMethod(defaultValues, 'findOne', [testId2]); - expect(p.bool1).toBe(true); - - await callMongoMethod(defaultValues, 'update', [ - testId1, - { - $set: { - bool1: true - } - } - ]); - p = await callMongoMethod(defaultValues, 'findOne', [testId1]); - expect(p.bool1).toBe(true); - }); - } -} diff --git a/tests/helper.js b/tests/helper.js deleted file mode 100644 index 7df3827..0000000 --- a/tests/helper.js +++ /dev/null @@ -1,39 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Mongo } from 'meteor/mongo'; - -let ASYNC_FRIENDLY = false; - -if (Mongo.Collection.prototype.insertAsync) { - ASYNC_FRIENDLY = true; -} - -const getMethodNameByMeteorVersion = (methodName) => - ASYNC_FRIENDLY ? `${methodName}Async` : methodName; - -export function callMongoMethod(collection, method, args) { - const methodName = getMethodNameByMeteorVersion(method); - - return new Promise((resolve, reject) => { - if (ASYNC_FRIENDLY) { - try { - resolve(collection[methodName](...args)); - } catch (error) { - reject(error); - } - } else { - collection[methodName](...args) - .then(resolve) - .catch(reject); - } - }); -} - -export function callMeteorFetch(collection, selector) { - return new Promise((resolve, reject) => { - if (ASYNC_FRIENDLY) { - resolve(collection.find(selector).fetchAsync()); - } else { - resolve(collection.find(selector).fetch()); - } - }); -} diff --git a/tests/multi.tests.js b/tests/multi.tests.js deleted file mode 100644 index 5ffd5a1..0000000 --- a/tests/multi.tests.js +++ /dev/null @@ -1,563 +0,0 @@ -import expect from 'expect'; -import { Mongo } from 'meteor/mongo'; -import SimpleSchema from "meteor/aldeed:simple-schema"; -import { Meteor } from 'meteor/meteor'; -import { callMeteorFetch, callMongoMethod } from './helper'; - -/* global describe, it, beforeEach */ - -const productSchema = new SimpleSchema({ - _id: { - type: String, - optional: true - }, - title: { - type: String, - defaultValue: '' - }, - type: { - label: 'Product Type', - type: String, - defaultValue: 'simple' - }, - description: { - type: String, - defaultValue: 'This is a simple product.' - } -}); - -const productVariantSchema = new SimpleSchema({ - _id: { - type: String, - optional: true - }, - title: { - type: String, - defaultValue: '' - }, - optionTitle: { - label: 'Option', - type: String, - optional: true - }, - type: { - label: 'Product Variant Type', - type: String, - defaultValue: 'variant' - }, - price: { - label: 'Price', - type: Number, - min: 0, - optional: true, - defaultValue: 5 - }, - createdAt: { - type: Date - } -}); - -const extendedProductSchema = new SimpleSchema(productSchema); -extendedProductSchema.extend({ - barcode: { - type: String, - defaultValue: 'ABC123' - } -}); - -/* Products */ - -// Need to define the client one on both client and server -let products = new Mongo.Collection('TestProductsClient'); -products.attachSchema(productSchema, { selector: { type: 'simple' } }); -products.attachSchema(productVariantSchema, { selector: { type: 'variant' } }); -if (Meteor.isServer) { - products = new Mongo.Collection('TestProductsServer'); - products.attachSchema(productSchema, { selector: { type: 'simple' } }); - products.attachSchema(productVariantSchema, { - selector: { type: 'variant' } - }); -} - -/* Extended Products */ -// Need to define the client one on both client and server -let extendedProducts = new Mongo.Collection('ExtendedProductsClient'); -extendedProducts.attachSchema(productSchema, { selector: { type: 'simple' } }); -extendedProducts.attachSchema(productVariantSchema, { - selector: { type: 'variant' } -}); -extendedProducts.attachSchema(extendedProductSchema, { - selector: { type: 'simple' } -}); -if (Meteor.isServer) { - extendedProducts = new Mongo.Collection('ExtendedProductsServer'); - extendedProducts.attachSchema(productSchema, { - selector: { type: 'simple' } - }); - extendedProducts.attachSchema(productVariantSchema, { - selector: { type: 'variant' } - }); - extendedProducts.attachSchema(extendedProductSchema, { - selector: { type: 'simple' } - }); -} - -export default function addMultiTests() { - describe('multiple top-level schemas', function () { - beforeEach(async function () { - for (const doc of await callMeteorFetch(products, {})) { - await callMongoMethod(products, 'remove', [doc._id]); - } - - for (const doc of await callMeteorFetch(extendedProducts, {})) { - await callMongoMethod(products, 'remove', [doc._id]); - } - - /* - for await (const doc of products.find({})) { - await products.removeAsync(doc._id); - } - - for await (const doc of extendedProducts.find({})) { - await products.removeAsync(doc._id); - } - */ - }); - - it('works', function () { - const c = new Mongo.Collection('multiSchema'); - - // Attach two different schemas - c.attachSchema( - new SimpleSchema({ - one: { type: String } - }) - ); - c.attachSchema( - new SimpleSchema({ - two: { type: String } - }) - ); - - // Check the combined schema - let combinedSchema = c.simpleSchema(); - expect(combinedSchema._schemaKeys.includes('one')).toBe(true); - expect(combinedSchema._schemaKeys.includes('two')).toBe(true); - expect(combinedSchema.schema('two').type).toEqual(SimpleSchema.oneOf(String)); - - // Attach a third schema and make sure that it extends/overwrites the others - c.attachSchema( - new SimpleSchema({ - two: { type: SimpleSchema.Integer } - }) - ); - combinedSchema = c.simpleSchema(); - expect(combinedSchema._schemaKeys.includes('one')).toBe(true); - expect(combinedSchema._schemaKeys.includes('two')).toBe(true); - expect(combinedSchema.schema('two').type).toEqual(SimpleSchema.oneOf(SimpleSchema.Integer)); - - // Ensure that we've only attached two deny functions - expect(c._validators.insert.deny.length).toBe(2); - expect(c._validators.update.deny.length).toBe(2); - }); - - if (Meteor.isServer) { - it('inserts doc correctly with selector passed via doc', async function () { - const productId = await callMongoMethod(products, 'insert', [ - { - title: 'Product one', - type: 'simple' // selector in doc - } - ]); - - const product = await callMongoMethod(products, 'findOne', [productId]); - expect(product.description).toBe('This is a simple product.'); - expect(product.price).toBe(undefined); - - const productId3 = await callMongoMethod(products, 'insert', [ - { - title: 'Product three', - createdAt: new Date(), - type: 'variant' // other selector in doc - } - ]); - const product3 = await callMongoMethod(products, 'findOne', [productId3]); - expect(product3.description).toBe(undefined); - expect(product3.price).toBe(5); - }); - - // Passing selector in options works only on the server because - // client options are not sent to the server and made availabe in - // the deny functions, where we call .simpleSchema() - // - // Also synchronous only works on server - it('insert selects the correct schema', async function () { - const productId = await callMongoMethod(products, 'insert', [ - { - title: 'Product one' - }, - { selector: { type: 'simple' } } - ]); - - const productVariantId = await callMongoMethod(products, 'insert', [ - { - title: 'Product variant one', - createdAt: new Date() - }, - { selector: { type: 'variant' } } - ]); - - const product = await callMongoMethod(products, 'findOne', [productId]); - const productVariant = await callMongoMethod(products, 'findOne', [productVariantId]); - - // we should receive new docs with correct property set for each type of doc - expect(product.description).toBe('This is a simple product.'); - expect(product.price).toBe(undefined); - expect(productVariant.description).toBe(undefined); - expect(productVariant.price).toBe(5); - }); - - it('inserts doc correctly with selector passed via doc and via