├── .babelrc ├── .editorconfig ├── .github ├── contributing.md ├── issue_template.md ├── pull_request_template.md └── stale.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs.md ├── package.json ├── src ├── common-tests.js ├── example-tests.js ├── index.js └── orm-tests.js └── test └── index.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ "add-module-exports" ], 3 | "presets": [ "es2015" ] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Feathers 2 | 3 | Thank you for contributing to Feathers! :heart: :tada: 4 | 5 | This repo is the main core and where most issues are reported. Feathers embraces modularity and is broken up across many repos. To make this easier to manage we currently use [Zenhub](https://www.zenhub.com/) for issue triage and visibility. They have a free browser plugin you can install so that you can see what is in flight at any time, but of course you also always see current issues in Github. 6 | 7 | ## Report a bug 8 | 9 | Before creating an issue please make sure you have checked out the docs, specifically the [FAQ](https://docs.feathersjs.com/help/faq.html) section. You might want to also try searching Github. It's pretty likely someone has already asked a similar question. 10 | 11 | If you haven't found your answer please feel free to join our [slack channel](http://slack.feathersjs.com), create an issue on Github, or post on [Stackoverflow](http://stackoverflow.com) using the `feathers` or `feathersjs` tag. We try our best to monitor Stackoverflow but you're likely to get more immediate responses in Slack and Github. 12 | 13 | Issues can be reported in the [issue tracker](https://github.com/feathersjs/feathers/issues). Since feathers combines many modules it can be hard for us to assess the root cause without knowing which modules are being used and what your configuration looks like, so **it helps us immensely if you can link to a simple example that reproduces your issue**. 14 | 15 | ## Report a Security Concern 16 | 17 | We take security very seriously at Feathers. We welcome any peer review of our 100% open source code to ensure nobody's Feathers app is ever compromised or hacked. As a web application developer you are responsible for any security breaches. We do our very best to make sure Feathers is as secure as possible by default. 18 | 19 | In order to give the community time to respond and upgrade we strongly urge you report all security issues to us. Send one of the core team members a PM in [Slack](http://slack.feathersjs.com) or email us at hello@feathersjs.com with details and we will respond ASAP. 20 | 21 | For full details refer to our [Security docs](https://docs.feathersjs.com/SECURITY.html). 22 | 23 | ## Pull Requests 24 | 25 | We :heart: pull requests and we're continually working to make it as easy as possible for people to contribute, including a [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) and a [common test suite](https://github.com/feathersjs/feathers-service-tests) for database adapters. 26 | 27 | We prefer small pull requests with minimal code changes. The smaller they are the easier they are to review and merge. A core team member will pick up your PR and review it as soon as they can. They may ask for changes or reject your pull request. This is not a reflection of you as an engineer or a person. Please accept feedback graciously as we will also try to be sensitive when providing it. 28 | 29 | Although we generally accept many PRs they can be rejected for many reasons. We will be as transparent as possible but it may simply be that you do not have the same context or information regarding the roadmap that the core team members have. We value the time you take to put together any contributions so we pledge to always be respectful of that time and will try to be as open as possible so that you don't waste it. :smile: 30 | 31 | **All PRs (except documentation) should be accompanied with tests and pass the linting rules.** 32 | 33 | ### Code style 34 | 35 | Before running the tests from the `test/` folder `npm test` will run ESlint. You can check your code changes individually by running `npm run lint`. 36 | 37 | ### ES6 compilation 38 | 39 | Feathers uses [Babel](https://babeljs.io/) to leverage the latest developments of the JavaScript language. All code and samples are currently written in ES2015. To transpile the code in this repository run 40 | 41 | > npm run compile 42 | 43 | __Note:__ `npm test` will run the compilation automatically before the tests. 44 | 45 | ### Tests 46 | 47 | [Mocha](http://mochajs.org/) tests are located in the `test/` folder and can be run using the `npm run mocha` or `npm test` (with ESLint and code coverage) command. 48 | 49 | ### Documentation 50 | 51 | Feathers documentation is contained in Markdown files in the [feathers-docs](https://github.com/feathersjs/feathers-docs) repository. To change the documentation submit a pull request to that repo, referencing any other PR if applicable, and the docs will be updated with the next release. 52 | 53 | ## External Modules 54 | 55 | If you're written something awesome for Feathers, the Feathers ecosystem, or using Feathers please add it to the [showcase](https://docs.feathersjs.com/why/showcase.html). You also might want to check out the [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) that can be used to scaffold plugins to be Feathers compliant from the start. 56 | 57 | If you think it would be a good core module then please contact one of the Feathers core team members in [Slack](http://slack.feathersjs.com) and we can discuss whether it belongs in core and how to get it there. :beers: 58 | 59 | ## Contributor Code of Conduct 60 | 61 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 62 | 63 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 64 | 65 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 66 | 67 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 68 | 69 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 72 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### Steps to reproduce 2 | 3 | (First please check that this issue is not already solved as [described 4 | here](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#report-a-bug)) 5 | 6 | - [ ] Tell us what broke. The more detailed the better. 7 | - [ ] If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc. 8 | 9 | ### Expected behavior 10 | Tell us what should happen 11 | 12 | ### Actual behavior 13 | Tell us what happens instead 14 | 15 | ### System configuration 16 | 17 | Tell us about the applicable parts of your setup. 18 | 19 | **Module versions** (especially the part that's not working): 20 | 21 | **NodeJS version**: 22 | 23 | **Operating System**: 24 | 25 | **Browser Version**: 26 | 27 | **React Native Version**: 28 | 29 | **Module Loader**: -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | 3 | (If you have not already please refer to the contributing guideline as [described 4 | here](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#pull-requests)) 5 | 6 | - [ ] Tell us about the problem your pull request is solving. 7 | - [ ] Are there any open issues that are related to this? 8 | - [ ] Is this PR dependent on PRs in other repos? 9 | 10 | If so, please mention them to keep the conversations linked together. 11 | 12 | ### Other Information 13 | 14 | If there's anything else that's important and relevant to your pull 15 | request, mention that information here. This could include 16 | benchmarks, or other information. 17 | 18 | Your PR will be reviewed by a core team member and they will work with you to get your changes merged in a timely manner. If merged your PR will automatically be added to the changelog in the next release. 19 | 20 | If your changes involve documentation updates please mention that and link the appropriate PR in [feathers-docs](https://github.com/feathersjs/feathers-docs). 21 | 22 | Thanks for contributing to Feathers! :heart: -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 84 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - greenkeeper 8 | - bug 9 | - security 10 | - enhancement 11 | # Label to use when marking an issue as stale 12 | staleLabel: wontfix 13 | # Comment to post when marking an issue as stale. Set to `false` to disable 14 | markComment: > 15 | This issue has been automatically marked as stale because it has not had 16 | recent activity. It will be closed if no further activity occurs. 17 | Apologies if the issue could not be resolved. FeathersJS ecosystem 18 | modules are community maintained so there may be a chance that there isn't anybody 19 | available to address the issue at the moment. 20 | For other ways to get help [see here](https://docs.feathersjs.com/help/readme.html). 21 | # Comment to post when closing a stale issue. Set to `false` to disable 22 | closeComment: false 23 | # Only close stale issues 24 | only: issues 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | 30 | lib/ 31 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | src/ 3 | test/ 4 | !lib/ 5 | .github/ 6 | coverage 7 | .editorconfig 8 | .travis.yml 9 | .istanbul.yml 10 | .babelrc -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | - '6' 5 | - '4' 6 | sudo: false 7 | notifications: 8 | email: false 9 | slack: 10 | rooms: 11 | secure: eYfXNTDBSKOAM7HH+ew4DYRFBdtDTrHlr4FDk4WsmB9bWdBLkiO8NroygSMtEUQnfa7Suth+NdDRNuiyGbOOIxU3pV2Jzr4FXgGDc+6+QWklUGLSkpbEv3Qw6EvvKvv62TXKvVBIoq4NY5MCRGj918aCy396o00FKHhGeeANqbL7JFGhQbcNGki2EIUcqj9poGL8DrcCgzzfYu1DbupjMvV87j8Dy6CFJLBWjpS099S8oEjUkN2Pw3Fjg5taU5NbYPOu0/EO2EF1HGnXOCn6FQGnJ5RI3DoGjnXskLuNSUfgPsq3n11ivvklbruteZZDkEkYxKcHO0b7tia8oNIMFPQ8w5HCVPujfmxuzD2Jb5V84+luCMdad3iSKPuX23wBcVPrlnICwf12tfPZCLq2Vy3qv5evgmVrrHdOJ55Qr5QvkOKYKx8Y60YsAUKH3iDcKUvJp5g/Ien4rbhw3cEsIEneHpJJzXRJLrrU7TbTspHy6hONA9fVXTfRhm38uxqGl4wP3u8uJ6MzP4HjNFZCPN76j9oMYyiix5uqHAyKsJoqicnj1TScp7eD4YRn1tpa0g/PRAXlLoWiXLLi/gR2lNh2kpo1MIQJApLrBUregh5KDjRNAk8lpQItx6pk6NW/NR+MspS879vt3xMeD0nEku43QWc/9WIT58nn6WaMPy0= 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v0.10.2](https://github.com/feathersjs/feathers-service-tests/tree/v0.10.2) (2017-05-03) 4 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.10.1...v0.10.2) 5 | 6 | **Merged pull requests:** 7 | 8 | - Update ORM tests to cover revove\(\) method [\#40](https://github.com/feathersjs/feathers-service-tests/pull/40) ([DesignByOnyx](https://github.com/DesignByOnyx)) 9 | 10 | ## [v0.10.1](https://github.com/feathersjs/feathers-service-tests/tree/v0.10.1) (2017-05-02) 11 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.10.0...v0.10.1) 12 | 13 | ## [v0.10.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.10.0) (2017-05-02) 14 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.9.3...v0.10.0) 15 | 16 | **Closed issues:** 17 | 18 | - Update ORM tests to cover common requirements [\#38](https://github.com/feathersjs/feathers-service-tests/issues/38) 19 | 20 | **Merged pull requests:** 21 | 22 | - Update ORM tests to ensure plain objects are returned [\#39](https://github.com/feathersjs/feathers-service-tests/pull/39) ([DesignByOnyx](https://github.com/DesignByOnyx)) 23 | - Update semistandard to the latest version 🚀 [\#37](https://github.com/feathersjs/feathers-service-tests/pull/37) ([greenkeeper[bot]](https://github.com/integration/greenkeeper)) 24 | - Update dependencies to enable Greenkeeper 🌴 [\#36](https://github.com/feathersjs/feathers-service-tests/pull/36) ([greenkeeper[bot]](https://github.com/integration/greenkeeper)) 25 | 26 | ## [v0.9.3](https://github.com/feathersjs/feathers-service-tests/tree/v0.9.3) (2017-01-07) 27 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.9.2...v0.9.3) 28 | 29 | **Merged pull requests:** 30 | 31 | - Do not rely on order of items returned without sorting. [\#35](https://github.com/feathersjs/feathers-service-tests/pull/35) ([jciolek](https://github.com/jciolek)) 32 | 33 | ## [v0.9.2](https://github.com/feathersjs/feathers-service-tests/tree/v0.9.2) (2017-01-04) 34 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.9.1...v0.9.2) 35 | 36 | **Closed issues:** 37 | 38 | - Add test for $search [\#7](https://github.com/feathersjs/feathers-service-tests/issues/7) 39 | 40 | **Merged pull requests:** 41 | 42 | - Add tests for limit 0 with pagination [\#34](https://github.com/feathersjs/feathers-service-tests/pull/34) ([daffl](https://github.com/daffl)) 43 | 44 | ## [v0.9.1](https://github.com/feathersjs/feathers-service-tests/tree/v0.9.1) (2016-11-15) 45 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.9.0...v0.9.1) 46 | 47 | **Merged pull requests:** 48 | 49 | - Do not pass an id to create [\#33](https://github.com/feathersjs/feathers-service-tests/pull/33) ([daffl](https://github.com/daffl)) 50 | 51 | ## [v0.9.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.9.0) (2016-11-11) 52 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.8.2...v0.9.0) 53 | 54 | **Closed issues:** 55 | 56 | - Add test for using $select with get [\#25](https://github.com/feathersjs/feathers-service-tests/issues/25) 57 | 58 | **Merged pull requests:** 59 | 60 | - Support $select in get and make sure patch many only returns changed items [\#32](https://github.com/feathersjs/feathers-service-tests/pull/32) ([daffl](https://github.com/daffl)) 61 | - 👻😱 Node.js 0.10 is unmaintained 😱👻 [\#31](https://github.com/feathersjs/feathers-service-tests/pull/31) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 62 | 63 | ## [v0.8.2](https://github.com/feathersjs/feathers-service-tests/tree/v0.8.2) (2016-10-25) 64 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.8.1...v0.8.2) 65 | 66 | **Merged pull requests:** 67 | 68 | - Install request as a dependency [\#28](https://github.com/feathersjs/feathers-service-tests/pull/28) ([daffl](https://github.com/daffl)) 69 | - jshint —\> semistandard [\#26](https://github.com/feathersjs/feathers-service-tests/pull/26) ([corymsmith](https://github.com/corymsmith)) 70 | 71 | ## [v0.8.1](https://github.com/feathersjs/feathers-service-tests/tree/v0.8.1) (2016-09-28) 72 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.8.0...v0.8.1) 73 | 74 | **Closed issues:** 75 | 76 | - Add test for `$sort` direction coming in as a string. [\#21](https://github.com/feathersjs/feathers-service-tests/issues/21) 77 | 78 | **Merged pull requests:** 79 | 80 | - Add test for sorting with strings [\#22](https://github.com/feathersjs/feathers-service-tests/pull/22) ([daffl](https://github.com/daffl)) 81 | 82 | ## [v0.8.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.8.0) (2016-09-21) 83 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.7.1...v0.8.0) 84 | 85 | **Merged pull requests:** 86 | 87 | - Add tests for proper multi patch [\#20](https://github.com/feathersjs/feathers-service-tests/pull/20) ([daffl](https://github.com/daffl)) 88 | 89 | ## [v0.7.1](https://github.com/feathersjs/feathers-service-tests/tree/v0.7.1) (2016-09-20) 90 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.7.0...v0.7.1) 91 | 92 | **Merged pull requests:** 93 | 94 | - Verify id property for multiple updates [\#19](https://github.com/feathersjs/feathers-service-tests/pull/19) ([daffl](https://github.com/daffl)) 95 | 96 | ## [v0.7.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.7.0) (2016-09-07) 97 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.6.2...v0.7.0) 98 | 99 | **Merged pull requests:** 100 | 101 | - Improved shared service tests [\#18](https://github.com/feathersjs/feathers-service-tests/pull/18) ([daffl](https://github.com/daffl)) 102 | - Upgrade to Mocha 3.x [\#17](https://github.com/feathersjs/feathers-service-tests/pull/17) ([daffl](https://github.com/daffl)) 103 | - Update mocha to version 3.0.0 🚀 [\#16](https://github.com/feathersjs/feathers-service-tests/pull/16) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 104 | - Update request-promise to version 4.0.0 🚀 [\#15](https://github.com/feathersjs/feathers-service-tests/pull/15) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 105 | 106 | ## [v0.6.2](https://github.com/feathersjs/feathers-service-tests/tree/v0.6.2) (2016-07-08) 107 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.6.1...v0.6.2) 108 | 109 | **Merged pull requests:** 110 | 111 | - Also make sure that update and create do not modify their data [\#14](https://github.com/feathersjs/feathers-service-tests/pull/14) ([daffl](https://github.com/daffl)) 112 | 113 | ## [v0.6.1](https://github.com/feathersjs/feathers-service-tests/tree/v0.6.1) (2016-07-08) 114 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.6.0...v0.6.1) 115 | 116 | **Merged pull requests:** 117 | 118 | - Make sure that id properties are not changed [\#13](https://github.com/feathersjs/feathers-service-tests/pull/13) ([daffl](https://github.com/daffl)) 119 | 120 | ## [v0.6.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.6.0) (2016-06-16) 121 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.5.8...v0.6.0) 122 | 123 | **Closed issues:** 124 | 125 | - NPM version different from current compiled version. [\#4](https://github.com/feathersjs/feathers-service-tests/issues/4) 126 | 127 | **Merged pull requests:** 128 | 129 | - Tests to allow to override paginate in params [\#12](https://github.com/feathersjs/feathers-service-tests/pull/12) ([daffl](https://github.com/daffl)) 130 | - Ensure services use internal methods [\#11](https://github.com/feathersjs/feathers-service-tests/pull/11) ([t2t2](https://github.com/t2t2)) 131 | - mocha@2.5.0 breaks build 🚨 [\#10](https://github.com/feathersjs/feathers-service-tests/pull/10) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 132 | - Update babel-plugin-add-module-exports to version 0.2.0 🚀 [\#9](https://github.com/feathersjs/feathers-service-tests/pull/9) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 133 | - Update request-promise to version 3.0.0 🚀 [\#6](https://github.com/feathersjs/feathers-service-tests/pull/6) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 134 | - Update all dependencies 🌴 [\#5](https://github.com/feathersjs/feathers-service-tests/pull/5) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 135 | 136 | ## [v0.5.8](https://github.com/feathersjs/feathers-service-tests/tree/v0.5.8) (2016-04-01) 137 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.5.7...v0.5.8) 138 | 139 | ## [v0.5.7](https://github.com/feathersjs/feathers-service-tests/tree/v0.5.7) (2016-04-01) 140 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.5.6...v0.5.7) 141 | 142 | ## [v0.5.6](https://github.com/feathersjs/feathers-service-tests/tree/v0.5.6) (2016-04-01) 143 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.5.5...v0.5.6) 144 | 145 | ## [v0.5.5](https://github.com/feathersjs/feathers-service-tests/tree/v0.5.5) (2016-04-01) 146 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.5.4...v0.5.5) 147 | 148 | ## [v0.5.4](https://github.com/feathersjs/feathers-service-tests/tree/v0.5.4) (2016-02-22) 149 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.5.3...v0.5.4) 150 | 151 | **Merged pull requests:** 152 | 153 | - updating request promise [\#3](https://github.com/feathersjs/feathers-service-tests/pull/3) ([ekryski](https://github.com/ekryski)) 154 | 155 | ## [v0.5.3](https://github.com/feathersjs/feathers-service-tests/tree/v0.5.3) (2015-12-30) 156 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.5.2...v0.5.3) 157 | 158 | ## [v0.5.2](https://github.com/feathersjs/feathers-service-tests/tree/v0.5.2) (2015-12-19) 159 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.5.1...v0.5.2) 160 | 161 | ## [v0.5.1](https://github.com/feathersjs/feathers-service-tests/tree/v0.5.1) (2015-12-09) 162 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.5.0...v0.5.1) 163 | 164 | ## [v0.5.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.5.0) (2015-12-02) 165 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.4.0...v0.5.0) 166 | 167 | ## [v0.4.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.4.0) (2015-12-01) 168 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.3.0...v0.4.0) 169 | 170 | **Closed issues:** 171 | 172 | - Supported databases [\#1](https://github.com/feathersjs/feathers-service-tests/issues/1) 173 | 174 | **Merged pull requests:** 175 | 176 | - Promises, patch/remove/create many and pagination tests [\#2](https://github.com/feathersjs/feathers-service-tests/pull/2) ([daffl](https://github.com/daffl)) 177 | 178 | ## [v0.3.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.3.0) (2015-11-07) 179 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.2.1...v0.3.0) 180 | 181 | ## [v0.2.1](https://github.com/feathersjs/feathers-service-tests/tree/v0.2.1) (2015-11-04) 182 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.2.0...v0.2.1) 183 | 184 | ## [v0.2.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.2.0) (2015-11-04) 185 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.1.1...v0.2.0) 186 | 187 | ## [v0.1.1](https://github.com/feathersjs/feathers-service-tests/tree/v0.1.1) (2015-11-03) 188 | [Full Changelog](https://github.com/feathersjs/feathers-service-tests/compare/v0.1.0...v0.1.1) 189 | 190 | ## [v0.1.0](https://github.com/feathersjs/feathers-service-tests/tree/v0.1.0) (2015-10-28) 191 | 192 | 193 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Feathers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # feathers-service-tests 2 | 3 | > __Important:__ The code for this module has been moved into the main Feathers repository at [feathersjs/feathers](https://github.com/feathersjs/feathers) ([package direct link](https://github.com/feathersjs/feathers/tree/master/packages/adapter-commons)). Please open issues and pull requests there. 4 | 5 | [![Build Status](https://travis-ci.org/feathersjs/feathers-service-tests.png?branch=master)](https://travis-ci.org/feathersjs/feathers-service-tests) 6 | [![Download Status](https://img.shields.io/npm/dm/feathers-service-tests.svg?style=flat-square)](https://www.npmjs.com/package/feathers-service-tests) 7 | 8 | 9 | > A standardized testing harness for Feathers services 10 | 11 | ## About 12 | 13 | feathers-service-tests is a test harness for different database service adapters to verify a common querying interface. Currently it is used by: 14 | 15 | - [feathers-mongodb](https://github.com/feathersjs/feathers-mongodb) 16 | - [feathers-mongoose](https://github.com/feathersjs/feathers-mongoose) 17 | - [feathers-knex](https://github.com/feathersjs/feathers-knex) 18 | - [feathers-sequelize](https://github.com/feathersjs/feathers-sequelize) 19 | - mysql 20 | - postgres 21 | - mariadb 22 | - sql server 23 | - sqlite 24 | - [feathers-levelup](https://github.com/feathersjs/feathers-levelup) 25 | - [feathers-waterline](https://github.com/feathersjs/feathers-waterline) 26 | - [feathers-nedb](https://github.com/feathersjs/feathers-nedb) 27 | - [feathers-memory](https://github.com/feathersjs/feathers-memory) 28 | - [feathers-localstorage](https://github.com/feathersjs/feathers-localstorage) 29 | - [feathers-rethinkdb](https://github.com/feathersjs/feathers-rethinkdb) 30 | - [feathers-elasticsearch](https://github.com/feathersjs/feathers-elasticsearch) 31 | - [feathers-objection](https://github.com/mcchrish/feathers-objection) 32 | - [feathers-service-tests-cassandra](https://github.com/dekelev/feathers-service-tests-cassandra) (fork) 33 | - [feathers-cassandra](https://github.com/dekelev/feathers-cassandra) 34 | 35 | ## License 36 | 37 | Copyright (c) 2016 38 | 39 | Licensed under the [MIT license](LICENSE). 40 | -------------------------------------------------------------------------------- /docs.md: -------------------------------------------------------------------------------- 1 | ## Pagination 2 | 3 | When initializing the service you can set the following pagination options in the `paginate` object: 4 | 5 | - `default` - Sets the default number of items 6 | - `max` - Sets the maximum allowed number of items per page (even if the `$limit` query parameter is set higher) 7 | 8 | When `paginate` is set, `find` will, instead of an Array, return an object in the follow form: 9 | 10 | ``` 11 | { 12 | "total": "", 13 | "limit": "", 14 | "count": "", 15 | "skip": "", 16 | "data": [/* data */] 17 | } 18 | ``` 19 | 20 | 21 | ## Query Parameters 22 | 23 | The `find` API allows the use of `$limit`, `$skip`, `$sort`, and `$select` in the query. These special parameters can be passed directly inside the query object: 24 | 25 | ```js 26 | // Find all recipes that include salt, limit to 10, only include name field. 27 | {"ingredients":"salt", "$limit":10, "$select": { "name" :1 } } // JSON 28 | 29 | GET /?ingredients=salt&$limit=10&$select[name]=1 // HTTP 30 | ``` 31 | 32 | As a result of allowing these to be put directly into the query string, you won't want to use `$limit`, `$skip`, `$sort`, or `$select` as the name of fields in your document schema. 33 | 34 | ### `$limit` 35 | 36 | `$limit` will return only the number of results you specify: 37 | 38 | ``` 39 | // Retrieves the first two records found where age is 37. 40 | query: { 41 | age: 37, 42 | $limit: 2 43 | } 44 | ``` 45 | 46 | ### `$skip` 47 | 48 | `$skip` will skip the specified number of results: 49 | 50 | ``` 51 | // Retrieves all except the first two records found where age is 37. 52 | query: { 53 | age: 37, 54 | $skip: 2 55 | } 56 | ``` 57 | 58 | ### `$sort` 59 | 60 | `$sort` will sort based on the object you provide: 61 | 62 | ``` 63 | // Retrieves all where age is 37, sorted ascending alphabetically by name. 64 | query: { 65 | age: 37, 66 | $sort: { name: 1 } 67 | } 68 | 69 | // Retrieves all where age is 37, sorted descending alphabetically by name. 70 | query: { 71 | age: 37, 72 | $sort: { name: -1} 73 | } 74 | ``` 75 | 76 | ### `$select` 77 | 78 | `$select` support in a query allows you to pick which fields to include or exclude in the results. Note: you can use the include syntax or the exclude syntax, not both together. See the section on [`Projections`](https://github.com/louischatriot/nedb#projections) in the NeDB docs. 79 | ``` 80 | // Only retrieve name. 81 | query: { 82 | name: 'Alice', 83 | $select: {'name': 1} 84 | } 85 | 86 | // Retrieve everything except age. 87 | query: { 88 | name: 'Alice', 89 | $select: {'age': 0} 90 | } 91 | ``` 92 | 93 | ## Filter criteria 94 | 95 | In addition to sorting and pagination, properties can also be filtered by criteria. Standard criteria can just be added to the query. For example, the following find all users with the name `Alice`: 96 | 97 | ```js 98 | query: { 99 | name: 'Alice' 100 | } 101 | ``` 102 | 103 | Additionally, the following advanced criteria are supported for each property. 104 | 105 | ### $in, $nin 106 | 107 | Find all records where the property does (`$in`) or does not (`$nin`) contain the given values. For example, the following query finds every user with the name of `Alice` or `Bob`: 108 | 109 | ```js 110 | query: { 111 | name: { 112 | $in: ['Alice', 'Bob'] 113 | } 114 | } 115 | ``` 116 | 117 | ### $lt, $lte 118 | 119 | Find all records where the value is less (`$lt`) or less and equal (`$lte`) to a given value. The following query retrieves all users 25 or younger: 120 | 121 | ```js 122 | query: { 123 | age: { 124 | $lte: 25 125 | } 126 | } 127 | ``` 128 | 129 | ### $gt, $gte 130 | 131 | Find all records where the value is more (`$gt`) or more and equal (`$gte`) to a given value. The following query retrieves all users older than 25: 132 | 133 | ```js 134 | query: { 135 | age: { 136 | $gt: 25 137 | } 138 | } 139 | ``` 140 | 141 | ### $ne 142 | 143 | Find all records that do not contain the given property value, for example anybody not age 25: 144 | 145 | ```js 146 | query: { 147 | age: { 148 | $ne: 25 149 | } 150 | } 151 | ``` 152 | 153 | ### $or 154 | 155 | Find all records that match any of the given objects. For example, find all users name Bob or Alice: 156 | 157 | ```js 158 | query: { 159 | $or: [ 160 | { name: 'Alice' }, 161 | { name: 'Bob' } 162 | ] 163 | } 164 | ``` 165 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "feathers-service-tests", 3 | "description": "A standardized testing harness for Feathers services", 4 | "version": "0.10.2", 5 | "homepage": "https://github.com/feathersjs/feathers-service-tests", 6 | "main": "lib/", 7 | "keywords": [ 8 | "feathers", 9 | "feathers-plugin" 10 | ], 11 | "licenses": [ 12 | { 13 | "type": "MIT", 14 | "url": "https://github.com/feathersjs/feathers-service-tests/blob/master/LICENSE" 15 | } 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/feathersjs/feathers-service-tests.git" 20 | }, 21 | "author": { 22 | "name": "Feathers contributors", 23 | "email": "hello@feathersjs.com", 24 | "url": "https://feathersjs.com" 25 | }, 26 | "contributors": [], 27 | "bugs": { 28 | "url": "https://github.com/feathersjs/feathers-service-tests/issues" 29 | }, 30 | "engines": { 31 | "node": ">= 4" 32 | }, 33 | "scripts": { 34 | "prepublish": "npm run compile", 35 | "changelog": "github_changelog_generator && git add CHANGELOG.md && git commit -am \"Updating changelog\"", 36 | "publish": "git push origin --tags && npm run changelog && git push origin", 37 | "release:patch": "npm version patch && npm publish", 38 | "release:minor": "npm version minor && npm publish", 39 | "release:major": "npm version major && npm publish", 40 | "compile": "rimraf lib/ && babel -d lib/ src/", 41 | "watch": "babel --watch -d lib/ src/", 42 | "lint": "eslint-if-supported semistandard --fix", 43 | "mocha": "mocha test/ --compilers js:babel-core/register", 44 | "test": "npm run lint && npm run mocha" 45 | }, 46 | "semistandard": { 47 | "env": [ 48 | "mocha" 49 | ], 50 | "ignore": [ 51 | "/lib" 52 | ] 53 | }, 54 | "directories": { 55 | "lib": "lib" 56 | }, 57 | "dependencies": { 58 | "chai": "^4.0.0", 59 | "request": "^2.85.0", 60 | "request-promise": "^4.0.0" 61 | }, 62 | "devDependencies": { 63 | "babel-cli": "^6.6.5", 64 | "babel-core": "^6.7.4", 65 | "babel-plugin-add-module-exports": "^0.2.0", 66 | "babel-preset-es2015": "^6.6.0", 67 | "eslint-if-supported": "^1.0.1", 68 | "mocha": "^5.0.0", 69 | "rimraf": "^2.5.4", 70 | "semistandard": "^12.0.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/common-tests.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-expressions */ 2 | 3 | import { expect } from 'chai'; 4 | 5 | function common (app, errors, serviceName = 'people', idProp = 'id') { 6 | describe(`Common tests, ${serviceName} service with` + 7 | ` '${idProp}' id property`, () => { 8 | const _ids = {}; 9 | 10 | beforeEach(() => 11 | app.service(serviceName).create({ 12 | name: 'Doug', 13 | age: 32 14 | }).then(data => (_ids.Doug = data[idProp])) 15 | ); 16 | 17 | afterEach(() => 18 | app.service(serviceName).remove(_ids.Doug).catch(() => {}) 19 | ); 20 | 21 | it('sets `id` property on the service', () => 22 | expect(app.service(serviceName).id).to.equal(idProp) 23 | ); 24 | 25 | it('sets `events` property from options', () => 26 | expect(app.service(serviceName).events.indexOf('testing')) 27 | .to.not.equal(-1) 28 | ); 29 | 30 | describe('extend', () => { 31 | it('extends and uses extended method', () => { 32 | let now = new Date().getTime(); 33 | let extended = app.service(serviceName).extend({ 34 | create (data) { 35 | data.time = now; 36 | return this._super.apply(this, arguments); 37 | } 38 | }); 39 | 40 | return extended.create({ name: 'Dave' }) 41 | .then(data => extended.remove(data[idProp])) 42 | .then(data => expect(data.time).to.equal(now)); 43 | }); 44 | }); 45 | 46 | describe('get', () => { 47 | it('returns an instance that exists', () => { 48 | return app.service(serviceName).get(_ids.Doug).then(data => { 49 | expect(data[idProp].toString()).to.equal(_ids.Doug.toString()); 50 | expect(data.name).to.equal('Doug'); 51 | expect(data.age).to.equal(32); 52 | }); 53 | }); 54 | 55 | it('supports $select', () => { 56 | return app.service(serviceName).get(_ids.Doug, { 57 | query: { $select: [ 'name' ] } 58 | }).then(data => { 59 | expect(data[idProp].toString()).to.equal(_ids.Doug.toString()); 60 | expect(data.name).to.equal('Doug'); 61 | expect(data.age).to.not.exist; 62 | }); 63 | }); 64 | 65 | it('returns NotFound error for non-existing id', () => { 66 | return app.service(serviceName).get('568225fbfe21222432e836ff') 67 | .catch(error => { 68 | expect(error instanceof errors.NotFound).to.be.ok; 69 | expect(error.message).to.equal('No record found for id \'568225fbfe21222432e836ff\''); 70 | }); 71 | }); 72 | }); 73 | 74 | describe('remove', () => { 75 | it('deletes an existing instance and returns the deleted instance', () => { 76 | return app.service(serviceName).remove(_ids.Doug).then(data => { 77 | expect(data).to.be.ok; 78 | expect(data.name).to.equal('Doug'); 79 | }); 80 | }); 81 | 82 | it('deletes an existing instance supports $select', () => { 83 | return app.service(serviceName).remove(_ids.Doug, { 84 | query: { $select: [ 'name' ] } 85 | }).then(data => { 86 | expect(data).to.be.ok; 87 | expect(data.name).to.equal('Doug'); 88 | expect(data.age).to.not.exist; 89 | }); 90 | }); 91 | 92 | it('deletes multiple instances', () => { 93 | return app.service(serviceName).create({ name: 'Dave', age: 29, created: true }) 94 | .then(() => app.service(serviceName).create({ 95 | name: 'David', 96 | age: 3, 97 | created: true 98 | })) 99 | .then(() => app.service(serviceName).remove(null, { 100 | query: { created: true } 101 | })) 102 | .then(data => { 103 | expect(data.length).to.equal(2); 104 | 105 | let names = data.map(person => person.name); 106 | expect(names.indexOf('Dave')).to.be.above(-1); 107 | expect(names.indexOf('David')).to.be.above(-1); 108 | }); 109 | }); 110 | }); 111 | 112 | describe('find', () => { 113 | beforeEach(() => { 114 | return app.service(serviceName).create({ 115 | name: 'Bob', 116 | age: 25 117 | }).then(bob => { 118 | _ids.Bob = bob[idProp].toString(); 119 | 120 | return app.service(serviceName).create({ 121 | name: 'Alice', 122 | age: 19 123 | }); 124 | }).then(alice => (_ids.Alice = alice[idProp].toString())); 125 | }); 126 | 127 | afterEach(() => 128 | app.service(serviceName) 129 | .remove(_ids.Bob) 130 | .then(() => app.service(serviceName) 131 | .remove(_ids.Alice)) 132 | ); 133 | 134 | it('returns all items', () => { 135 | return app.service(serviceName).find().then(data => { 136 | expect(data).to.be.instanceof(Array); 137 | expect(data.length).to.equal(3); 138 | }); 139 | }); 140 | 141 | it('filters results by a single parameter', () => { 142 | const params = { query: { name: 'Alice' } }; 143 | 144 | return app.service(serviceName).find(params).then(data => { 145 | expect(data).to.be.instanceof(Array); 146 | expect(data.length).to.equal(1); 147 | expect(data[0].name).to.equal('Alice'); 148 | }); 149 | }); 150 | 151 | it('filters results by multiple parameters', () => { 152 | const params = { query: { name: 'Alice', age: 19 } }; 153 | 154 | return app.service(serviceName).find(params).then(data => { 155 | expect(data).to.be.instanceof(Array); 156 | expect(data.length).to.equal(1); 157 | expect(data[0].name).to.equal('Alice'); 158 | }); 159 | }); 160 | 161 | describe('special filters', () => { 162 | it('can $sort', () => { 163 | const params = { 164 | query: { 165 | $sort: {name: 1} 166 | } 167 | }; 168 | 169 | return app.service(serviceName).find(params).then(data => { 170 | expect(data.length).to.equal(3); 171 | expect(data[0].name).to.equal('Alice'); 172 | expect(data[1].name).to.equal('Bob'); 173 | expect(data[2].name).to.equal('Doug'); 174 | }); 175 | }); 176 | 177 | it('can $sort with strings', () => { 178 | const params = { 179 | query: { 180 | $sort: { name: '1' } 181 | } 182 | }; 183 | 184 | return app.service(serviceName).find(params).then(data => { 185 | expect(data.length).to.equal(3); 186 | expect(data[0].name).to.equal('Alice'); 187 | expect(data[1].name).to.equal('Bob'); 188 | expect(data[2].name).to.equal('Doug'); 189 | }); 190 | }); 191 | 192 | it('can $limit', () => { 193 | const params = { 194 | query: { 195 | $limit: 2 196 | } 197 | }; 198 | 199 | return app.service(serviceName).find(params) 200 | .then(data => expect(data.length).to.equal(2)); 201 | }); 202 | 203 | it('can $limit 0', () => { 204 | const params = { 205 | query: { 206 | $limit: 0 207 | } 208 | }; 209 | 210 | return app.service(serviceName).find(params) 211 | .then(data => expect(data.length).to.equal(0)); 212 | }); 213 | 214 | it('can $skip', () => { 215 | const params = { 216 | query: { 217 | $sort: {name: 1}, 218 | $skip: 1 219 | } 220 | }; 221 | 222 | return app.service(serviceName).find(params).then(data => { 223 | expect(data.length).to.equal(2); 224 | expect(data[0].name).to.equal('Bob'); 225 | expect(data[1].name).to.equal('Doug'); 226 | }); 227 | }); 228 | 229 | it('can $select', () => { 230 | const params = { 231 | query: { 232 | name: 'Alice', 233 | $select: ['name'] 234 | } 235 | }; 236 | 237 | return app.service(serviceName).find(params).then(data => { 238 | expect(data.length).to.equal(1); 239 | expect(data[0].name).to.equal('Alice'); 240 | expect(data[0].age).to.be.undefined; 241 | }); 242 | }); 243 | 244 | it('can $or', () => { 245 | const params = { 246 | query: { 247 | $or: [ 248 | { name: 'Alice' }, 249 | { name: 'Bob' } 250 | ], 251 | $sort: {name: 1} 252 | } 253 | }; 254 | 255 | return app.service(serviceName).find(params).then(data => { 256 | expect(data).to.be.instanceof(Array); 257 | expect(data.length).to.equal(2); 258 | expect(data[0].name).to.equal('Alice'); 259 | expect(data[1].name).to.equal('Bob'); 260 | }); 261 | }); 262 | 263 | it.skip('can $not', () => { 264 | var params = { 265 | query: { 266 | age: { $not: 19 }, 267 | name: { $not: 'Doug' } 268 | } 269 | }; 270 | 271 | return app.service(serviceName).find(params).then(data => { 272 | expect(data).to.be.instanceof(Array); 273 | expect(data.length).to.equal(1); 274 | expect(data[0].name).to.equal('Bob'); 275 | }); 276 | }); 277 | 278 | it('can $in', () => { 279 | const params = { 280 | query: { 281 | name: { 282 | $in: ['Alice', 'Bob'] 283 | }, 284 | $sort: {name: 1} 285 | } 286 | }; 287 | 288 | return app.service(serviceName).find(params).then(data => { 289 | expect(data).to.be.instanceof(Array); 290 | expect(data.length).to.equal(2); 291 | expect(data[0].name).to.equal('Alice'); 292 | expect(data[1].name).to.equal('Bob'); 293 | }); 294 | }); 295 | 296 | it('can $nin', () => { 297 | const params = { 298 | query: { 299 | name: { 300 | $nin: ['Alice', 'Bob'] 301 | } 302 | } 303 | }; 304 | 305 | return app.service(serviceName).find(params).then(data => { 306 | expect(data).to.be.instanceof(Array); 307 | expect(data.length).to.equal(1); 308 | expect(data[0].name).to.equal('Doug'); 309 | }); 310 | }); 311 | 312 | it('can $lt', () => { 313 | const params = { 314 | query: { 315 | age: { 316 | $lt: 30 317 | } 318 | } 319 | }; 320 | 321 | return app.service(serviceName).find(params).then(data => { 322 | expect(data).to.be.instanceof(Array); 323 | expect(data.length).to.equal(2); 324 | }); 325 | }); 326 | 327 | it('can $lte', () => { 328 | const params = { 329 | query: { 330 | age: { 331 | $lte: 25 332 | } 333 | } 334 | }; 335 | 336 | return app.service(serviceName).find(params).then(data => { 337 | expect(data).to.be.instanceof(Array); 338 | expect(data.length).to.equal(2); 339 | }); 340 | }); 341 | 342 | it('can $gt', () => { 343 | const params = { 344 | query: { 345 | age: { 346 | $gt: 30 347 | } 348 | } 349 | }; 350 | 351 | return app.service(serviceName).find(params).then(data => { 352 | expect(data).to.be.instanceof(Array); 353 | expect(data.length).to.equal(1); 354 | }); 355 | }); 356 | 357 | it('can $gte', () => { 358 | const params = { 359 | query: { 360 | age: { 361 | $gte: 25 362 | } 363 | } 364 | }; 365 | 366 | return app.service(serviceName).find(params).then(data => { 367 | expect(data).to.be.instanceof(Array); 368 | expect(data.length).to.equal(2); 369 | }); 370 | }); 371 | 372 | it('can $ne', () => { 373 | const params = { 374 | query: { 375 | age: { 376 | $ne: 25 377 | } 378 | } 379 | }; 380 | 381 | return app.service(serviceName).find(params).then(data => { 382 | expect(data).to.be.instanceof(Array); 383 | expect(data.length).to.equal(2); 384 | }); 385 | }); 386 | }); 387 | 388 | it('can $gt and $lt and $sort', () => { 389 | const params = { 390 | query: { 391 | age: { 392 | $gt: 18, 393 | $lt: 30 394 | }, 395 | $sort: { name: 1 } 396 | } 397 | }; 398 | 399 | return app.service(serviceName).find(params).then(data => { 400 | expect(data.length).to.equal(2); 401 | expect(data[0].name).to.equal('Alice'); 402 | expect(data[1].name).to.equal('Bob'); 403 | }); 404 | }); 405 | 406 | it('can handle nested $or queries and $sort', () => { 407 | const params = { 408 | query: { 409 | $or: [ 410 | { name: 'Doug' }, 411 | { 412 | age: { 413 | $gte: 18, 414 | $lt: 25 415 | } 416 | } 417 | ], 418 | $sort: { name: 1 } 419 | } 420 | }; 421 | 422 | return app.service(serviceName).find(params).then(data => { 423 | expect(data.length).to.equal(2); 424 | expect(data[0].name).to.equal('Alice'); 425 | expect(data[1].name).to.equal('Doug'); 426 | }); 427 | }); 428 | 429 | describe('paginate', function () { 430 | beforeEach(() => 431 | (app.service(serviceName).paginate = { default: 1, max: 2 }) 432 | ); 433 | 434 | afterEach(() => (app.service(serviceName).paginate = {})); 435 | 436 | it('returns paginated object, paginates by default and shows total', () => { 437 | return app.service(serviceName) 438 | .find({ query: { $sort: { name: -1 } } }) 439 | .then(paginator => { 440 | expect(paginator.total).to.equal(3); 441 | expect(paginator.limit).to.equal(1); 442 | expect(paginator.skip).to.equal(0); 443 | expect(paginator.data[0].name).to.equal('Doug'); 444 | }); 445 | }); 446 | 447 | it('paginates max and skips', () => { 448 | const params = { 449 | query: { 450 | $skip: 1, 451 | $limit: 4, 452 | $sort: { name: -1 } 453 | } 454 | }; 455 | 456 | return app.service(serviceName).find(params).then(paginator => { 457 | expect(paginator.total).to.equal(3); 458 | expect(paginator.limit).to.equal(2); 459 | expect(paginator.skip).to.equal(1); 460 | expect(paginator.data[0].name).to.equal('Bob'); 461 | expect(paginator.data[1].name).to.equal('Alice'); 462 | }); 463 | }); 464 | 465 | it('$limit 0 with pagination', () => { 466 | return app.service(serviceName).find({ query: { $limit: 0 } }) 467 | .then(paginator => expect(paginator.data.length).to.equal(0)); 468 | }); 469 | 470 | it('allows to override paginate in params', () => { 471 | return app.service(serviceName) 472 | .find({ paginate: { default: 2 } }) 473 | .then(paginator => { 474 | expect(paginator.limit).to.equal(2); 475 | expect(paginator.skip).to.equal(0); 476 | 477 | return app.service(serviceName) 478 | .find({ paginate: false }) 479 | .then(results => expect(results.length).to.equal(3)); 480 | }); 481 | }); 482 | }); 483 | }); 484 | 485 | describe('update', () => { 486 | it('replaces an existing instance, does not modify original data', () => { 487 | const originalData = { [idProp]: _ids.Doug, name: 'Dougler' }; 488 | const originalCopy = Object.assign({}, originalData); 489 | 490 | return app.service(serviceName).update(_ids.Doug, originalData) 491 | .then(data => { 492 | expect(originalData).to.deep.equal(originalCopy); 493 | expect(data[idProp].toString()).to.equal(_ids.Doug.toString()); 494 | expect(data.name).to.equal('Dougler'); 495 | expect(!data.age).to.be.ok; 496 | }); 497 | }); 498 | 499 | it('replaces an existing instance, supports $select', () => { 500 | const originalData = { 501 | [idProp]: _ids.Doug, 502 | name: 'Dougler', 503 | age: 10 504 | }; 505 | 506 | return app.service(serviceName).update(_ids.Doug, originalData, { 507 | query: { $select: [ 'name' ] } 508 | }).then(data => { 509 | expect(data.name).to.equal('Dougler'); 510 | expect(data.age).to.not.exist; 511 | }); 512 | }); 513 | 514 | it('returns NotFound error for non-existing id', () => { 515 | return app.service(serviceName) 516 | .update('568225fbfe21222432e836ff', { name: 'NotFound' }) 517 | .catch(error => { 518 | expect(error).to.be.ok; 519 | expect(error instanceof errors.NotFound).to.be.ok; 520 | expect(error.message).to.equal('No record found for id \'568225fbfe21222432e836ff\''); 521 | }); 522 | }); 523 | }); 524 | 525 | describe('patch', () => { 526 | it('updates an existing instance, does not modify original data', () => { 527 | const originalData = { [idProp]: _ids.Doug, name: 'PatchDoug' }; 528 | const originalCopy = Object.assign({}, originalData); 529 | 530 | return app.service(serviceName).patch(_ids.Doug, originalData) 531 | .then(data => { 532 | expect(originalData).to.deep.equal(originalCopy); 533 | expect(data[idProp].toString()).to.equal(_ids.Doug.toString()); 534 | expect(data.name).to.equal('PatchDoug'); 535 | expect(data.age).to.equal(32); 536 | }); 537 | }); 538 | 539 | it('updates an existing instance, supports $select', () => { 540 | const originalData = { [idProp]: _ids.Doug, name: 'PatchDoug' }; 541 | 542 | return app.service(serviceName).patch(_ids.Doug, originalData, { 543 | query: { $select: [ 'name' ] } 544 | }).then(data => { 545 | expect(data.name).to.equal('PatchDoug'); 546 | expect(data.age).to.not.exist; 547 | }); 548 | }); 549 | 550 | it('patches multiple instances', () => { 551 | const service = app.service(serviceName); 552 | const params = { 553 | query: { created: true } 554 | }; 555 | 556 | return service.create({ 557 | name: 'Dave', 558 | age: 29, 559 | created: true 560 | }).then(() => 561 | service.create({ 562 | name: 'David', 563 | age: 3, 564 | created: true 565 | }) 566 | ).then(() => 567 | service.patch(null, { 568 | age: 2 569 | }, params 570 | )).then(data => { 571 | expect(data.length).to.equal(2); 572 | expect(data[0].age).to.equal(2); 573 | expect(data[1].age).to.equal(2); 574 | }).then(() => service.remove(null, params)); 575 | }); 576 | 577 | it('patches multiple instances and returns the actually changed items', () => { 578 | const service = app.service(serviceName); 579 | const params = { 580 | query: { age: { $lt: 10 } } 581 | }; 582 | 583 | return service.create({ 584 | name: 'Dave', 585 | age: 8, 586 | created: true 587 | }).then(() => 588 | service.create({ 589 | name: 'David', 590 | age: 4, 591 | created: true 592 | }) 593 | ).then(() => 594 | service.patch(null, { 595 | age: 2 596 | }, params 597 | )).then(data => { 598 | expect(data.length).to.equal(2); 599 | expect(data[0].age).to.equal(2); 600 | expect(data[1].age).to.equal(2); 601 | }).then(() => service.remove(null, params)); 602 | }); 603 | 604 | it('patches multiple, returns correct items', () => { 605 | const service = app.service(serviceName); 606 | 607 | return service.create([{ 608 | name: 'Dave', 609 | age: 2, 610 | created: true 611 | }, { 612 | name: 'David', 613 | age: 2, 614 | created: true 615 | }, { 616 | name: 'D', 617 | age: 8, 618 | created: true 619 | } 620 | ]).then(() => 621 | service.patch(null, { 622 | age: 8 623 | }, { query: { age: 2 } } 624 | )).then(data => { 625 | expect(data.length).to.equal(2); 626 | expect(data[0].age).to.equal(8); 627 | expect(data[1].age).to.equal(8); 628 | }).then(() => service.remove(null, { 629 | query: { age: 8 } 630 | })); 631 | }); 632 | 633 | it('returns NotFound error for non-existing id', () => { 634 | return app.service(serviceName) 635 | .patch('568225fbfe21222432e836ff', { name: 'PatchDoug' }) 636 | .then(() => { throw new Error('Should never get here'); }) 637 | .catch(error => { 638 | expect(error).to.be.ok; 639 | expect(error instanceof errors.NotFound).to.be.ok; 640 | expect(error.message).to.equal('No record found for id \'568225fbfe21222432e836ff\''); 641 | }); 642 | }); 643 | }); 644 | 645 | describe('create', () => { 646 | it('creates a single new instance and returns the created instance', () => { 647 | const originalData = { 648 | name: 'Bill', 649 | age: 40 650 | }; 651 | const originalCopy = Object.assign({}, originalData); 652 | 653 | return app.service(serviceName).create(originalData).then(data => { 654 | expect(originalData).to.deep.equal(originalCopy); 655 | expect(data).to.be.instanceof(Object); 656 | expect(data).to.not.be.empty; 657 | expect(data.name).to.equal('Bill'); 658 | }); 659 | }); 660 | 661 | it('creates a single new instance, supports $select', () => { 662 | const originalData = { 663 | name: 'William', 664 | age: 23 665 | }; 666 | 667 | return app.service(serviceName).create(originalData, { 668 | query: { $select: [ 'name' ] } 669 | }).then(data => { 670 | expect(data.name).to.equal('William'); 671 | expect(data.age).to.not.exist; 672 | }); 673 | }); 674 | 675 | it('creates multiple new instances', () => { 676 | const items = [ 677 | { 678 | name: 'Gerald', 679 | age: 18 680 | }, 681 | { 682 | name: 'Herald', 683 | age: 18 684 | } 685 | ]; 686 | 687 | return app.service(serviceName).create(items).then(data => { 688 | expect(data).to.not.be.empty; 689 | expect(Array.isArray(data)).to.equal(true); 690 | expect(typeof data[0][idProp]).to.not.equal('undefined'); 691 | expect(data[0].name).to.equal('Gerald'); 692 | expect(typeof data[1][idProp]).to.not.equal('undefined'); 693 | expect(data[1].name).to.equal('Herald'); 694 | }); 695 | }); 696 | }); 697 | 698 | describe('Services don\'t call public methods internally', () => { 699 | let throwing; 700 | 701 | before(() => { 702 | throwing = app.service(serviceName).extend({ 703 | get store () { 704 | return app.service(serviceName).store; 705 | }, 706 | 707 | find () { 708 | throw new Error('find method called'); 709 | }, 710 | get () { 711 | throw new Error('get method called'); 712 | }, 713 | create () { 714 | throw new Error('create method called'); 715 | }, 716 | update () { 717 | throw new Error('update method called'); 718 | }, 719 | patch () { 720 | throw new Error('patch method called'); 721 | }, 722 | remove () { 723 | throw new Error('remove method called'); 724 | } 725 | }); 726 | }); 727 | 728 | it('find', () => app.service(serviceName).find.call(throwing)); 729 | 730 | it('get', () => 731 | app.service(serviceName).get.call(throwing, _ids.Doug) 732 | ); 733 | 734 | it('create', () => app.service(serviceName) 735 | .create.call(throwing, { 736 | name: 'Bob', 737 | age: 25 738 | }) 739 | ); 740 | 741 | it('update', () => 742 | app.service(serviceName).update.call(throwing, _ids.Doug, { 743 | name: 'Dougler' 744 | }) 745 | ); 746 | 747 | it('patch', () => 748 | app.service(serviceName).patch.call(throwing, _ids.Doug, { 749 | name: 'PatchDoug' 750 | }) 751 | ); 752 | 753 | it('remove', () => 754 | app.service(serviceName).remove.call(throwing, _ids.Doug) 755 | ); 756 | }); 757 | }); 758 | } 759 | 760 | export default common; 761 | -------------------------------------------------------------------------------- /src/example-tests.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-expressions */ 2 | 3 | import { expect } from 'chai'; 4 | import request from 'request-promise'; 5 | 6 | export default function (idProp = 'id', url = 'http://localhost:3030/todos') { 7 | let firstId; 8 | 9 | it('POST', () => { 10 | let body = { text: 'first todo', complete: false }; 11 | 12 | return request.post({ url, json: true, body }) 13 | .then(todo => { 14 | let body = { text: 'second todo', complete: false }; 15 | 16 | firstId = todo[idProp]; 17 | expect(todo[idProp]).to.exist; 18 | expect(todo.text).to.equal('first todo'); 19 | 20 | return request.post({ url, json: true, body }); 21 | }) 22 | .then(todo => { 23 | let body = { text: 'third todo', complete: false }; 24 | 25 | expect(todo.text).to.equal('second todo'); 26 | 27 | return request.post({ url, json: true, body }); 28 | }) 29 | .then(todo => expect(todo.text).to.equal('third todo')); 30 | }); 31 | 32 | describe('GET /', () => { 33 | it('GET / with default pagination', () => { 34 | return request({ 35 | url, 36 | json: true, 37 | qs: { $sort: { text: 1 } } 38 | }).then(page => { 39 | expect(page.total).to.equal(3); 40 | expect(page.limit).to.equal(2); 41 | expect(page.skip).to.equal(0); 42 | expect(page.data.length).to.equal(2); 43 | expect(page.data[0].text).to.equal('first todo'); 44 | expect(page.data[1].text).to.equal('second todo'); 45 | }); 46 | }); 47 | 48 | it('GET / with skip', () => { 49 | return request({ 50 | url, 51 | json: true, 52 | qs: { $skip: 2, $sort: { text: 1 } } 53 | }).then(page => { 54 | expect(page.total).to.equal(3); 55 | expect(page.limit).to.equal(2); 56 | expect(page.skip).to.equal(2); 57 | expect(page.data.length).to.equal(1); 58 | expect(page.data[0].text).to.equal('third todo'); 59 | }); 60 | }); 61 | 62 | it('GET / with filter', () => { 63 | return request({ 64 | url, 65 | json: true, 66 | qs: { text: 'second todo' } 67 | }).then(page => { 68 | expect(page.total).to.equal(1); 69 | expect(page.limit).to.equal(2); 70 | expect(page.skip).to.equal(0); 71 | expect(page.data.length).to.equal(1); 72 | expect(page.data[0].text).to.equal('second todo'); 73 | }); 74 | }); 75 | }); 76 | 77 | it('GET /id', () => { 78 | return request({ url: `${url}/${firstId}`, json: true }) 79 | .then(todo => { 80 | expect(todo[idProp]).to.equal(firstId); 81 | expect(todo.text).to.equal('first todo'); 82 | }); 83 | }); 84 | 85 | it('PATCH', () => { 86 | return request.patch({ 87 | url: `${url}/${firstId}`, 88 | json: true, 89 | body: { complete: true } 90 | }).then(todo => { 91 | expect(todo[idProp]).to.equal(firstId); 92 | expect(todo.text).to.equal('first todo'); 93 | expect(todo.complete).to.be.ok; 94 | }); 95 | }); 96 | 97 | it('DELETE /id', () => { 98 | return request.post({ 99 | url, 100 | json: true, 101 | body: { text: 'to delete', complete: false } 102 | }).then(todo => 103 | request.del({ url: `${url}/${todo[idProp]}`, json: true }) 104 | .then(todo => expect(todo.text).to.equal('to delete')) 105 | ); 106 | }); 107 | } 108 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import example from './example-tests'; 2 | import base from './common-tests'; 3 | import orm from './orm-tests'; 4 | 5 | export { example }; 6 | export { base }; 7 | export { orm }; 8 | -------------------------------------------------------------------------------- /src/orm-tests.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-expressions */ 2 | 3 | import { expect } from 'chai'; 4 | 5 | export default function orm (people, errors, idProp = 'id') { 6 | describe('Feathers ORM Common Tests', () => { 7 | it('wraps an ORM error in a feathers error', () => { 8 | return people.create({}).catch(error => { 9 | expect(error instanceof errors.FeathersError).to.be.ok; 10 | }); 11 | }); 12 | 13 | describe('Raw/Lean Queries', () => { 14 | const _ids = {}; 15 | const _data = {}; 16 | 17 | beforeEach(() => 18 | people.create({ 19 | name: 'Doug', 20 | age: 32 21 | }).then(data => { 22 | _data.Doug = data; 23 | _ids.Doug = data[idProp]; 24 | }) 25 | ); 26 | 27 | afterEach(() => 28 | people.remove(_ids.Doug).catch(() => {}) 29 | ); 30 | 31 | function noPOJO () { 32 | // The prototype objects are huge and cause node to hang 33 | // when the reporter tries to log the errors to the console. 34 | throw new Error('The expected result was not a POJO.'); 35 | } 36 | 37 | it('returns POJOs for find()', () => { 38 | return people.find({}).then(results => 39 | expect(Object.getPrototypeOf(results[0])).to.equal(Object.prototype) 40 | ).catch(noPOJO); 41 | }); 42 | 43 | it('returns a POJO for get()', () => { 44 | return people.get(_ids.Doug).then(result => 45 | expect(Object.getPrototypeOf(result)).to.equal(Object.prototype) 46 | ).catch(noPOJO); 47 | }); 48 | 49 | it('returns a POJO for create()', () => { 50 | return people.create({name: 'Sarah', age: 30}).then(result => 51 | expect(Object.getPrototypeOf(result)).to.equal(Object.prototype) 52 | ).catch(noPOJO); 53 | }); 54 | 55 | it('returns POJOs for bulk create()', () => { 56 | return people.create([{name: 'Sarah', age: 30}]).then(result => 57 | expect(Object.getPrototypeOf(result[0])).to.equal(Object.prototype) 58 | ).catch(noPOJO); 59 | }); 60 | 61 | it('returns a POJO for patch()', () => { 62 | return people.patch(_ids.Doug, {name: 'Sarah'}).then(result => 63 | expect(Object.getPrototypeOf(result)).to.equal(Object.prototype) 64 | ).catch(noPOJO); 65 | }); 66 | 67 | it('returns a POJO for update()', () => { 68 | return people.update(_ids.Doug, Object.assign(_data.Doug, {name: 'Sarah'})).then(result => 69 | expect(Object.getPrototypeOf(result)).to.equal(Object.prototype) 70 | ).catch(noPOJO); 71 | }); 72 | 73 | it('returns a POJO for remove()', () => { 74 | return people.remove(_ids.Doug).then(result => 75 | expect(Object.getPrototypeOf(result)).to.equal(Object.prototype) 76 | ).catch(noPOJO); 77 | }); 78 | }); 79 | }); 80 | } 81 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-expressions */ 2 | 3 | import assert from 'assert'; 4 | import * as plugin from '../src'; 5 | 6 | describe('feathers-service-tests', () => { 7 | it('basic functionality', () => { 8 | assert.equal(typeof plugin, 'object', 'plugin is not an object'); 9 | assert.equal(typeof plugin.base, 'function', 'plugin.base is not a function'); 10 | assert.equal(typeof plugin.example, 'function', 'plugin.example is not a function'); 11 | assert.equal(typeof plugin.orm, 'function', 'plugin.orm is not a function'); 12 | }); 13 | }); 14 | --------------------------------------------------------------------------------