From 9e35dcc9a4ebf80380a97f1e16c53dd35255f110 Mon Sep 17 00:00:00 2001 From: Manuel Timita Date: Sun, 3 Mar 2024 20:48:02 +0000 Subject: [PATCH 1/3] Additional tests to check for behaviour in PR #177 The discussion in #177 suggests `changed` is not properly triggered in subscription. Tests were added to validate that this is indeed the case (answer: no). The changes in #177 have been reverted. All tests have been modified to only use the sync minimongo API, for simplicity. README updated --- .npm/package/npm-shrinkwrap.json | 2 +- README.md | 10 ++ lib/subscription.js | 3 +- package.js | 2 - tests/client.js | 260 +++++++++++++++++++------------ tests/common.js | 2 + tests/server.js | 65 ++++++-- 7 files changed, 232 insertions(+), 112 deletions(-) diff --git a/.npm/package/npm-shrinkwrap.json b/.npm/package/npm-shrinkwrap.json index 20919e5..0c67550 100644 --- a/.npm/package/npm-shrinkwrap.json +++ b/.npm/package/npm-shrinkwrap.json @@ -1,5 +1,5 @@ { - "lockfileVersion": 1, + "lockfileVersion": 4, "dependencies": { "assertion-error": { "version": "1.1.0", diff --git a/README.md b/README.md index 287be74..bd44182 100644 --- a/README.md +++ b/README.md @@ -269,6 +269,16 @@ This package is great for publishing small sets of related documents. If you use You will not be able to access `this.userId` inside your `find` functions if you use [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). +## Testing + +Run the following: +```shell +meteor test-packages reywood:publish-composite --driver-package meteortesting:mocha +``` +The tests are executing a combination of methods and subscriptions. The quickest option was to add a pause after each +operation (see usage of `sleep()` in `./tests/server.js`), to allow for the publications to send down all the +documents. However, this is flaky, so you may want to refresh the browser if you notice tests failing for no +apparent reason. ## Reporting issues/bugs diff --git a/lib/subscription.js b/lib/subscription.js index bfe76e1..4e0b77c 100644 --- a/lib/subscription.js +++ b/lib/subscription.js @@ -23,8 +23,7 @@ class Subscription { added (collectionName, doc) { this.refCounter.increment(collectionName, doc._id) - const existingDoc = this.docHash[buildHashKey(collectionName, doc.id)] - if (!existingDoc) { + if (this._hasDocChanged(collectionName, doc._id, doc)) { debugLog('Subscription.added', `${collectionName}:${doc._id}`) this.meteorSub.added(collectionName, doc._id, doc) this._addDocHash(collectionName, doc) diff --git a/package.js b/package.js index d8cbf5d..30c180f 100644 --- a/package.js +++ b/package.js @@ -36,8 +36,6 @@ Package.onUse((api) => { // meteor test-packages reywood:publish-composite --driver-package meteortesting:mocha Package.onTest((api) => { Npm.depends({ - 'lodash.isequal': '4.5.0', - 'chai-as-promised': '7.1.1', chai: '4.3.10' }) api.use([ diff --git a/tests/client.js b/tests/client.js index 9c5ca46..f023110 100644 --- a/tests/client.js +++ b/tests/client.js @@ -1,21 +1,16 @@ /* global describe, it */ -/* eslint-disable no-unused-expressions */ - import { Meteor } from 'meteor/meteor' import { Mongo } from 'meteor/mongo' -import chai, { expect } from 'chai' -import chaiAsPromised from 'chai-as-promised' -import { Authors, Comments, Posts } from './common' - -chai.use(chaiAsPromised) +import { expect } from 'chai' +import {Authors, Comments, Groups, Posts} from './common' const Articles = new Mongo.Collection('articles') const CommentAuthors = new Mongo.Collection('commentAuthors') describe('publishComposite', () => { /** - * Define test helper - */ + * Define test helper + */ const testPublication = (testName, options) => { it(testName, (onComplete) => { let subscription @@ -42,7 +37,7 @@ describe('publishComposite', () => { }) } - const asyncExpect = (expectAction, onComplete) => { + const tryExpect = (expectAction, onComplete) => { try { expectAction() } catch (error) { @@ -51,17 +46,104 @@ describe('publishComposite', () => { } } + const expectCursorCountToEqual = (cursor, value, onComplete) => { + return tryExpect(() => expect(cursor.fetch().length).to.equal(value), onComplete) + } + + const expectValueToBeUndefined = (value, onComplete) => { + return tryExpect(() => expect(value).to.be.undefined, onComplete) + } + + const expectValueToBeDefined = (value, onComplete) => { + return tryExpect(() => expect(value).to.not.be.undefined, onComplete) + } + /** - * Define tests - */ + * Define tests + */ + testPublication('Should publish all groups', { + publication: 'allGroups', + + testHandler: (onComplete) => { + expectCursorCountToEqual(Groups.find(), 2, onComplete) + + onComplete() + } + }) + + testPublication('Should publish group authors', { + publication: 'allGroups', + + testHandler: (onComplete) => { + expectCursorCountToEqual(Authors.find({ groupIds: 'Writers' }), 4, onComplete) + expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 1, onComplete) + + onComplete() + } + }) + + testPublication('Should publish author added to new group', { + publication: 'allGroups', + + testHandler: (onComplete) => { + expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 1, onComplete) + + Meteor.call('addAuthorToGroup', 'stephen', 'Editors', (error) => { + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Authors.find({ groupIds: 'Writers' }), 4, onComplete) + expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 2, onComplete) + + onComplete() + }) + } + }) + + testPublication('Should unpublish author removed from group', { + publication: 'allGroups', + + testHandler: (onComplete) => { + expectCursorCountToEqual(Authors.find({ groupIds: 'Writers' }), 4, onComplete) + + Meteor.call('removeAuthorFromGroup', 'richard', 'Writers', (error) => { + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Authors.find({ groupIds: 'Writers' }), 3, onComplete) + + onComplete() + }) + } + }) + + testPublication('Should publish/unpublish author added to/removed from group', { + publication: 'allGroups', + + testHandler: (onComplete) => { + expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 1, onComplete) + + Meteor.call('addAuthorToGroup', 'stephen', 'Editors', (error) => { + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 2, onComplete) + + Meteor.call('removeAuthorFromGroup', 'stephen', 'Editors', (error) => { + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 1, onComplete) + + // Meteor.call('removeAuthorFromGroup', 'john', 'Editors', (error) => { + // expectValueToBeUndefined(error, onComplete) + // expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 0, onComplete) + // + // onComplete() + // }) + onComplete() + }) + }) + } + }) + testPublication('Should publish all posts', { publication: 'allPosts', testHandler: (onComplete) => { - // const posts = Posts.find() - // asyncExpect(() => expect(posts.count()).to.equal(4), onComplete) - - expect(Posts.countDocuments()).to.eventually.equal(4) + expectCursorCountToEqual(Posts.find(), 4, onComplete) onComplete() } @@ -71,10 +153,7 @@ describe('publishComposite', () => { publication: 'allPostsAsync', testHandler: (onComplete) => { - // const posts = Posts.find() - // asyncExpect(() => expect(posts.count()).to.equal(4), onComplete) - - expect(Posts.countDocuments()).to.eventually.equal(4) + expectCursorCountToEqual(Posts.find(), 4, onComplete) onComplete() } @@ -84,11 +163,12 @@ describe('publishComposite', () => { publication: 'allPosts', testHandler: (onComplete) => { - const posts = Posts.find() + const posts = Posts.find().fetch() + tryExpect(() => expect(posts.length).to.be.greaterThan(0), onComplete) posts.forEach((post) => { const postAuthor = Authors.findOne({ username: post.author }) - asyncExpect(() => expect(postAuthor).to.not.be.undefined, onComplete) + expectValueToBeDefined(postAuthor, onComplete) }) onComplete() @@ -99,10 +179,7 @@ describe('publishComposite', () => { publication: 'allPosts', testHandler: (onComplete) => { - // const comments = Comments.find() - // asyncExpect(() => expect(comments.count()).to.equal(5), onComplete) - - expect(Comments.countDocuments()).to.eventually.equal(5) + expectCursorCountToEqual(Comments.find(), 5, onComplete) onComplete() } @@ -112,11 +189,12 @@ describe('publishComposite', () => { publication: 'allPosts', testHandler: (onComplete) => { - const comments = Comments.find() + const comments = Comments.find().fetch() + tryExpect(() => expect(comments.length).to.be.greaterThan(0), onComplete) comments.forEach((comment) => { const commentAuthor = Authors.findOne({ username: comment.author }) - asyncExpect(() => expect(commentAuthor).to.not.be.undefined, onComplete) + expectValueToBeDefined(commentAuthor, onComplete) }) onComplete() @@ -127,11 +205,12 @@ describe('publishComposite', () => { publication: 'allPostsWithChildrenAsFunction', testHandler: (onComplete) => { - const comments = Comments.find() + const comments = Comments.find().fetch() + tryExpect(() => expect(comments.length).to.be.greaterThan(0), onComplete) comments.forEach((comment) => { const commentAuthor = Authors.findOne({ username: comment.author }) - asyncExpect(() => expect(commentAuthor).to.not.be.undefined, onComplete) + expectValueToBeDefined(commentAuthor, onComplete) }) onComplete() @@ -144,10 +223,10 @@ describe('publishComposite', () => { testHandler: (onComplete) => { const allSubscribedPosts = Posts.find() - asyncExpect(() => expect(allSubscribedPosts.count()).to.equal(2), onComplete) + expectCursorCountToEqual(allSubscribedPosts, 2, onComplete) const postsByOtherAuthors = Posts.find({ author: { $ne: 'marie' } }) - asyncExpect(() => expect(postsByOtherAuthors.count()).to.equal(0), onComplete) + expectCursorCountToEqual(postsByOtherAuthors, 0, onComplete) onComplete() } @@ -158,16 +237,14 @@ describe('publishComposite', () => { args: ['marie'], testHandler: (onComplete) => { - const mariesSecondPost = Posts.findOne({ title: 'Marie\'s second post' }) - - asyncExpect(() => expect(Authors.find({ username: 'richard' }).count()).to.equal(1), onComplete) + expectCursorCountToEqual(Authors.find({ username: 'richard' }), 1, onComplete) + const mariesSecondPost = Posts.findOne({ title: 'Marie\'s second post' }) const richardsComment = Comments.findOne({ postId: mariesSecondPost._id, author: 'richard' }) Meteor.call('removeComment', richardsComment._id, (error) => { - asyncExpect(() => expect(error).to.be.undefined, onComplete) - - asyncExpect(() => expect(Authors.find({ username: 'richard' }).count()).to.equal(0), onComplete) + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Authors.find({ username: 'richard' }), 0, onComplete) onComplete() }) @@ -179,16 +256,14 @@ describe('publishComposite', () => { args: ['marie'], testHandler: (onComplete) => { - const mariesSecondPost = Posts.findOne({ title: 'Marie\'s second post' }) - - asyncExpect(() => expect(Authors.find({ username: 'marie' }).count()).to.equal(1), onComplete) + expectCursorCountToEqual(Authors.find({ username: 'marie' }), 1, onComplete) + const mariesSecondPost = Posts.findOne({ title: 'Marie\'s second post' }) const mariesComment = Comments.findOne({ postId: mariesSecondPost._id, author: 'marie' }) Meteor.call('removeComment', mariesComment._id, (error) => { - asyncExpect(() => expect(error).to.be.undefined, onComplete) - - asyncExpect(() => expect(Authors.find({ username: 'marie' }).count()).to.equal(1), onComplete) + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Authors.find({ username: 'marie' }), 1, onComplete) onComplete() }) @@ -201,15 +276,13 @@ describe('publishComposite', () => { testHandler: (onComplete) => { const post = Posts.findOne({ title: 'Post with no comments' }) - - asyncExpect(() => expect(post).to.not.be.undefined, onComplete) - asyncExpect(() => expect(Authors.find({ username: 'stephen' }).count()).to.equal(1), onComplete) + expectValueToBeDefined(post, onComplete) + expectCursorCountToEqual(Authors.find({ username: 'stephen' }), 1, onComplete) Meteor.call('updatePostAuthor', post._id, 'marie', (error) => { - asyncExpect(() => expect(error).to.be.undefined, onComplete) - - asyncExpect(() => expect(Posts.find().count()).to.equal(0), onComplete) - asyncExpect(() => expect(Authors.find().count()).to.equal(0), onComplete) + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Posts.find(), 0, onComplete) + expectCursorCountToEqual(Authors.find(), 0, onComplete) onComplete() }) @@ -221,17 +294,16 @@ describe('publishComposite', () => { args: ['albert'], testHandler: (onComplete) => { + expectCursorCountToEqual(Authors.find({ username: 'richard' }), 1, onComplete) + expectCursorCountToEqual(Authors.find({ username: 'john' }), 0, onComplete) + const albertsPost = Posts.findOne({ title: 'Post with one comment' }) const comment = Comments.findOne({ postId: albertsPost._id, author: 'richard' }) - asyncExpect(() => expect(Authors.find({ username: 'richard' }).count()).to.equal(1), onComplete) - asyncExpect(() => expect(Authors.find({ username: 'john' }).count()).to.equal(0), onComplete) - Meteor.call('updateCommentAuthor', comment._id, 'john', (error) => { - asyncExpect(() => expect(error).to.be.undefined, onComplete) - - asyncExpect(() => expect(Authors.find({ username: 'richard' }).count()).to.equal(0), onComplete) - asyncExpect(() => expect(Authors.find({ username: 'john' }).count()).to.equal(1), onComplete) + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Authors.find({ username: 'richard' }), 0, onComplete) + expectCursorCountToEqual(Authors.find({ username: 'john' }), 1, onComplete) onComplete() }) @@ -245,19 +317,15 @@ describe('publishComposite', () => { testHandler: (onComplete) => { const mariesFirstPost = Posts.findOne({ title: 'Marie\'s first post' }) - asyncExpect(() => expect(mariesFirstPost).to.not.be.undefined, onComplete) - const oldCommentCount = Comments.find({ postId: mariesFirstPost._id, author: 'albert' }).count() - asyncExpect(() => expect(oldCommentCount).to.equal(1), onComplete) - asyncExpect(() => expect(Authors.find({ username: 'albert' }).count()).to.equal(1), onComplete) + expectValueToBeDefined(mariesFirstPost, onComplete) + expectCursorCountToEqual(Comments.find({ postId: mariesFirstPost._id, author: 'albert' }), 1, onComplete) + expectCursorCountToEqual(Authors.find({ username: 'albert' }), 1, onComplete) Meteor.call('removePost', mariesFirstPost._id, (error) => { - asyncExpect(() => expect(error).to.be.undefined, onComplete) - - const newPostCount = Posts.find({ title: 'Marie\'s first post' }).count() - asyncExpect(() => expect(newPostCount).to.equal(0), onComplete) - const newCommentCount = Comments.find({ postId: mariesFirstPost._id, author: 'albert' }).count() - asyncExpect(() => expect(newCommentCount).to.equal(0), onComplete) - asyncExpect(() => expect(Authors.find({ username: 'albert' }).count()).to.equal(0), onComplete) + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Posts.find({ title: 'Marie\'s first post' }), 0, onComplete) + expectCursorCountToEqual(Comments.find({ postId: mariesFirstPost._id, author: 'albert' }), 0, onComplete) + expectCursorCountToEqual(Authors.find({ username: 'albert' }), 0, onComplete) onComplete() }) @@ -268,8 +336,8 @@ describe('publishComposite', () => { publication: 'postsAsArticles', testHandler: (onComplete) => { - asyncExpect(() => expect(Posts.find().count()).to.equal(0), onComplete) - asyncExpect(() => expect(Articles.find().count()).to.equal(4), onComplete) + expectCursorCountToEqual(Posts.find(), 0, onComplete) + expectCursorCountToEqual(Articles.find(), 4, onComplete) onComplete() } @@ -280,15 +348,13 @@ describe('publishComposite', () => { testHandler: (onComplete) => { const mariesFirstPost = Posts.findOne({ title: 'Marie\'s first post' }) - const oldComments = Comments.find({ postId: mariesFirstPost._id }) - - asyncExpect(() => expect(oldComments.count()).to.equal(0), onComplete) + expectValueToBeDefined(mariesFirstPost, onComplete) + expectCursorCountToEqual(Comments.find({ postId: mariesFirstPost._id }), 0, onComplete) Meteor.call('updatePostAuthor', mariesFirstPost._id, 'albert', (error) => { - asyncExpect(() => expect(error).to.be.undefined, onComplete) - - const newComments = Comments.find({ postId: mariesFirstPost._id }) - asyncExpect(() => expect(newComments.count()).to.be.greaterThan(0), onComplete) + expectValueToBeUndefined(error, onComplete) + const newComments = Comments.find({ postId: mariesFirstPost._id }).fetch() + tryExpect(() => expect(newComments.length).to.be.greaterThan(0), onComplete) onComplete() }) @@ -300,15 +366,13 @@ describe('publishComposite', () => { testHandler: (onComplete) => { const albertsPost = Posts.findOne({ author: 'albert' }) - const oldComments = Comments.find({ postId: albertsPost._id }) - - asyncExpect(() => expect(oldComments.count()).to.be.greaterThan(0), onComplete) + expectValueToBeDefined(albertsPost, onComplete) + const oldComments = Comments.find({ postId: albertsPost._id }).fetch() + tryExpect(() => expect(oldComments.length).to.be.greaterThan(0), onComplete) Meteor.call('updatePostAuthor', albertsPost._id, 'marie', (error) => { - asyncExpect(() => expect(error).to.be.undefined, onComplete) - - const newComments = Comments.find({ postId: albertsPost._id }) - asyncExpect(() => expect(newComments.count()).to.equal(0), onComplete) + expectValueToBeUndefined(error, onComplete) + expectCursorCountToEqual(Comments.find({ postId: albertsPost._id }), 0, onComplete) onComplete() }) @@ -320,15 +384,15 @@ describe('publishComposite', () => { testHandler: (onComplete) => { const albertsPost = Posts.findOne({ author: 'albert' }) + expectValueToBeDefined(albertsPost, onComplete) const oldComment = Comments.findOne({ postId: albertsPost._id }) - - asyncExpect(() => expect(oldComment.text).to.not.be.undefined, onComplete) + expectValueToBeDefined(oldComment.text, onComplete) Meteor.call('unsetCommentText', oldComment._id, (error) => { - asyncExpect(() => expect(error).to.be.undefined, onComplete) + expectValueToBeUndefined(error, onComplete) const newComment = Comments.findOne({ postId: albertsPost._id }) - asyncExpect(() => expect(newComment.text).to.be.undefined, onComplete) + expectValueToBeUndefined(newComment.text, onComplete) onComplete() }) @@ -342,8 +406,8 @@ describe('publishComposite', () => { const albertAsAuthor = Authors.findOne({ username: 'albert' }) const albertAsCommentAuthor = CommentAuthors.findOne({ username: 'albert' }) - asyncExpect(() => expect(albertAsAuthor).to.not.be.undefined, onComplete) - asyncExpect(() => expect(albertAsCommentAuthor).to.not.be.undefined, onComplete) + expectValueToBeDefined(albertAsAuthor, onComplete) + expectValueToBeDefined(albertAsCommentAuthor, onComplete) onComplete() } @@ -356,8 +420,8 @@ describe('publishComposite', () => { const marieAsAuthor = Authors.findOne({ username: 'marie' }) const stephenAsCommentAuthor = CommentAuthors.findOne({ username: 'stephen' }) - asyncExpect(() => expect(marieAsAuthor).to.not.be.undefined, onComplete) - asyncExpect(() => expect(stephenAsCommentAuthor).to.be.undefined, onComplete) + expectValueToBeDefined(marieAsAuthor, onComplete) + expectValueToBeUndefined(stephenAsCommentAuthor, onComplete) onComplete() } @@ -371,8 +435,8 @@ describe('publishComposite', () => { const mariesPost = Posts.findOne({ author: 'marie' }) const albertsPost = Posts.findOne({ author: 'albert' }) - asyncExpect(() => expect(mariesPost).to.not.be.undefined, onComplete) - asyncExpect(() => expect(albertsPost).to.not.be.undefined, onComplete) + expectValueToBeDefined(mariesPost, onComplete) + expectValueToBeDefined(albertsPost, onComplete) onComplete() } @@ -385,8 +449,8 @@ describe('publishComposite', () => { const marie = Authors.findOne({ username: 'marie' }) const albert = Authors.findOne({ username: 'albert' }) - asyncExpect(() => expect(marie).to.not.be.undefined, onComplete) - asyncExpect(() => expect(albert).to.not.be.undefined, onComplete) + expectValueToBeDefined(marie, onComplete) + expectValueToBeDefined(albert, onComplete) onComplete() } @@ -396,7 +460,7 @@ describe('publishComposite', () => { publication: 'returnNothing', testHandler: (onComplete, subscription) => { - asyncExpect(() => expect(subscription.ready()).to.be.true, onComplete) + tryExpect(() => expect(subscription.ready()).to.be.true, onComplete) onComplete() } diff --git a/tests/common.js b/tests/common.js index 274a09e..22514ae 100644 --- a/tests/common.js +++ b/tests/common.js @@ -6,11 +6,13 @@ import { Mongo } from 'meteor/mongo' * Define collections used in tests */ const Posts = new Mongo.Collection('posts') +const Groups = new Mongo.Collection('groups') const Authors = new Mongo.Collection('authors') const Comments = new Mongo.Collection('comments') export { Posts, + Groups, Authors, Comments } diff --git a/tests/server.js b/tests/server.js index 5bf6d3a..2e98ebd 100644 --- a/tests/server.js +++ b/tests/server.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor' import { Mongo } from 'meteor/mongo' import { enableDebugLogging, publishComposite } from 'meteor/reywood:publish-composite' -import { Authors, Comments, Posts } from './common' +import { Authors, Groups, Comments, Posts } from './common' import { debugLog } from '../lib/logging' enableDebugLogging() @@ -10,6 +10,19 @@ enableDebugLogging() /** * Set up publications for testing */ +publishComposite('allGroups', { + find () { + return Groups.find() + }, + children: [ + { + find (group) { + return Authors.find({ groupIds: group._id }) + } + } + ] +}) + const postPublicationChildren = [ { find (post) { @@ -178,7 +191,7 @@ publishComposite('returnNothing', () => undefined) // tests fail. The problem is that they are nonetheless still // flaky. We must replace sleep with something more reliable and // predictable in the frontend, using Tracker or observeChanges -const sleep = async function (ms) { +const sleep = async function (ms = 50) { return new Promise(resolve => setTimeout(resolve, ms)) } @@ -188,7 +201,8 @@ const sleep = async function (ms) { Meteor.methods({ async initTestData () { await removeAllData() - await sleep(100) + await sleep() + await initGroups() await initUsers() await initPosts() }, @@ -202,14 +216,35 @@ async function removeAllData () { await Comments.removeAsync({}) await Posts.removeAsync({}) await Authors.removeAsync({}) + await Groups.removeAsync({}) +} + +async function initGroups () { + await insertGroup('Writers') + await insertGroup('Editors') +} + +async function insertGroup (groupId) { + await Groups.insertAsync({ + _id: groupId, + }) } async function initUsers () { - await Authors.insertAsync({ _id: new Mongo.ObjectID(), username: 'marie' }) - await Authors.insertAsync({ _id: new Mongo.ObjectID(), username: 'albert' }) - await Authors.insertAsync({ _id: new Mongo.ObjectID(), username: 'richard' }) - await Authors.insertAsync({ _id: new Mongo.ObjectID(), username: 'stephen' }) - await Authors.insertAsync({ _id: new Mongo.ObjectID(), username: 'john' }) + await insertUser('marie') + await insertUser('albert') + await insertUser('richard') + await insertUser('stephen') + await insertUser('john', 'Editors') +} + +async function insertUser (username, groupId = 'Writers') { + const userId = new Mongo.ObjectID() + await Authors.insertAsync({ + _id: userId, + username, + groupIds: [groupId], + }) } async function initPosts () { @@ -277,6 +312,18 @@ Meteor.methods({ await sleep(100) }, + async addAuthorToGroup (username, groupId) { + console.log(`calling addAuthorToGroup, username: ${username}, groupId: ${groupId}`) + await Authors.updateAsync({ username }, { $push: { groupIds: groupId } }) + await sleep(100) + }, + + async removeAuthorFromGroup (username, groupId) { + console.log(`calling addAuthorToGroup, username: ${username}, groupId: ${groupId}`) + await Authors.updateAsync({ username }, { $pull: { groupIds: groupId } }) + await sleep(100) + }, + async updatePostAuthor (postId, newAuthor) { console.log(`calling updatePostAuthor, postId: ${postId}, newAuthor: ${newAuthor}`) await Posts.updateAsync({ _id: postId }, { $set: { author: newAuthor } }) @@ -292,6 +339,6 @@ Meteor.methods({ async unsetCommentText (commentId) { console.log(`calling unsetCommentText, commentId: ${commentId}`) await Comments.updateAsync({ _id: commentId }, { $unset: { text: '' } }) - await sleep(100) + await sleep() } }) From 9ab4f6f54f810a77781282dfa7de42bc023bc0fc Mon Sep 17 00:00:00 2001 From: Manuel Timita Date: Sun, 3 Mar 2024 21:23:54 +0000 Subject: [PATCH 2/3] Improved tests --- tests/client.js | 42 ++++++++++++++++++++++++------------------ tests/server.js | 20 +++++++++++++------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/tests/client.js b/tests/client.js index f023110..19d7b3b 100644 --- a/tests/client.js +++ b/tests/client.js @@ -58,6 +58,13 @@ describe('publishComposite', () => { return tryExpect(() => expect(value).to.not.be.undefined, onComplete) } + const validateAuthorsGroups = (groupId, count, onComplete) => { + const group = Groups.findOne({ _id: groupId }) + expectValueToBeDefined(group, onComplete) + tryExpect(() => expect(group.authors.length).to.equal(count), onComplete) + expectCursorCountToEqual(Authors.find({ groupIds: groupId }), count, onComplete) + } + /** * Define tests */ @@ -75,8 +82,8 @@ describe('publishComposite', () => { publication: 'allGroups', testHandler: (onComplete) => { - expectCursorCountToEqual(Authors.find({ groupIds: 'Writers' }), 4, onComplete) - expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 1, onComplete) + validateAuthorsGroups('Writers', 4, onComplete) + validateAuthorsGroups('Editors', 1, onComplete) onComplete() } @@ -86,12 +93,12 @@ describe('publishComposite', () => { publication: 'allGroups', testHandler: (onComplete) => { - expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 1, onComplete) + validateAuthorsGroups('Editors', 1, onComplete) Meteor.call('addAuthorToGroup', 'stephen', 'Editors', (error) => { expectValueToBeUndefined(error, onComplete) - expectCursorCountToEqual(Authors.find({ groupIds: 'Writers' }), 4, onComplete) - expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 2, onComplete) + validateAuthorsGroups('Writers', 4, onComplete) + validateAuthorsGroups('Editors', 2, onComplete) onComplete() }) @@ -102,11 +109,11 @@ describe('publishComposite', () => { publication: 'allGroups', testHandler: (onComplete) => { - expectCursorCountToEqual(Authors.find({ groupIds: 'Writers' }), 4, onComplete) + validateAuthorsGroups('Writers', 4, onComplete) Meteor.call('removeAuthorFromGroup', 'richard', 'Writers', (error) => { expectValueToBeUndefined(error, onComplete) - expectCursorCountToEqual(Authors.find({ groupIds: 'Writers' }), 3, onComplete) + validateAuthorsGroups('Writers', 3, onComplete) onComplete() }) @@ -117,23 +124,22 @@ describe('publishComposite', () => { publication: 'allGroups', testHandler: (onComplete) => { - expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 1, onComplete) + validateAuthorsGroups('Editors', 1, onComplete) Meteor.call('addAuthorToGroup', 'stephen', 'Editors', (error) => { expectValueToBeUndefined(error, onComplete) - expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 2, onComplete) + validateAuthorsGroups('Editors', 2, onComplete) Meteor.call('removeAuthorFromGroup', 'stephen', 'Editors', (error) => { expectValueToBeUndefined(error, onComplete) - expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 1, onComplete) - - // Meteor.call('removeAuthorFromGroup', 'john', 'Editors', (error) => { - // expectValueToBeUndefined(error, onComplete) - // expectCursorCountToEqual(Authors.find({ groupIds: 'Editors' }), 0, onComplete) - // - // onComplete() - // }) - onComplete() + validateAuthorsGroups('Editors', 1, onComplete) + + Meteor.call('removeAuthorFromGroup', 'john', 'Editors', (error) => { + expectValueToBeUndefined(error, onComplete) + validateAuthorsGroups('Editors', 0, onComplete) + + onComplete() + }) }) }) } diff --git a/tests/server.js b/tests/server.js index 2e98ebd..264b2f1 100644 --- a/tests/server.js +++ b/tests/server.js @@ -191,7 +191,7 @@ publishComposite('returnNothing', () => undefined) // tests fail. The problem is that they are nonetheless still // flaky. We must replace sleep with something more reliable and // predictable in the frontend, using Tracker or observeChanges -const sleep = async function (ms = 50) { +const sleep = async function (ms = 100) { return new Promise(resolve => setTimeout(resolve, ms)) } @@ -227,6 +227,7 @@ async function initGroups () { async function insertGroup (groupId) { await Groups.insertAsync({ _id: groupId, + authors: [], }) } @@ -245,6 +246,9 @@ async function insertUser (username, groupId = 'Writers') { username, groupIds: [groupId], }) + if (groupId) { + await Groups.updateAsync({ _id: groupId }, { $push: { authors: username } }) + } } async function initPosts () { @@ -303,37 +307,39 @@ Meteor.methods({ async removePost (postId) { console.log('calling removePost') await Posts.removeAsync(postId) - await sleep(100) + await sleep() }, async removeComment (commentId) { console.log('calling removeComment') await Comments.removeAsync(commentId) - await sleep(100) + await sleep() }, async addAuthorToGroup (username, groupId) { console.log(`calling addAuthorToGroup, username: ${username}, groupId: ${groupId}`) await Authors.updateAsync({ username }, { $push: { groupIds: groupId } }) - await sleep(100) + await Groups.updateAsync({ _id: groupId }, { $push: { authors: username } }) + await sleep() }, async removeAuthorFromGroup (username, groupId) { console.log(`calling addAuthorToGroup, username: ${username}, groupId: ${groupId}`) await Authors.updateAsync({ username }, { $pull: { groupIds: groupId } }) - await sleep(100) + await Groups.updateAsync({ _id: groupId }, { $pull: { authors: username } }) + await sleep() }, async updatePostAuthor (postId, newAuthor) { console.log(`calling updatePostAuthor, postId: ${postId}, newAuthor: ${newAuthor}`) await Posts.updateAsync({ _id: postId }, { $set: { author: newAuthor } }) - await sleep(100) + await sleep() }, async updateCommentAuthor (commentId, newAuthor) { console.log(`calling updateCommentAuthor, commentId: ${commentId}, newAuthor: ${newAuthor}`) await Comments.updateAsync({ _id: commentId }, { $set: { author: newAuthor } }) - await sleep(100) + await sleep() }, async unsetCommentText (commentId) { From 4e814604554eaeabef72d1311404d9687df26029 Mon Sep 17 00:00:00 2001 From: Manuel Timita Date: Sun, 3 Mar 2024 21:38:18 +0000 Subject: [PATCH 3/3] Fixed linting issues --- tests/client.js | 2 +- tests/server.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/client.js b/tests/client.js index 19d7b3b..56186fe 100644 --- a/tests/client.js +++ b/tests/client.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor' import { Mongo } from 'meteor/mongo' import { expect } from 'chai' -import {Authors, Comments, Groups, Posts} from './common' +import { Authors, Comments, Groups, Posts } from './common' const Articles = new Mongo.Collection('articles') const CommentAuthors = new Mongo.Collection('commentAuthors') diff --git a/tests/server.js b/tests/server.js index 264b2f1..02ad2d8 100644 --- a/tests/server.js +++ b/tests/server.js @@ -227,7 +227,7 @@ async function initGroups () { async function insertGroup (groupId) { await Groups.insertAsync({ _id: groupId, - authors: [], + authors: [] }) } @@ -244,7 +244,7 @@ async function insertUser (username, groupId = 'Writers') { await Authors.insertAsync({ _id: userId, username, - groupIds: [groupId], + groupIds: [groupId] }) if (groupId) { await Groups.updateAsync({ _id: groupId }, { $push: { authors: username } })