Git Product home page Git Product logo

parse-mockdb's Introduction

Parse MockDB

Master Build Status: Circle CI

Provides a mocked Parse RESTController compatible with version 2.0+ of the JavaScript SDK.

Installation and Usage

npm install parse-mockdb --save-dev
'use strict';
const Parse = require('parse-shim');
const ParseMockDB = require('parse-mockdb');

ParseMockDB.mockDB(Parse); // Mock the Parse RESTController

// Perform saves, queries, updates, deletes, etc... using the Parse JS SDK

ParseMockDB.cleanUp(); // Clear the Database
ParseMockDB.unMockDB(); // Un-mock the Parse RESTController

Completeness

  • Basic CRUD (save, destroy, fetch)
  • Query operators ($exists, $in, $nin, $eq, $ne, $lt, $lte, $gt, $gte, $regex, $select, $inQuery, $all, $nearSphere)
  • Update operators (Increment, Add, AddUnique, Remove, Delete)
  • Parse.Relation (AddRelation, RemoveRelation)
  • Parse query dotted notation matching eg { "name.first": "Tyler" })
  • Parse class level permissions
  • Parse.ACL (row level permissions)
  • Parse special classes (Parse.User, Parse.Role, ...)
  • Parse lifecycle hooks (beforeSave - done, afterSave - done, beforeDelete - done, afterDelete)

Changelog

v0.4.0

  • Breaking Change This library is now targeting the 2.x series of the Parse JS SDK. If you are using Parse 1.6+, you should pin to the v0.3.x release.

v0.3.0

  • Breaking Change When calling mockDB() you must now pass in a reference to the Parse SDK that you want to mock.

  • Breaking Change Stopped patching MockDB object on to Parse module. You can no longer access Parse.MockDB, you must load the parse-mockdb module explicitly.

  • Breaking Change Removed ParseMockDB.promiseResultSync method

Tests

npm test

parse-mockdb's People

Contributors

ashtong avatar codebreach avatar eddyionescu avatar higgins avatar ninachaubal avatar noahsilas avatar raufdean avatar roddylindsay avatar roost avatar tjtorola avatar tylerbrock avatar yutin1987 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

parse-mockdb's Issues

User is not supported on request objects

Currently, in the request object, the user object is a string, which causes weird errors about either bad atoms or unsaved objects when attempting to use it in a beforeSave hook. This makes it really difficult to test code that depends on the user on the request.

Push version 0.1.0 to npm

I blindly followed the README and ran tests to get:

  48) ParseMock when object has beforeSave hook registered rejects the save if there is a problem:
     Error: You need to call Parse.initialize before using Parse.
      at Context.<anonymous> (test.js:828:13)

Only then did I realise npm_modules contained 0.0.3 and not the latest 0.1.0.

Please publish latest version

It appears the latest version published to NPM is 0.2.1. I need to use the changes from the latest package release but since it's unpublished, it is unavailable.

Upgrade to lodash 4.0

Just tried upgrading — but tests fail because the following methods were removed

  • _.all
  • _.any

... and after that, 5 test are broken.

Cloud jobs

I didn't really find a way to call cloud jobs using this lib:

(node:9393) UnhandledPromiseRejectionWarning: TypeError: Parse.Cloud.job is not a function

This is my temporary solution by just adding this file mock.CloudJob.js somewhere:

module.exports = function(Parse){
    Parse.Cloud = {
        jobs:{},
        define: function(){},
        job: function(k,f){
            Parse.Cloud.jobs[k] = f
        },
        startJob: function(k,v){
            return Parse.Cloud.jobs[k](v)
        }
    }
}

Now in your code you can do require('mock.CloudJob')(Parse) and register & start Cloud-jobs as usual.

NOTE: I know this hack doesn't really follow the narrative of this lib, therefore I just figured to post this here (instead of doing a PR which doesn't really fit this lib). It might be useful to someone, or could start a brainstorm for Cloud-jobs in parse-mockdb.

Get fatuhoku pr's merged...

@fatuhoku has three outstanding pr's from December. The only thing outstanding is to squash, which hasn't happened in six months and is preventing the mainline from being as useful as it could be. I think that there are a few options:

  1. use the github squash on merge options. I haven't used, but i think it'll just do the right thing here
  2. i could do the squash and try to maintin @fatuhoku as the author, and then open up three new prs
  3. close the prs to indicate that they are not going to get merged
  4. merge them as is. i for one would think the value to the package would outweigh the downside of adding more commits to the history....

I am going to mege his pr's into my master and change my npm to pull my master. if i then make any changes (like adding a mock response to the before save) they wont be able to be merged in to this repo
:(.

Support for Parse.File.save()

This is a combo question / feature request.

I just want to confirm that Parse.File.save() doesn't work on marse-mockdb.

When I do:

    const parseFile = new Parse.File('test.json', { base64: "{}".toString('base64') });
    await parseFile.save();

I get no error, but when I try to save the object the file is set on, I get "Tried to encode an unsaved file."

Is there a workaround for mocking parse files? And if not, has anyone looked into what it'll take to mock it. I can look into it, but want to be sure no one else is doing it (or has decided that it's not possible / worth the effort).

Relation objects have no className

When retrieving a relation objects via relation.query, the resulting output have the className of the parent object, even though they have been correctly created, which causes issues downstream.

Parse.Save not working when working with extended classes

I am seeing an issue where saving a custom class that extends Parse.Object results in the
You need to call Parse.initialize before using Parse. error message when the _CoreManager tries to get the APPLICATION_ID.

class CoolClass extends Parse.Object {

    constructor(attributes) {

        // Pass the ClassName to the Parse.Object constructor
        super('CoolClass');

        // All other initialization
        this.coolness = 'awesome';
    }
}


let coolClass = new CoolClass();

coolClass.save() <<<<<<<<<<<<<<<<< Problem occurs here

The following code however works as expected.

let coolClass = new Parse.Object("CoolClass");
coolClass.save();

Am I missing something or custom classes not supported?

Support Parse.User (and subclasses)

Parse.User doesn't seem well supported right now because if you perform a save operation, Parse.User returns an undefined className. This doesn't play well with the way that Parse mockDB resolves its paths. The underlying request path looks like users rather than classes/ClassName/1.

Furthermore, you're allowed to subclass Parse.User with the Parse JS SDK:https://www.parse.com/questions/extending-the-default-parse-user. Subclasses are also not supported.

The mocked afterSave hook doesn't provide the request.original object

Problem

The afterSave hook in Parse MockDB behaves differently to a real Parse Server (v4.4.0 at time of writing).

In a real server, the afterSave hook will provide the original object from before the save as request.original, as well as the saved object in request.object as expected. In other words, it should provide the same objects on the request object as you would see in beforeSave.

Thus, any cloud code which relies on using this information to perform certain actions when particular data has changed isn't able to be unit tested via Parse MockDB.

Testing

The cloud code just logs out the request.original and request.object in the beforeSave hook and the afterSave hook.

The test script just creates an object and then updates it.

Run the test Parse Server via Docker as below:

$ docker run --rm --name test-mongo -d mongo && \
> docker run --rm --name test-parse-server -p 1337:1337 -e VERBOSE=0 \
>            -v $(pwd):/parse-server/cloud --link test-mongo parseplatform/parse-server \
>                --appId test-app --masterKey "M@stErK3y" \
>                --databaseURI "mongodb://test-mongo/test" --cloud /parse-server/cloud/main.js
...
[1] parse-server running on http://localhost:1337/parse
...

After running the test script in a separate terminal:

$ node test_context.js 
Created { foo: 'bar',
  createdAt: '2020-11-09T07:42:11.956Z',
  updatedAt: '2020-11-09T07:42:11.956Z',
  objectId: 'zwZdxCML9F' }
Updated { foo: 'baaaah',
  createdAt: '2020-11-09T07:42:11.956Z',
  updatedAt: '2020-11-09T07:42:12.043Z',
  objectId: 'zwZdxCML9F' }

You will see this in the server logs (split into two chunks: (1) create then (2) update):

Create

BEFORE: undefined -> { foo: 'bar' }
info: beforeSave triggered for Thing for user undefined:
  Input: {"foo":"bar"}
  Result: {"object":{"foo":"bar"}} {"className":"Thing","triggerType":"beforeSave"}
AFTER: undefined -> {
  foo: 'bar',
  createdAt: '2020-11-09T07:42:11.956Z',
  updatedAt: '2020-11-09T07:42:11.956Z',
  objectId: 'zwZdxCML9F'
}
info: afterSave triggered for Thing for user undefined:
  Input: {"foo":"bar","createdAt":"2020-11-09T07:42:11.956Z","updatedAt":"2020-11-09T07:42:11.956Z","objectId":"zwZdxCML9F"} {"className":"Thing","triggerType":"afterSave"}
info: afterSave triggered for Thing for user undefined:
  Input: {"foo":"bar","createdAt":"2020-11-09T07:42:11.956Z","updatedAt":"2020-11-09T07:42:11.956Z","objectId":"zwZdxCML9F"}
  Result: undefined {"className":"Thing","triggerType":"afterSave"}

Update

BEFORE: {
  foo: 'bar',
  createdAt: '2020-11-09T07:42:11.956Z',
  updatedAt: '2020-11-09T07:42:11.956Z',
  objectId: 'zwZdxCML9F'
} -> {
  foo: 'baaaah',
  createdAt: '2020-11-09T07:42:11.956Z',
  updatedAt: '2020-11-09T07:42:11.956Z',
  objectId: 'zwZdxCML9F'
}
info: beforeSave triggered for Thing for user undefined:
  Input: {"foo":"baaaah","createdAt":"2020-11-09T07:42:11.956Z","updatedAt":"2020-11-09T07:42:11.956Z","objectId":"zwZdxCML9F"}
  Result: {"object":{"foo":"baaaah"}} {"className":"Thing","triggerType":"beforeSave"}
AFTER: {
  foo: 'bar',
  createdAt: '2020-11-09T07:42:11.956Z',
  updatedAt: '2020-11-09T07:42:11.956Z',
  objectId: 'zwZdxCML9F'
} -> {
  foo: 'baaaah',
  createdAt: '2020-11-09T07:42:11.956Z',
  updatedAt: '2020-11-09T07:42:12.043Z',
  objectId: 'zwZdxCML9F'
}
info: afterSave triggered for Thing for user undefined:
  Input: {"foo":"baaaah","createdAt":"2020-11-09T07:42:11.956Z","updatedAt":"2020-11-09T07:42:12.043Z","objectId":"zwZdxCML9F"} {"className":"Thing","triggerType":"afterSave"}
info: afterSave triggered for Thing for user undefined:
  Input: {"foo":"baaaah","createdAt":"2020-11-09T07:42:11.956Z","updatedAt":"2020-11-09T07:42:12.043Z","objectId":"zwZdxCML9F"}
  Result: undefined {"className":"Thing","triggerType":"afterSave"}

As you can see in the final part of the logs above, the original object and the saved object are sent through to afterSave.

MockDB inappropriately accepts equalTo for array columns

Given a model MyModel with a column tags containing an array of pointers to a Tag model:

Parse Server:

  var q = new Parse.Query(MyModel);
  q.equalTo('tags', [tag])
  q.find().fail(err => console.log(err))

results in this error:

  ParseError {
    code: 107,
    message: 'You cannot use [object Object] as a query parameter.' }

MockDB finds and returns the matching objects.

"XMLHttpRequest failed" in jest but not mocha

I realize this may not be directly related to parse-mockdb, but I've spent a day on this and narrowed it way down and was wondering if you could give me any insight. It's as if jest is interfering with parse-mockdb's mocking of the REST controller. The jest tests were working until today. I have tried reverting to earlier versions of my repo where they were working, but I still get the errors. (I removed node_modules and reinstalled each time I reverted.)

I've written two very simple tests files, one using mocha and the other jest. There are no special configs for mocha or jest. Both use parse/node. Strangely, if I remove the Parse.initialize call from the mocha test, it still works, but in the jest test I get the usual complaint from Parse.

This is the mocha version, which I simply run with "mocha "

'use strict'

import Parse from 'parse/node'
import ParseMockDB from 'parse-mockdb'

Parse.initialize('test')

describe('parse-mockdb testing', () => {
  
  it('should save correctly', () => {
    ParseMockDB.mockDB(); // Mock the Parse RESTController
    let
      TheObj = Parse.Object.extend('TheParseObj'),
      obj = new TheObj()
    obj.save({message:'hello'})
      .then(obj => {
        assert.equal(obj.get('message'), 'hello');
        ParseMockDB.cleanUp(); // Clear the Database
        ParseMockDB.unMockDB(); // Un-mock the Parse RESTController
      })
  }
  )
})

The output for the mocha test:

  parse-mockdb testing
    ✓ should save correctly

  1 passing (21ms)

This is the jest version, which I simply run with "jest "

'use strict'

import Parse from 'parse/node'
import ParseMockDB from 'parse-mockdb'

Parse.initialize('test')

describe('parse-mockdb testing', () => {
  
  test('should save correctly', () => {
    expect.assertions(1)
    ParseMockDB.mockDB(); // Mock the Parse RESTController
    let
      TheObj = Parse.Object.extend('TheParseObj'),
      obj = new TheObj()
    return obj.save({message:'hello'})
      .then(obj => {
        expect(obj.get('message')).toBeDefined()
        ParseMockDB.cleanUp(); // Clear the Database
        ParseMockDB.unMockDB(); // Un-mock the Parse RESTController
      })
  })
})

The output for the jest test (after a 5-second wait):

  parse-mockdb testing
    ✕ should save correctly (4593ms)

  ● parse-mockdb testing › should save correctly

    Error
      Failed: XMLHttpRequest failed: "Unable to connect to the Parse API"

  ● parse-mockdb testing › should save correctly

    expect.assertions(1)

    Expected one assertion to be called but received zero assertion calls.

      at extractExpectedAssertionsErrors (node_modules/expect/build/extract_expected_assertions_errors.js:46:19)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        5.476s

I debugged both files in Webstorm, adding breakpoints in the MockRESTController and mockDB functions in node_modules/parse-mockdb/src/parse-mockdb.js (see screenshot below). When I ran the debugger, it stopped at both breakpoints in the mocha version, but only at the mockDb breakpoint in the jest version.

screenshot at oct 20 21-07-28

How do I access to beforeSave's request and response objects?

It looks like ParseMockDB.registerHook expects a promise, even though regular Cloud Code beforeX handlers are of type function(request,response). The contract should be identical to Cloud Code's for fidelity. callbacks are a pain!

EDIT: Okay, instead of function(request,response), how about we replace the response part with promise syntax, but leave the request as a parameter: i.e. function(request).

Check the Parse.Object docs. Most *Request types are the same as each other and the *Response types are the same as each other too:

Parse.Cloud.AfterDeleteRequest
Parse.Cloud.AfterSaveRequest
Parse.Cloud.BeforeDeleteRequest
Parse.Cloud.BeforeDeleteResponse
Parse.Cloud.BeforeSaveRequest
Parse.Cloud.BeforeSaveResponse

Note: some assumptions have to be made about the masterKey.

Any interest in linting?

I configured eslint to check for function () and also for missing ;.

If there's any interest, I can set it up using custom rules, or a standard set like google or airbnb and then conform and get all to pass.

I'd add a dev dependency to eslint, and i'd edit package.json to include a lint sript.

eslint can be configured in package.json.

I simply chose eslint over jshint because the first thing i googled sounded convincing, but have invested little in it, so glad to change to a preferred tool?

support Parse.Relation

when use

const qService = new Parse.Query('Service');
qService
  .get(id)
  .then((service) => {
    const qMember = service.relation('member').query();
    return qMember.find()
  });

Error Message

Type Error: Cannot read property 'object' of undefined
  at node_modules/parse-mockdb/src/parse-mockdb.js:501:64

console.log(qMember);

ParseQuery {
  classNmae: 'Member',
  where: {'$relatedTo': {object: [Object], key: 'member'}
  _include: [],
  _limit: -1,
  _skip: 0,
  _extraOptions: {}
}

parse-mockdb is a nice repository 💯

Support for default ACL assignment

When an object is 'saved' for the first time, its ACL should be set to Public Read/Write.
This is necessary to test that the beforeSave sets ACL's as appropriate.

Inconsistent isNew behaviour when calling beforeSave and afterSave

Hello,

I'm running into a problem when I register both beforeSave and afterSave hooks; my afterSave hook relies on the class to be saved. This isn't a problem when I test the hooks separately but I need to be able to run both hooks in other tests. I've got a simple reproduction that might help.

const Parse = require('parse-shim');
const ParseMockDB = require('parse-mockdb');

class Target extends Parse.Object {
  constructor(attributes, options) {
    super('Target', attributes, options);
  }
}

Parse.Object.registerSubclass('Target', Target);

ParseMockDB.mockDB();

ParseMockDB.registerHook('Target', 'afterSave', (request) => {
  console.warn(`calling afterSave, isNew() => ${request.object.isNew()}`);
  return Parse.Promise.as(request.object);
});

ParseMockDB.registerHook('Target', 'beforeSave', (request) => {
  console.warn(`calling beforeSave, isNew() => ${request.object.isNew()}`);
  return Parse.Promise.as(request.object);
});

const target = new Target({
  title: 'Hello, world',
});

target.save()
  .then(() => {
    console.warn(`after hooks, isNew() => ${target.isNew()}`)
  })
  .then(() => {
    ParseMockDB.cleanUp();
    ParseMockDB.unMockDB();
  });

When I run this code, I get this output:

calling beforeSave, isNew() => true
calling afterSave, isNew() => true
after hooks, isNew() => false

However, when I remove the beforeSave handler, I get:

calling afterSave, isNew() => false
after hooks, isNew() => false

The expected behaviour is that with the beforeSave handler, the log should be:

calling beforeSave, isNew() => true
calling afterSave, isNew() => false
after hooks, isNew() => false

Let me know if you need any further details or if I can be of assistance.

npm link oddness?

ugh. I'm trying to npm link my local parse-mockdb repo, but getting odd error, and I am curious if this is a known issue, or if I can get some help.

  1. I have verified that the hash installed by npm install parse-mockdb is the same as the HEAD of my local repo. And npm test in my project works!
  2. I create the link with npm link ../parse-mockdb
  3. now when i run npm test in my project, I get this error:
Failed: Error: You need to call Parse.initialize before using Parse.,Error: You need to call Parse.initialize before using Parse.
  Stack:
    Error: Failed: Error: You need to call Parse.initialize before using Parse.,Error: You need to call Parse.initialize before using Parse.
        at /Users/arthur/code/Epic-Cloud/spec/feedItemSpec.js:73:35
        at Array.wrappedRejectedCallback (/Users/arthur/code/Epic-Cloud/node_modules/parse/lib/node/ParsePromise.js:160:25)
        at ParsePromise.reject (/Users/arthur/code/Epic-Cloud/node_modules/parse/lib/node/ParsePromise.js:95:35)
        at Array.wrappedRejectedCallback (/Users/arthur/code/Epic-Cloud/node_modules/parse/lib/node/ParsePromise.js:181:19)

Any ideas?

Unit test eating assertion fails....

Hi @TylerBrock,

i'm continuing to investigate #27, and i have a pr coming up real soon, but wanted to get your feedback on a question.

As part of digging into my most recent change and why it broke my tests, I was wondering why the tests didn't fail on parse-mockdb. I then added a test that should have failed, but it didn't, ok. That's when I noticed that the test wasn't using done(). Ok. Fixed that, but then I see that when an assertion in a promise fails, it doesn't get caught by the test runner.

I created a branch to show what I mean. acinader@292fa00

In any event, it seems to me that Parse.Promise and mocha don't play well together. Have you noticed what I am talking about?

I'm game to give it some thought, but before I waste our time, I figured I'd ask if this is in any way on your radar screen?

Trigger afterSave hook

It's really useful if a test is able to perform verifications on the state of the database after the 'afterSave' hook completes. Usually I use afterSave to trigger updates on other classes' fields and it's important for that behaviour to be verified.

Fetched nested objects aren't fetched after save

When saving an object with nested objects that are already fetched, exactly those nested objects should still be fetched after the save. Check out the following unit test that asserts this:

it('should keep fetched nested objects fetched after the save', function() {
  return new Item().save({
    price: 45
  }).then(function(item0) {
    return new Item().save({
      price: 50
    }).then(function(item2) {
      return new Item({
        price: 55
      }).save().then(function(item3) {
        const brand = new Brand();
        const item1 = new Item();
        item1.id = item0.id; // create pointer to item0
        brand.set("items", [item1, item2, item3]);
        return brand.save();
      });
    });
  }).then(function(brand) {
    assert.equal(brand.get("items")[0].get("price"), undefined);
    assert.equal(brand.get("items")[1].get("price"), 50);
    assert.equal(brand.get("items")[2].get("price"), 55);

    brand.get("items")[0].set("price", 30);
    brand.set("name", "foo");
    return brand.save();
  }).then(function(sbrand) {
    assert.equal(sbrand.get("items")[0].get("price"), undefined);
    assert.equal(sbrand.get("items")[1].get("price"), 50);
    assert.equal(sbrand.get("items")[2].get("price"), 30);
  });
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.