Skip to content

Commit 3ec1ba5

Browse files
committed
test: coverage - add throwIf tests
1 parent 8c62848 commit 3ec1ba5

21 files changed

+376
-102
lines changed

.github/contributing.md

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,49 @@
22

33
Thank you for contributing to Feathers! :heart: :tada:
44

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-
75
## Report a bug
86

97
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.
108

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.
9+
If you haven't found your answer please feel free to join our [Discord server](https://discord.gg/qa8kez8QBx), create an issue on Github, or post on [Stackoverflow](http://stackoverflow.com) using the `feathersjs` tag. We try our best to monitor Stackoverflow but you're likely to get more immediate responses in Discord and Github.
1210

1311
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**.
1412

1513
## Report a Security Concern
1614

1715
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.
1816

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.
17+
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 [Discord](https://discord.gg/qa8kez8QBx) or email us at <a href="mailto:">hello@feathersjs.com</a> with details and we will respond ASAP.
2018

2119
For full details refer to our [Security docs](https://docs.feathersjs.com/SECURITY.html).
2220

2321
## Pull Requests
2422

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.
23+
We :heart: pull requests and we're continually working to make it as easy as possible for people to contribute.
2624

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.
25+
We prefer small pull requests with minimal code changes. The smaller they are the easier they are to review and merge. A FeathersJS maintainer 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.
2826

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:
27+
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, historical knowledge or information regarding the roadmap that the maintainers 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:
3028

3129
**All PRs (except documentation) should be accompanied with tests and pass the linting rules.**
3230

3331
### Code style
3432

3533
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`.
3634

37-
**Note:** `npm test` will run the compilation automatically before the tests.
38-
3935
### Tests
4036

4137
[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.
4238

4339
### Documentation
4440

45-
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.
41+
Feathers documentation is contained in Markdown files in the [docs folder](https://github.com/feathersjs/feathers) of the main repository. To change the documentation submit a pull request to that repo, referencing any other PR if applicable, and the docs will be updated as soon as it is merged.
4642

47-
## External Modules
43+
## Community Contributions
4844

49-
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.
45+
If you've written something awesome about Feathers, for the Feathers ecosystem, or created an app using Feathers please add it to the [awesome-feathersjs](https://github.com/feathersjs-ecosystem/awesome-feathersjs).
5046

51-
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:
47+
If you think your module would be a good core `feathersjs` module or [featherjs-ecosystem](https://github.com/feathersjs-ecosystem) module then please contact one of the Feathers maintainers on [Discord](https://discord.gg/qa8kez8QBx) and we can discuss whether it belongs and how to get it there. :beers:
5248

5349
## Contributor Code of Conduct
5450

.github/workflows/nodejs.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,22 @@ jobs:
1919
runs-on: ubuntu-latest
2020
steps:
2121
- uses: actions/checkout@v4
22-
- name: Use Node.js 20.x
22+
- name: Use Node.js 22.x
2323
uses: actions/setup-node@v4
2424
with:
25-
node-version: 20.x
25+
node-version: 22.x
2626
- run: npm install
2727
- run: npm run build
28+
29+
are_the_types_wrong:
30+
name: Are The Types Wrong?
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
- name: Use Node.js 22.x
35+
uses: actions/setup-node@v4
36+
with:
37+
node-version: 22.x
38+
- run: npm install
39+
- run: npm run build
40+
- run: npx --yes @arethetypeswrong/cli --pack . --profile esm-only

package.json

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,29 @@
33
"version": "8.2.1",
44
"description": "Useful hooks for use with Feathersjs services.",
55
"main": "./dist/index.js",
6-
"module": "./dist/index.mjs",
6+
"module": "./dist/index.js",
77
"types": "./dist/index.d.ts",
88
"type": "module",
99
"exports": {
1010
".": {
1111
"types": "./dist/index.d.ts",
12-
"require": "./dist/index.js",
13-
"import": "./dist/index.mjs"
12+
"import": "./dist/index.js"
13+
},
14+
"./hooks": {
15+
"types": "./dist/hooks.d.ts",
16+
"import": "./dist/hooks.js"
17+
},
18+
"./utils": {
19+
"types": "./dist/utils.d.ts",
20+
"import": "./dist/utils.js"
21+
},
22+
"./predicates": {
23+
"types": "./dist/predicates.d.ts",
24+
"import": "./dist/predicates.js"
25+
},
26+
"./resolvers": {
27+
"types": "./dist/resolvers.d.ts",
28+
"import": "./dist/resolvers.js"
1429
}
1530
},
1631
"scripts": {

src/common/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,5 @@ export function isPromise(p: any): p is Promise<any> {
22
return p instanceof Promise
33
}
44

5-
export { setFields } from './set-fields.js'
6-
export { transformItems } from './transform-items.js'
75
export { traverse } from './traverse.js'
86
export { clone } from './clone.js'

src/common/set-fields.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/common/transform-items.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/hooks/soft-delete/soft-delete.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('softDelete', () => {
5656
})
5757

5858
assert.deepStrictEqual(
59-
users.map(x => x.id),
59+
users.map((x: any) => x.id),
6060
[0, 1, 2, 3, 4, 5],
6161
)
6262
})
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { BadRequest } from '@feathersjs/errors'
2+
import { throwIfIsMulti } from './throw-if-is-multi.js'
3+
import type { HookContext } from '@feathersjs/feathers'
4+
5+
describe('throwIfIsMulti', () => {
6+
describe('general', () => {
7+
it('does not throw on find', async () => {
8+
const context = {
9+
method: 'find',
10+
params: { query: { name: 'item1' } },
11+
} as HookContext
12+
13+
await throwIfIsMulti()(context)
14+
})
15+
16+
it('does not throw on get', async () => {
17+
const context = {
18+
method: 'get',
19+
id: 'item1',
20+
} as HookContext
21+
await throwIfIsMulti()(context)
22+
})
23+
24+
it('does not throw on single create', async () => {
25+
const context = {
26+
method: 'create',
27+
data: { name: 'item1' },
28+
} as HookContext
29+
30+
await throwIfIsMulti()(context)
31+
})
32+
33+
it('does not throw on single update', async () => {
34+
const context = {
35+
method: 'update',
36+
id: 'item1',
37+
data: { name: 'item1' },
38+
} as HookContext
39+
40+
await throwIfIsMulti()(context)
41+
})
42+
43+
it('does not throw on single patch', async () => {
44+
const context = {
45+
method: 'patch',
46+
id: 'item1',
47+
data: { name: 'item1' },
48+
} as HookContext
49+
50+
await throwIfIsMulti()(context)
51+
})
52+
53+
it('does not throw on single remove', async () => {
54+
const context = {
55+
method: 'remove',
56+
id: 'item1',
57+
} as HookContext
58+
59+
await throwIfIsMulti()(context)
60+
})
61+
})
62+
63+
it('throws on multi create by default', async () => {
64+
const context = {
65+
method: 'create',
66+
data: [{ name: 'item1' }, { name: 'item2' }],
67+
} as HookContext
68+
69+
await expect(() => throwIfIsMulti()(context)).rejects.toThrow(BadRequest)
70+
})
71+
72+
it('throws on multi patch by default', async () => {
73+
const context = {
74+
method: 'patch',
75+
id: null,
76+
data: { name: 'item1' },
77+
} as any as HookContext
78+
79+
await expect(() => throwIfIsMulti()(context)).rejects.toThrow(BadRequest)
80+
})
81+
82+
it('throws on multi remove by default', async () => {
83+
const context = {
84+
method: 'remove',
85+
id: null,
86+
} as any as HookContext
87+
88+
await expect(() => throwIfIsMulti()(context)).rejects.toThrow(BadRequest)
89+
})
90+
91+
describe('with filter', () => {
92+
it('filter function has context as argument', async () => {
93+
const filterFn = vi.fn(() => true)
94+
const context = {
95+
method: 'create',
96+
data: [{ name: 'item1' }, { name: 'item2' }],
97+
} as HookContext
98+
await expect(() => throwIfIsMulti({ filter: filterFn })(context)).rejects.toThrow(BadRequest)
99+
expect(filterFn).toHaveBeenCalledWith(context)
100+
})
101+
102+
it('does not throw on multi create if filter returns false', async () => {
103+
const context = {
104+
method: 'create',
105+
data: [{ name: 'item1' }, { name: 'item2' }],
106+
} as HookContext
107+
108+
await expect(() => throwIfIsMulti()(context)).rejects.toThrow(BadRequest)
109+
110+
// sync
111+
await expect(
112+
throwIfIsMulti({
113+
filter: () => false,
114+
})(context),
115+
).resolves.not.toThrow()
116+
117+
// async
118+
await expect(
119+
throwIfIsMulti({
120+
filter: async () => false,
121+
})(context),
122+
).resolves.not.toThrow()
123+
})
124+
125+
it('throws on multi create if filter returns true', async () => {
126+
const context = {
127+
method: 'create',
128+
data: [{ name: 'item1' }, { name: 'item2' }],
129+
} as HookContext
130+
131+
// sync
132+
await expect(() =>
133+
throwIfIsMulti({
134+
filter: () => true,
135+
})(context),
136+
).rejects.toThrow()
137+
138+
// async
139+
await expect(() =>
140+
throwIfIsMulti({
141+
filter: async () => true,
142+
})(context),
143+
).rejects.toThrow()
144+
})
145+
})
146+
})
Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import type { HookContext, NextFunction } from '@feathersjs/feathers'
2-
import { checkContext } from '../../utils/index.js'
1+
import type { HookContext } from '@feathersjs/feathers'
32
import type { PredicateFn } from '../../types.js'
4-
import { BadRequest, type FeathersError } from '@feathersjs/errors'
5-
import { isMulti } from '../../predicates/index.js'
3+
import { type FeathersError } from '@feathersjs/errors'
4+
import { every, isMulti } from '../../predicates/index.js'
5+
import { throwIf } from './throw-if.js'
66

77
export type ThrowIfIsMultiOptions = {
88
filter?: PredicateFn
@@ -11,22 +11,13 @@ export type ThrowIfIsMultiOptions = {
1111

1212
export const throwIfIsMulti = <H extends HookContext = HookContext>(
1313
options?: ThrowIfIsMultiOptions,
14-
) => {
15-
return async (context: H, next?: NextFunction) => {
16-
checkContext(context, ['before', 'around'], ['create', 'patch', 'remove'], 'throwIfIsMulti')
17-
18-
if (!isMulti(context)) {
19-
return context
20-
}
21-
22-
if (!options?.filter || (await options.filter(context))) {
23-
throw options?.error ? options.error(context) : new BadRequest('Invalid operation')
24-
}
25-
26-
if (next) {
27-
await next()
28-
}
29-
30-
return context
31-
}
32-
}
14+
) =>
15+
throwIf<H>(
16+
every(
17+
every(isMulti, context => context.method !== 'find'),
18+
options?.filter,
19+
),
20+
{
21+
error: options?.error,
22+
},
23+
)

0 commit comments

Comments
 (0)