@payloadcms/plugin-stripe 4.0.0-canary.34 → 4.0.0-canary.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -8
- package/dist/index.spec.js +0 -73
- package/dist/index.spec.js.map +0 -1
- package/dist/routes/rest.spec.js +0 -275
- package/dist/routes/rest.spec.js.map +0 -1
- package/dist/utilities/deepen.spec.js +0 -110
- package/dist/utilities/deepen.spec.js.map +0 -1
- package/dist/utilities/sanitizeStripeRESTConfig.spec.js +0 -116
- package/dist/utilities/sanitizeStripeRESTConfig.spec.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/plugin-stripe",
|
|
3
|
-
"version": "4.0.0-canary.
|
|
3
|
+
"version": "4.0.0-canary.36",
|
|
4
4
|
"description": "Stripe plugin for Payload",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"payload",
|
|
@@ -55,19 +55,19 @@
|
|
|
55
55
|
"lodash.get": "^4.4.2",
|
|
56
56
|
"stripe": "^10.2.0",
|
|
57
57
|
"uuid": "14.0.0",
|
|
58
|
-
"@payloadcms/translations": "4.0.0-canary.
|
|
59
|
-
"@payloadcms/ui": "4.0.0-canary.
|
|
58
|
+
"@payloadcms/translations": "4.0.0-canary.36",
|
|
59
|
+
"@payloadcms/ui": "4.0.0-canary.36"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/lodash.get": "^4.4.7",
|
|
63
63
|
"@types/react": "19.2.14",
|
|
64
64
|
"@types/react-dom": "19.2.3",
|
|
65
|
-
"@payloadcms/
|
|
66
|
-
"payload": "4.0.0-canary.
|
|
67
|
-
"@payloadcms/
|
|
65
|
+
"@payloadcms/next": "4.0.0-canary.36",
|
|
66
|
+
"payload": "4.0.0-canary.36",
|
|
67
|
+
"@payloadcms/eslint-config": "3.28.0"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"payload": "4.0.0-canary.
|
|
70
|
+
"payload": "4.0.0-canary.36"
|
|
71
71
|
},
|
|
72
72
|
"publishConfig": {
|
|
73
73
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
|
|
80
80
|
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
|
81
81
|
"clean": "rimraf -g {dist,*.tsbuildinfo}",
|
|
82
|
-
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
|
82
|
+
"copyfiles": "copyfiles -u 1 -e \"**/*.spec.*\" \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
|
83
83
|
"lint": "eslint .",
|
|
84
84
|
"lint:fix": "eslint . --fix"
|
|
85
85
|
}
|
package/dist/index.spec.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { stripePlugin } from './index.js';
|
|
3
|
-
const createBaseConfig = ()=>({
|
|
4
|
-
collections: []
|
|
5
|
-
});
|
|
6
|
-
const getStripeRESTEndpoints = async (pluginConfig)=>{
|
|
7
|
-
const transformedConfig = await stripePlugin(pluginConfig)(createBaseConfig());
|
|
8
|
-
return transformedConfig.endpoints?.filter(({ path })=>path === '/stripe/rest') ?? [];
|
|
9
|
-
};
|
|
10
|
-
describe('stripePlugin REST endpoint registration', ()=>{
|
|
11
|
-
it('should not register the REST endpoint when the option is absent', async ()=>{
|
|
12
|
-
const endpoints = await getStripeRESTEndpoints({
|
|
13
|
-
stripeSecretKey: 'sk_test_example'
|
|
14
|
-
});
|
|
15
|
-
expect(endpoints).toHaveLength(0);
|
|
16
|
-
});
|
|
17
|
-
it('should treat false from an untyped configuration as disabled', async ()=>{
|
|
18
|
-
const endpoints = await getStripeRESTEndpoints({
|
|
19
|
-
// @ts-expect-error -- omission is the supported way to disable the endpoint.
|
|
20
|
-
rest: false,
|
|
21
|
-
stripeSecretKey: 'sk_test_example'
|
|
22
|
-
});
|
|
23
|
-
expect(endpoints).toHaveLength(0);
|
|
24
|
-
});
|
|
25
|
-
it('should register exactly one POST endpoint for a valid REST configuration', async ()=>{
|
|
26
|
-
const endpoints = await getStripeRESTEndpoints({
|
|
27
|
-
rest: {
|
|
28
|
-
allowedMethods: [
|
|
29
|
-
'subscriptions.list'
|
|
30
|
-
]
|
|
31
|
-
},
|
|
32
|
-
stripeSecretKey: 'sk_test_example'
|
|
33
|
-
});
|
|
34
|
-
expect(endpoints).toHaveLength(1);
|
|
35
|
-
expect(endpoints[0]?.method).toBe('post');
|
|
36
|
-
});
|
|
37
|
-
it.each([
|
|
38
|
-
[
|
|
39
|
-
'the legacy boolean form',
|
|
40
|
-
true
|
|
41
|
-
],
|
|
42
|
-
[
|
|
43
|
-
'an empty allowlist',
|
|
44
|
-
{
|
|
45
|
-
allowedMethods: []
|
|
46
|
-
}
|
|
47
|
-
],
|
|
48
|
-
[
|
|
49
|
-
'a wildcard method',
|
|
50
|
-
{
|
|
51
|
-
allowedMethods: [
|
|
52
|
-
'subscriptions.*'
|
|
53
|
-
]
|
|
54
|
-
}
|
|
55
|
-
],
|
|
56
|
-
[
|
|
57
|
-
'a non-string method',
|
|
58
|
-
{
|
|
59
|
-
allowedMethods: [
|
|
60
|
-
123
|
|
61
|
-
]
|
|
62
|
-
}
|
|
63
|
-
]
|
|
64
|
-
])('should reject %s during plugin transformation', async (_description, rest)=>{
|
|
65
|
-
const invalidConfig = {
|
|
66
|
-
rest,
|
|
67
|
-
stripeSecretKey: 'sk_test_example'
|
|
68
|
-
};
|
|
69
|
-
expect(()=>stripePlugin(invalidConfig)(createBaseConfig())).toThrow('The Stripe REST endpoint now requires an object with a non-empty allowedMethods array');
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
//# sourceMappingURL=index.spec.js.map
|
package/dist/index.spec.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.spec.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport { describe, expect, it } from 'vitest'\n\nimport type { StripePluginConfig } from './types.js'\n\nimport { stripePlugin } from './index.js'\n\nconst createBaseConfig = (): Config => ({ collections: [] })\n\nconst getStripeRESTEndpoints = async (pluginConfig: StripePluginConfig) => {\n const transformedConfig = await stripePlugin(pluginConfig)(createBaseConfig())\n\n return transformedConfig.endpoints?.filter(({ path }) => path === '/stripe/rest') ?? []\n}\n\ndescribe('stripePlugin REST endpoint registration', () => {\n it('should not register the REST endpoint when the option is absent', async () => {\n const endpoints = await getStripeRESTEndpoints({ stripeSecretKey: 'sk_test_example' })\n\n expect(endpoints).toHaveLength(0)\n })\n\n it('should treat false from an untyped configuration as disabled', async () => {\n const endpoints = await getStripeRESTEndpoints({\n // @ts-expect-error -- omission is the supported way to disable the endpoint.\n rest: false,\n stripeSecretKey: 'sk_test_example',\n })\n\n expect(endpoints).toHaveLength(0)\n })\n\n it('should register exactly one POST endpoint for a valid REST configuration', async () => {\n const endpoints = await getStripeRESTEndpoints({\n rest: { allowedMethods: ['subscriptions.list'] },\n stripeSecretKey: 'sk_test_example',\n })\n\n expect(endpoints).toHaveLength(1)\n expect(endpoints[0]?.method).toBe('post')\n })\n\n it.each([\n ['the legacy boolean form', true],\n ['an empty allowlist', { allowedMethods: [] }],\n ['a wildcard method', { allowedMethods: ['subscriptions.*'] }],\n ['a non-string method', { allowedMethods: [123] }],\n ])('should reject %s during plugin transformation', async (_description, rest) => {\n const invalidConfig = {\n rest,\n stripeSecretKey: 'sk_test_example',\n } as unknown as StripePluginConfig\n\n expect(() => stripePlugin(invalidConfig)(createBaseConfig())).toThrow(\n 'The Stripe REST endpoint now requires an object with a non-empty allowedMethods array',\n )\n })\n})\n"],"names":["describe","expect","it","stripePlugin","createBaseConfig","collections","getStripeRESTEndpoints","pluginConfig","transformedConfig","endpoints","filter","path","stripeSecretKey","toHaveLength","rest","allowedMethods","method","toBe","each","_description","invalidConfig","toThrow"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAI7C,SAASC,YAAY,QAAQ,aAAY;AAEzC,MAAMC,mBAAmB,IAAe,CAAA;QAAEC,aAAa,EAAE;IAAC,CAAA;AAE1D,MAAMC,yBAAyB,OAAOC;IACpC,MAAMC,oBAAoB,MAAML,aAAaI,cAAcH;IAE3D,OAAOI,kBAAkBC,SAAS,EAAEC,OAAO,CAAC,EAAEC,IAAI,EAAE,GAAKA,SAAS,mBAAmB,EAAE;AACzF;AAEAX,SAAS,2CAA2C;IAClDE,GAAG,mEAAmE;QACpE,MAAMO,YAAY,MAAMH,uBAAuB;YAAEM,iBAAiB;QAAkB;QAEpFX,OAAOQ,WAAWI,YAAY,CAAC;IACjC;IAEAX,GAAG,gEAAgE;QACjE,MAAMO,YAAY,MAAMH,uBAAuB;YAC7C,6EAA6E;YAC7EQ,MAAM;YACNF,iBAAiB;QACnB;QAEAX,OAAOQ,WAAWI,YAAY,CAAC;IACjC;IAEAX,GAAG,4EAA4E;QAC7E,MAAMO,YAAY,MAAMH,uBAAuB;YAC7CQ,MAAM;gBAAEC,gBAAgB;oBAAC;iBAAqB;YAAC;YAC/CH,iBAAiB;QACnB;QAEAX,OAAOQ,WAAWI,YAAY,CAAC;QAC/BZ,OAAOQ,SAAS,CAAC,EAAE,EAAEO,QAAQC,IAAI,CAAC;IACpC;IAEAf,GAAGgB,IAAI,CAAC;QACN;YAAC;YAA2B;SAAK;QACjC;YAAC;YAAsB;gBAAEH,gBAAgB,EAAE;YAAC;SAAE;QAC9C;YAAC;YAAqB;gBAAEA,gBAAgB;oBAAC;iBAAkB;YAAC;SAAE;QAC9D;YAAC;YAAuB;gBAAEA,gBAAgB;oBAAC;iBAAI;YAAC;SAAE;KACnD,EAAE,iDAAiD,OAAOI,cAAcL;QACvE,MAAMM,gBAAgB;YACpBN;YACAF,iBAAiB;QACnB;QAEAX,OAAO,IAAME,aAAaiB,eAAehB,qBAAqBiB,OAAO,CACnE;IAEJ;AACF"}
|
package/dist/routes/rest.spec.js
DELETED
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
import { UnauthorizedError } from 'payload';
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
const mocks = vi.hoisted(()=>({
|
|
4
|
-
addDataAndFileToRequest: vi.fn(),
|
|
5
|
-
canAccessAdmin: vi.fn(),
|
|
6
|
-
stripeProxy: vi.fn()
|
|
7
|
-
}));
|
|
8
|
-
vi.mock('payload', async (importOriginal)=>{
|
|
9
|
-
const payload = await importOriginal();
|
|
10
|
-
return {
|
|
11
|
-
...payload,
|
|
12
|
-
addDataAndFileToRequest: mocks.addDataAndFileToRequest,
|
|
13
|
-
canAccessAdmin: mocks.canAccessAdmin
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
vi.mock('../utilities/stripeProxy.js', ()=>({
|
|
17
|
-
stripeProxy: mocks.stripeProxy
|
|
18
|
-
}));
|
|
19
|
-
import { stripeREST } from './rest.js';
|
|
20
|
-
const loggerError = vi.fn();
|
|
21
|
-
const createPluginConfig = ({ access } = {})=>({
|
|
22
|
-
rest: {
|
|
23
|
-
access,
|
|
24
|
-
allowedMethods: [
|
|
25
|
-
'subscriptions.list'
|
|
26
|
-
]
|
|
27
|
-
},
|
|
28
|
-
stripeSecretKey: 'sk_test_example',
|
|
29
|
-
sync: []
|
|
30
|
-
});
|
|
31
|
-
const createRequest = ({ stripeArgs = [], stripeMethod = 'subscriptions.list', user = {
|
|
32
|
-
collection: 'users',
|
|
33
|
-
id: 'user-1'
|
|
34
|
-
} } = {})=>({
|
|
35
|
-
data: {
|
|
36
|
-
stripeArgs,
|
|
37
|
-
stripeMethod
|
|
38
|
-
},
|
|
39
|
-
payload: {
|
|
40
|
-
logger: {
|
|
41
|
-
error: loggerError
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
user
|
|
45
|
-
});
|
|
46
|
-
describe('stripeREST', ()=>{
|
|
47
|
-
beforeEach(()=>{
|
|
48
|
-
vi.clearAllMocks();
|
|
49
|
-
mocks.addDataAndFileToRequest.mockResolvedValue(undefined);
|
|
50
|
-
mocks.canAccessAdmin.mockResolvedValue(undefined);
|
|
51
|
-
mocks.stripeProxy.mockResolvedValue({
|
|
52
|
-
data: {
|
|
53
|
-
id: 'sub_123'
|
|
54
|
-
},
|
|
55
|
-
status: 200
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
it('should return 401 for an anonymous request without invoking Stripe', async ()=>{
|
|
59
|
-
const req = createRequest({
|
|
60
|
-
user: null
|
|
61
|
-
});
|
|
62
|
-
const response = await stripeREST({
|
|
63
|
-
pluginConfig: createPluginConfig(),
|
|
64
|
-
req
|
|
65
|
-
});
|
|
66
|
-
expect(response.status).toBe(401);
|
|
67
|
-
expect(mocks.addDataAndFileToRequest).toHaveBeenCalledWith(req);
|
|
68
|
-
expect(mocks.canAccessAdmin).not.toHaveBeenCalled();
|
|
69
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
70
|
-
});
|
|
71
|
-
it('should return 403 when default admin access denies the user', async ()=>{
|
|
72
|
-
mocks.canAccessAdmin.mockRejectedValue(new UnauthorizedError());
|
|
73
|
-
const req = createRequest();
|
|
74
|
-
const response = await stripeREST({
|
|
75
|
-
pluginConfig: createPluginConfig(),
|
|
76
|
-
req
|
|
77
|
-
});
|
|
78
|
-
expect(response.status).toBe(403);
|
|
79
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
80
|
-
});
|
|
81
|
-
it('should return a generic 500 when default admin access throws unexpectedly', async ()=>{
|
|
82
|
-
const adminAccessError = new Error('sensitive admin access detail');
|
|
83
|
-
mocks.canAccessAdmin.mockRejectedValue(adminAccessError);
|
|
84
|
-
const req = createRequest();
|
|
85
|
-
const response = await stripeREST({
|
|
86
|
-
pluginConfig: createPluginConfig(),
|
|
87
|
-
req
|
|
88
|
-
});
|
|
89
|
-
const body = await response.json();
|
|
90
|
-
expect(response.status).toBe(500);
|
|
91
|
-
expect(body).toEqual({
|
|
92
|
-
message: 'Internal server error'
|
|
93
|
-
});
|
|
94
|
-
expect(JSON.stringify(body)).not.toContain('sensitive admin access detail');
|
|
95
|
-
expect(loggerError).toHaveBeenCalledWith({
|
|
96
|
-
err: adminAccessError,
|
|
97
|
-
msg: 'An unexpected error occurred in the Stripe plugin REST handler.'
|
|
98
|
-
});
|
|
99
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
100
|
-
});
|
|
101
|
-
it('should invoke an exactly listed method for an admin user', async ()=>{
|
|
102
|
-
const req = createRequest({
|
|
103
|
-
stripeArgs: [
|
|
104
|
-
{
|
|
105
|
-
limit: 10
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
});
|
|
109
|
-
const response = await stripeREST({
|
|
110
|
-
pluginConfig: createPluginConfig(),
|
|
111
|
-
req
|
|
112
|
-
});
|
|
113
|
-
expect(response.status).toBe(200);
|
|
114
|
-
expect(mocks.stripeProxy).toHaveBeenCalledWith({
|
|
115
|
-
stripeArgs: [
|
|
116
|
-
{
|
|
117
|
-
limit: 10
|
|
118
|
-
}
|
|
119
|
-
],
|
|
120
|
-
stripeMethod: 'subscriptions.list',
|
|
121
|
-
stripeSecretKey: 'sk_test_example'
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
it('should allow a non-admin when an asynchronous custom access callback permits it', async ()=>{
|
|
125
|
-
const access = vi.fn(async ()=>true);
|
|
126
|
-
const req = createRequest();
|
|
127
|
-
const response = await stripeREST({
|
|
128
|
-
pluginConfig: createPluginConfig({
|
|
129
|
-
access
|
|
130
|
-
}),
|
|
131
|
-
req
|
|
132
|
-
});
|
|
133
|
-
expect(response.status).toBe(200);
|
|
134
|
-
expect(access).toHaveBeenCalledWith({
|
|
135
|
-
req
|
|
136
|
-
});
|
|
137
|
-
expect(mocks.canAccessAdmin).not.toHaveBeenCalled();
|
|
138
|
-
});
|
|
139
|
-
it('should return 403 when custom access denies the user', async ()=>{
|
|
140
|
-
const access = vi.fn(async ()=>false);
|
|
141
|
-
const req = createRequest();
|
|
142
|
-
const response = await stripeREST({
|
|
143
|
-
pluginConfig: createPluginConfig({
|
|
144
|
-
access
|
|
145
|
-
}),
|
|
146
|
-
req
|
|
147
|
-
});
|
|
148
|
-
expect(response.status).toBe(403);
|
|
149
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
150
|
-
});
|
|
151
|
-
it('should return a generic 500 when custom access throws', async ()=>{
|
|
152
|
-
const access = vi.fn(async ()=>{
|
|
153
|
-
throw new Error('sensitive access detail');
|
|
154
|
-
});
|
|
155
|
-
const req = createRequest();
|
|
156
|
-
const response = await stripeREST({
|
|
157
|
-
pluginConfig: createPluginConfig({
|
|
158
|
-
access
|
|
159
|
-
}),
|
|
160
|
-
req
|
|
161
|
-
});
|
|
162
|
-
const body = await response.text();
|
|
163
|
-
expect(response.status).toBe(500);
|
|
164
|
-
expect(body).not.toContain('sensitive access detail');
|
|
165
|
-
expect(loggerError).toHaveBeenCalledWith({
|
|
166
|
-
err: expect.any(Error),
|
|
167
|
-
msg: 'An unexpected error occurred in the Stripe plugin REST handler.'
|
|
168
|
-
});
|
|
169
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
170
|
-
});
|
|
171
|
-
it.each([
|
|
172
|
-
'customers.list',
|
|
173
|
-
'subscriptions.cancel',
|
|
174
|
-
'subscriptions.*'
|
|
175
|
-
])('should return 400 for unlisted method %s without invoking Stripe', async (stripeMethod)=>{
|
|
176
|
-
const req = createRequest({
|
|
177
|
-
stripeMethod
|
|
178
|
-
});
|
|
179
|
-
const response = await stripeREST({
|
|
180
|
-
pluginConfig: createPluginConfig(),
|
|
181
|
-
req
|
|
182
|
-
});
|
|
183
|
-
expect(response.status).toBe(400);
|
|
184
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
185
|
-
});
|
|
186
|
-
it.each([
|
|
187
|
-
[
|
|
188
|
-
''
|
|
189
|
-
],
|
|
190
|
-
[
|
|
191
|
-
' '
|
|
192
|
-
],
|
|
193
|
-
[
|
|
194
|
-
123
|
|
195
|
-
],
|
|
196
|
-
[
|
|
197
|
-
null
|
|
198
|
-
]
|
|
199
|
-
])('should return 400 for malformed method %s without invoking Stripe', async (stripeMethod)=>{
|
|
200
|
-
const req = createRequest({
|
|
201
|
-
stripeMethod
|
|
202
|
-
});
|
|
203
|
-
const response = await stripeREST({
|
|
204
|
-
pluginConfig: createPluginConfig(),
|
|
205
|
-
req
|
|
206
|
-
});
|
|
207
|
-
expect(response.status).toBe(400);
|
|
208
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
209
|
-
});
|
|
210
|
-
it('should return 400 for a missing method without invoking Stripe', async ()=>{
|
|
211
|
-
const req = createRequest();
|
|
212
|
-
delete req.data?.stripeMethod;
|
|
213
|
-
const response = await stripeREST({
|
|
214
|
-
pluginConfig: createPluginConfig(),
|
|
215
|
-
req
|
|
216
|
-
});
|
|
217
|
-
expect(response.status).toBe(400);
|
|
218
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
219
|
-
});
|
|
220
|
-
it('should return 400 for non-array Stripe arguments without invoking Stripe', async ()=>{
|
|
221
|
-
const req = createRequest({
|
|
222
|
-
stripeArgs: {
|
|
223
|
-
limit: 10
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
const response = await stripeREST({
|
|
227
|
-
pluginConfig: createPluginConfig(),
|
|
228
|
-
req
|
|
229
|
-
});
|
|
230
|
-
expect(response.status).toBe(400);
|
|
231
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
232
|
-
});
|
|
233
|
-
it('should evaluate access before revealing whether a method is listed', async ()=>{
|
|
234
|
-
mocks.canAccessAdmin.mockRejectedValue(new UnauthorizedError());
|
|
235
|
-
const req = createRequest({
|
|
236
|
-
stripeMethod: 'customers.delete'
|
|
237
|
-
});
|
|
238
|
-
const response = await stripeREST({
|
|
239
|
-
pluginConfig: createPluginConfig(),
|
|
240
|
-
req
|
|
241
|
-
});
|
|
242
|
-
expect(response.status).toBe(403);
|
|
243
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
244
|
-
});
|
|
245
|
-
it('should preserve the Stripe proxy response status and body after all guards pass', async ()=>{
|
|
246
|
-
mocks.stripeProxy.mockResolvedValue({
|
|
247
|
-
message: 'Stripe request failed',
|
|
248
|
-
status: 404
|
|
249
|
-
});
|
|
250
|
-
const req = createRequest();
|
|
251
|
-
const response = await stripeREST({
|
|
252
|
-
pluginConfig: createPluginConfig(),
|
|
253
|
-
req
|
|
254
|
-
});
|
|
255
|
-
expect(response.status).toBe(404);
|
|
256
|
-
expect(await response.json()).toEqual({
|
|
257
|
-
message: 'Stripe request failed',
|
|
258
|
-
status: 404
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
it('should return a generic 500 when request parsing fails', async ()=>{
|
|
262
|
-
mocks.addDataAndFileToRequest.mockRejectedValue(new Error('sensitive parser detail'));
|
|
263
|
-
const req = createRequest();
|
|
264
|
-
const response = await stripeREST({
|
|
265
|
-
pluginConfig: createPluginConfig(),
|
|
266
|
-
req
|
|
267
|
-
});
|
|
268
|
-
const body = await response.text();
|
|
269
|
-
expect(response.status).toBe(500);
|
|
270
|
-
expect(body).not.toContain('sensitive parser detail');
|
|
271
|
-
expect(mocks.stripeProxy).not.toHaveBeenCalled();
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
//# sourceMappingURL=rest.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routes/rest.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { UnauthorizedError } from 'payload'\nimport { beforeEach, describe, expect, it, vi } from 'vitest'\n\nimport type { SanitizedStripePluginConfig, StripeRESTAccess } from '../types.js'\n\nconst mocks = vi.hoisted(() => ({\n addDataAndFileToRequest: vi.fn(),\n canAccessAdmin: vi.fn(),\n stripeProxy: vi.fn(),\n}))\n\nvi.mock('payload', async (importOriginal) => {\n const payload = await importOriginal<typeof import('payload')>()\n\n return {\n ...payload,\n addDataAndFileToRequest: mocks.addDataAndFileToRequest,\n canAccessAdmin: mocks.canAccessAdmin,\n }\n})\n\nvi.mock('../utilities/stripeProxy.js', () => ({\n stripeProxy: mocks.stripeProxy,\n}))\n\nimport { stripeREST } from './rest.js'\n\nconst loggerError = vi.fn()\n\nconst createPluginConfig = ({\n access,\n}: {\n access?: StripeRESTAccess\n} = {}): SanitizedStripePluginConfig => ({\n rest: {\n access,\n allowedMethods: ['subscriptions.list'],\n },\n stripeSecretKey: 'sk_test_example',\n sync: [],\n})\n\nconst createRequest = ({\n stripeArgs = [],\n stripeMethod = 'subscriptions.list',\n user = { collection: 'users', id: 'user-1' },\n}: {\n stripeArgs?: unknown\n stripeMethod?: unknown\n user?: null | { collection: string; id: string }\n} = {}): PayloadRequest =>\n ({\n data: { stripeArgs, stripeMethod },\n payload: {\n logger: { error: loggerError },\n },\n user,\n }) as unknown as PayloadRequest\n\ndescribe('stripeREST', () => {\n beforeEach(() => {\n vi.clearAllMocks()\n mocks.addDataAndFileToRequest.mockResolvedValue(undefined)\n mocks.canAccessAdmin.mockResolvedValue(undefined)\n mocks.stripeProxy.mockResolvedValue({ data: { id: 'sub_123' }, status: 200 })\n })\n\n it('should return 401 for an anonymous request without invoking Stripe', async () => {\n const req = createRequest({ user: null })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(401)\n expect(mocks.addDataAndFileToRequest).toHaveBeenCalledWith(req)\n expect(mocks.canAccessAdmin).not.toHaveBeenCalled()\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should return 403 when default admin access denies the user', async () => {\n mocks.canAccessAdmin.mockRejectedValue(new UnauthorizedError())\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(403)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should return a generic 500 when default admin access throws unexpectedly', async () => {\n const adminAccessError = new Error('sensitive admin access detail')\n\n mocks.canAccessAdmin.mockRejectedValue(adminAccessError)\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n const body = await response.json()\n\n expect(response.status).toBe(500)\n expect(body).toEqual({ message: 'Internal server error' })\n expect(JSON.stringify(body)).not.toContain('sensitive admin access detail')\n expect(loggerError).toHaveBeenCalledWith({\n err: adminAccessError,\n msg: 'An unexpected error occurred in the Stripe plugin REST handler.',\n })\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should invoke an exactly listed method for an admin user', async () => {\n const req = createRequest({ stripeArgs: [{ limit: 10 }] })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(200)\n expect(mocks.stripeProxy).toHaveBeenCalledWith({\n stripeArgs: [{ limit: 10 }],\n stripeMethod: 'subscriptions.list',\n stripeSecretKey: 'sk_test_example',\n })\n })\n\n it('should allow a non-admin when an asynchronous custom access callback permits it', async () => {\n const access = vi.fn(async () => true)\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig({ access }), req })\n\n expect(response.status).toBe(200)\n expect(access).toHaveBeenCalledWith({ req })\n expect(mocks.canAccessAdmin).not.toHaveBeenCalled()\n })\n\n it('should return 403 when custom access denies the user', async () => {\n const access = vi.fn(async () => false)\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig({ access }), req })\n\n expect(response.status).toBe(403)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should return a generic 500 when custom access throws', async () => {\n const access = vi.fn(async () => {\n throw new Error('sensitive access detail')\n })\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig({ access }), req })\n const body = await response.text()\n\n expect(response.status).toBe(500)\n expect(body).not.toContain('sensitive access detail')\n expect(loggerError).toHaveBeenCalledWith({\n err: expect.any(Error),\n msg: 'An unexpected error occurred in the Stripe plugin REST handler.',\n })\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it.each(['customers.list', 'subscriptions.cancel', 'subscriptions.*'])(\n 'should return 400 for unlisted method %s without invoking Stripe',\n async (stripeMethod) => {\n const req = createRequest({ stripeMethod })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(400)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n },\n )\n\n it.each([[''], [' '], [123], [null]])(\n 'should return 400 for malformed method %s without invoking Stripe',\n async (stripeMethod) => {\n const req = createRequest({ stripeMethod })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(400)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n },\n )\n\n it('should return 400 for a missing method without invoking Stripe', async () => {\n const req = createRequest()\n\n delete req.data?.stripeMethod\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(400)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should return 400 for non-array Stripe arguments without invoking Stripe', async () => {\n const req = createRequest({ stripeArgs: { limit: 10 } })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(400)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should evaluate access before revealing whether a method is listed', async () => {\n mocks.canAccessAdmin.mockRejectedValue(new UnauthorizedError())\n const req = createRequest({ stripeMethod: 'customers.delete' })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(403)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should preserve the Stripe proxy response status and body after all guards pass', async () => {\n mocks.stripeProxy.mockResolvedValue({ message: 'Stripe request failed', status: 404 })\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(404)\n expect(await response.json()).toEqual({ message: 'Stripe request failed', status: 404 })\n })\n\n it('should return a generic 500 when request parsing fails', async () => {\n mocks.addDataAndFileToRequest.mockRejectedValue(new Error('sensitive parser detail'))\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n const body = await response.text()\n\n expect(response.status).toBe(500)\n expect(body).not.toContain('sensitive parser detail')\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n})\n"],"names":["UnauthorizedError","beforeEach","describe","expect","it","vi","mocks","hoisted","addDataAndFileToRequest","fn","canAccessAdmin","stripeProxy","mock","importOriginal","payload","stripeREST","loggerError","createPluginConfig","access","rest","allowedMethods","stripeSecretKey","sync","createRequest","stripeArgs","stripeMethod","user","collection","id","data","logger","error","clearAllMocks","mockResolvedValue","undefined","status","req","response","pluginConfig","toBe","toHaveBeenCalledWith","not","toHaveBeenCalled","mockRejectedValue","adminAccessError","Error","body","json","toEqual","message","JSON","stringify","toContain","err","msg","limit","text","any","each"],"mappings":"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"}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { deepen, getSyncedFields } from './deepen.js';
|
|
3
|
-
describe('Stripe field mappings', ()=>{
|
|
4
|
-
it('should build constrained mappings from owned values', ()=>{
|
|
5
|
-
const marker = 'constrainedStripeMapping';
|
|
6
|
-
const inheritedSource = 'constrainedStripeSource';
|
|
7
|
-
const originalDescriptor = Object.getOwnPropertyDescriptor(Object.prototype, marker);
|
|
8
|
-
let inheritedGetterCalls = 0;
|
|
9
|
-
let processStateChanged = false;
|
|
10
|
-
let nestedMapping;
|
|
11
|
-
let syncedMapping;
|
|
12
|
-
let webhookMapping;
|
|
13
|
-
try {
|
|
14
|
-
expect(()=>deepen({
|
|
15
|
-
[`__proto__.${marker}`]: 'value'
|
|
16
|
-
})).toThrow('Invalid field path.');
|
|
17
|
-
nestedMapping = deepen(Object.assign(Object.create({
|
|
18
|
-
'ignored.value': 'inherited'
|
|
19
|
-
}), {
|
|
20
|
-
[`constructor.prototype.${marker}`]: 'local',
|
|
21
|
-
'profile.displayName': 'Ada'
|
|
22
|
-
}));
|
|
23
|
-
const data = Object.create({
|
|
24
|
-
get [inheritedSource] () {
|
|
25
|
-
inheritedGetterCalls += 1;
|
|
26
|
-
return 'inherited';
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
data.displayName = 'Ada';
|
|
30
|
-
syncedMapping = getSyncedFields({
|
|
31
|
-
data,
|
|
32
|
-
fields: [
|
|
33
|
-
{
|
|
34
|
-
fieldPath: inheritedSource,
|
|
35
|
-
stripeProperty: 'metadata.inherited'
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
fieldPath: 'constructor',
|
|
39
|
-
stripeProperty: 'constructor'
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
fieldPath: 'displayName',
|
|
43
|
-
stripeProperty: `constructor.prototype.${marker}`
|
|
44
|
-
}
|
|
45
|
-
],
|
|
46
|
-
source: 'payload'
|
|
47
|
-
});
|
|
48
|
-
const stripeData = Object.create({
|
|
49
|
-
get [inheritedSource] () {
|
|
50
|
-
inheritedGetterCalls += 1;
|
|
51
|
-
return 'inherited';
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
stripeData.name = 'Ada';
|
|
55
|
-
webhookMapping = getSyncedFields({
|
|
56
|
-
data: stripeData,
|
|
57
|
-
fields: [
|
|
58
|
-
{
|
|
59
|
-
fieldPath: 'profile.inherited',
|
|
60
|
-
stripeProperty: inheritedSource
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
fieldPath: 'profile.displayName',
|
|
64
|
-
stripeProperty: 'name'
|
|
65
|
-
}
|
|
66
|
-
],
|
|
67
|
-
source: 'stripe'
|
|
68
|
-
});
|
|
69
|
-
processStateChanged = Object.hasOwn(Object.prototype, marker);
|
|
70
|
-
} finally{
|
|
71
|
-
if (originalDescriptor) {
|
|
72
|
-
Object.defineProperty(Object.prototype, marker, originalDescriptor);
|
|
73
|
-
} else {
|
|
74
|
-
delete Object.prototype[marker];
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
expect(processStateChanged).toBe(false);
|
|
78
|
-
expect(inheritedGetterCalls).toBe(0);
|
|
79
|
-
expect(Object.getOwnPropertyDescriptor(Object.prototype, marker)).toEqual(originalDescriptor);
|
|
80
|
-
expect(nestedMapping).toEqual({
|
|
81
|
-
constructor: {
|
|
82
|
-
prototype: {
|
|
83
|
-
[marker]: 'local'
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
profile: {
|
|
87
|
-
displayName: 'Ada'
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
expect(Object.hasOwn(nestedMapping, 'ignored')).toBe(false);
|
|
91
|
-
expect(syncedMapping).toEqual({
|
|
92
|
-
constructor: {
|
|
93
|
-
prototype: {
|
|
94
|
-
[marker]: 'Ada'
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
metadata: {
|
|
98
|
-
inherited: undefined
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
expect(webhookMapping).toEqual({
|
|
102
|
-
profile: {
|
|
103
|
-
displayName: 'Ada',
|
|
104
|
-
inherited: undefined
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
//# sourceMappingURL=deepen.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/deepen.spec.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest'\n\nimport { deepen, getSyncedFields } from './deepen.js'\n\ndescribe('Stripe field mappings', () => {\n it('should build constrained mappings from owned values', () => {\n const marker = 'constrainedStripeMapping'\n const inheritedSource = 'constrainedStripeSource'\n const originalDescriptor = Object.getOwnPropertyDescriptor(Object.prototype, marker)\n let inheritedGetterCalls = 0\n let processStateChanged = false\n let nestedMapping: Record<string, unknown> | undefined\n let syncedMapping: Record<string, unknown> | undefined\n let webhookMapping: Record<string, unknown> | undefined\n\n try {\n expect(() => deepen({ [`__proto__.${marker}`]: 'value' })).toThrow('Invalid field path.')\n\n nestedMapping = deepen(\n Object.assign(Object.create({ 'ignored.value': 'inherited' }), {\n [`constructor.prototype.${marker}`]: 'local',\n 'profile.displayName': 'Ada',\n }),\n )\n\n const data = Object.create({\n get [inheritedSource]() {\n inheritedGetterCalls += 1\n return 'inherited'\n },\n }) as Record<string, unknown>\n data.displayName = 'Ada'\n\n syncedMapping = getSyncedFields({\n data,\n fields: [\n { fieldPath: inheritedSource, stripeProperty: 'metadata.inherited' },\n { fieldPath: 'constructor', stripeProperty: 'constructor' },\n {\n fieldPath: 'displayName',\n stripeProperty: `constructor.prototype.${marker}`,\n },\n ],\n source: 'payload',\n })\n\n const stripeData = Object.create({\n get [inheritedSource]() {\n inheritedGetterCalls += 1\n return 'inherited'\n },\n }) as Record<string, unknown>\n stripeData.name = 'Ada'\n\n webhookMapping = getSyncedFields({\n data: stripeData,\n fields: [\n { fieldPath: 'profile.inherited', stripeProperty: inheritedSource },\n { fieldPath: 'profile.displayName', stripeProperty: 'name' },\n ],\n source: 'stripe',\n })\n processStateChanged = Object.hasOwn(Object.prototype, marker)\n } finally {\n if (originalDescriptor) {\n Object.defineProperty(Object.prototype, marker, originalDescriptor)\n } else {\n delete (Object.prototype as Record<string, unknown>)[marker]\n }\n }\n\n expect(processStateChanged).toBe(false)\n expect(inheritedGetterCalls).toBe(0)\n expect(Object.getOwnPropertyDescriptor(Object.prototype, marker)).toEqual(originalDescriptor)\n expect(nestedMapping).toEqual({\n constructor: {\n prototype: {\n [marker]: 'local',\n },\n },\n profile: {\n displayName: 'Ada',\n },\n })\n expect(Object.hasOwn(nestedMapping as object, 'ignored')).toBe(false)\n expect(syncedMapping).toEqual({\n constructor: {\n prototype: {\n [marker]: 'Ada',\n },\n },\n metadata: {\n inherited: undefined,\n },\n })\n expect(webhookMapping).toEqual({\n profile: {\n displayName: 'Ada',\n inherited: undefined,\n },\n })\n })\n})\n"],"names":["describe","expect","it","deepen","getSyncedFields","marker","inheritedSource","originalDescriptor","Object","getOwnPropertyDescriptor","prototype","inheritedGetterCalls","processStateChanged","nestedMapping","syncedMapping","webhookMapping","toThrow","assign","create","data","displayName","fields","fieldPath","stripeProperty","source","stripeData","name","hasOwn","defineProperty","toBe","toEqual","profile","metadata","inherited","undefined"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,MAAM,EAAEC,eAAe,QAAQ,cAAa;AAErDJ,SAAS,yBAAyB;IAChCE,GAAG,uDAAuD;QACxD,MAAMG,SAAS;QACf,MAAMC,kBAAkB;QACxB,MAAMC,qBAAqBC,OAAOC,wBAAwB,CAACD,OAAOE,SAAS,EAAEL;QAC7E,IAAIM,uBAAuB;QAC3B,IAAIC,sBAAsB;QAC1B,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QAEJ,IAAI;YACFd,OAAO,IAAME,OAAO;oBAAE,CAAC,CAAC,UAAU,EAAEE,QAAQ,CAAC,EAAE;gBAAQ,IAAIW,OAAO,CAAC;YAEnEH,gBAAgBV,OACdK,OAAOS,MAAM,CAACT,OAAOU,MAAM,CAAC;gBAAE,iBAAiB;YAAY,IAAI;gBAC7D,CAAC,CAAC,sBAAsB,EAAEb,QAAQ,CAAC,EAAE;gBACrC,uBAAuB;YACzB;YAGF,MAAMc,OAAOX,OAAOU,MAAM,CAAC;gBACzB,IAAI,CAACZ,gBAAgB,IAAG;oBACtBK,wBAAwB;oBACxB,OAAO;gBACT;YACF;YACAQ,KAAKC,WAAW,GAAG;YAEnBN,gBAAgBV,gBAAgB;gBAC9Be;gBACAE,QAAQ;oBACN;wBAAEC,WAAWhB;wBAAiBiB,gBAAgB;oBAAqB;oBACnE;wBAAED,WAAW;wBAAeC,gBAAgB;oBAAc;oBAC1D;wBACED,WAAW;wBACXC,gBAAgB,CAAC,sBAAsB,EAAElB,QAAQ;oBACnD;iBACD;gBACDmB,QAAQ;YACV;YAEA,MAAMC,aAAajB,OAAOU,MAAM,CAAC;gBAC/B,IAAI,CAACZ,gBAAgB,IAAG;oBACtBK,wBAAwB;oBACxB,OAAO;gBACT;YACF;YACAc,WAAWC,IAAI,GAAG;YAElBX,iBAAiBX,gBAAgB;gBAC/Be,MAAMM;gBACNJ,QAAQ;oBACN;wBAAEC,WAAW;wBAAqBC,gBAAgBjB;oBAAgB;oBAClE;wBAAEgB,WAAW;wBAAuBC,gBAAgB;oBAAO;iBAC5D;gBACDC,QAAQ;YACV;YACAZ,sBAAsBJ,OAAOmB,MAAM,CAACnB,OAAOE,SAAS,EAAEL;QACxD,SAAU;YACR,IAAIE,oBAAoB;gBACtBC,OAAOoB,cAAc,CAACpB,OAAOE,SAAS,EAAEL,QAAQE;YAClD,OAAO;gBACL,OAAO,AAACC,OAAOE,SAAS,AAA4B,CAACL,OAAO;YAC9D;QACF;QAEAJ,OAAOW,qBAAqBiB,IAAI,CAAC;QACjC5B,OAAOU,sBAAsBkB,IAAI,CAAC;QAClC5B,OAAOO,OAAOC,wBAAwB,CAACD,OAAOE,SAAS,EAAEL,SAASyB,OAAO,CAACvB;QAC1EN,OAAOY,eAAeiB,OAAO,CAAC;YAC5B,aAAa;gBACXpB,WAAW;oBACT,CAACL,OAAO,EAAE;gBACZ;YACF;YACA0B,SAAS;gBACPX,aAAa;YACf;QACF;QACAnB,OAAOO,OAAOmB,MAAM,CAACd,eAAyB,YAAYgB,IAAI,CAAC;QAC/D5B,OAAOa,eAAegB,OAAO,CAAC;YAC5B,aAAa;gBACXpB,WAAW;oBACT,CAACL,OAAO,EAAE;gBACZ;YACF;YACA2B,UAAU;gBACRC,WAAWC;YACb;QACF;QACAjC,OAAOc,gBAAgBe,OAAO,CAAC;YAC7BC,SAAS;gBACPX,aAAa;gBACba,WAAWC;YACb;QACF;IACF;AACF"}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { sanitizeStripeRESTConfig } from './sanitizeStripeRESTConfig.js';
|
|
3
|
-
describe('sanitizeStripeRESTConfig', ()=>{
|
|
4
|
-
it('should disable the REST endpoint when configuration is absent', ()=>{
|
|
5
|
-
expect(sanitizeStripeRESTConfig({
|
|
6
|
-
rest: undefined
|
|
7
|
-
})).toBeUndefined();
|
|
8
|
-
});
|
|
9
|
-
it('should treat false from an untyped configuration as disabled', ()=>{
|
|
10
|
-
expect(sanitizeStripeRESTConfig({
|
|
11
|
-
rest: false
|
|
12
|
-
})).toBeUndefined();
|
|
13
|
-
});
|
|
14
|
-
it('should return a new configuration with exact, de-duplicated, frozen methods', ()=>{
|
|
15
|
-
const access = vi.fn(()=>true);
|
|
16
|
-
const rest = {
|
|
17
|
-
access,
|
|
18
|
-
allowedMethods: [
|
|
19
|
-
'subscriptions.list',
|
|
20
|
-
' paymentIntents.retrieve ',
|
|
21
|
-
'subscriptions.list'
|
|
22
|
-
]
|
|
23
|
-
};
|
|
24
|
-
const sanitized = sanitizeStripeRESTConfig({
|
|
25
|
-
rest
|
|
26
|
-
});
|
|
27
|
-
expect(sanitized).not.toBe(rest);
|
|
28
|
-
expect(sanitized).toEqual({
|
|
29
|
-
access,
|
|
30
|
-
allowedMethods: [
|
|
31
|
-
'subscriptions.list',
|
|
32
|
-
' paymentIntents.retrieve '
|
|
33
|
-
]
|
|
34
|
-
});
|
|
35
|
-
expect(Object.isFrozen(sanitized.allowedMethods)).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
it.each([
|
|
38
|
-
[
|
|
39
|
-
'the legacy boolean form',
|
|
40
|
-
true
|
|
41
|
-
],
|
|
42
|
-
[
|
|
43
|
-
'an array',
|
|
44
|
-
[
|
|
45
|
-
{
|
|
46
|
-
allowedMethods: [
|
|
47
|
-
'subscriptions.list'
|
|
48
|
-
]
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
],
|
|
52
|
-
[
|
|
53
|
-
'a missing allowlist',
|
|
54
|
-
{}
|
|
55
|
-
],
|
|
56
|
-
[
|
|
57
|
-
'a non-array allowlist',
|
|
58
|
-
{
|
|
59
|
-
allowedMethods: 'subscriptions.list'
|
|
60
|
-
}
|
|
61
|
-
],
|
|
62
|
-
[
|
|
63
|
-
'an empty allowlist',
|
|
64
|
-
{
|
|
65
|
-
allowedMethods: []
|
|
66
|
-
}
|
|
67
|
-
],
|
|
68
|
-
[
|
|
69
|
-
'an empty method',
|
|
70
|
-
{
|
|
71
|
-
allowedMethods: [
|
|
72
|
-
''
|
|
73
|
-
]
|
|
74
|
-
}
|
|
75
|
-
],
|
|
76
|
-
[
|
|
77
|
-
'a whitespace-only method',
|
|
78
|
-
{
|
|
79
|
-
allowedMethods: [
|
|
80
|
-
' '
|
|
81
|
-
]
|
|
82
|
-
}
|
|
83
|
-
],
|
|
84
|
-
[
|
|
85
|
-
'a non-string method',
|
|
86
|
-
{
|
|
87
|
-
allowedMethods: [
|
|
88
|
-
123
|
|
89
|
-
]
|
|
90
|
-
}
|
|
91
|
-
],
|
|
92
|
-
[
|
|
93
|
-
'a wildcard method',
|
|
94
|
-
{
|
|
95
|
-
allowedMethods: [
|
|
96
|
-
'subscriptions.*'
|
|
97
|
-
]
|
|
98
|
-
}
|
|
99
|
-
],
|
|
100
|
-
[
|
|
101
|
-
'a non-function access value',
|
|
102
|
-
{
|
|
103
|
-
access: true,
|
|
104
|
-
allowedMethods: [
|
|
105
|
-
'subscriptions.list'
|
|
106
|
-
]
|
|
107
|
-
}
|
|
108
|
-
]
|
|
109
|
-
])('should reject %s', (_description, rest)=>{
|
|
110
|
-
expect(()=>sanitizeStripeRESTConfig({
|
|
111
|
-
rest
|
|
112
|
-
})).toThrow('The Stripe REST endpoint now requires an object with a non-empty allowedMethods array');
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
//# sourceMappingURL=sanitizeStripeRESTConfig.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/sanitizeStripeRESTConfig.spec.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest'\n\nimport { sanitizeStripeRESTConfig } from './sanitizeStripeRESTConfig.js'\n\ndescribe('sanitizeStripeRESTConfig', () => {\n it('should disable the REST endpoint when configuration is absent', () => {\n expect(sanitizeStripeRESTConfig({ rest: undefined })).toBeUndefined()\n })\n\n it('should treat false from an untyped configuration as disabled', () => {\n expect(sanitizeStripeRESTConfig({ rest: false })).toBeUndefined()\n })\n\n it('should return a new configuration with exact, de-duplicated, frozen methods', () => {\n const access = vi.fn(() => true)\n const rest = {\n access,\n allowedMethods: ['subscriptions.list', ' paymentIntents.retrieve ', 'subscriptions.list'],\n } as const\n\n const sanitized = sanitizeStripeRESTConfig({ rest })\n\n expect(sanitized).not.toBe(rest)\n expect(sanitized).toEqual({\n access,\n allowedMethods: ['subscriptions.list', ' paymentIntents.retrieve '],\n })\n expect(Object.isFrozen(sanitized.allowedMethods)).toBe(true)\n })\n\n it.each([\n ['the legacy boolean form', true],\n ['an array', [{ allowedMethods: ['subscriptions.list'] }]],\n ['a missing allowlist', {}],\n ['a non-array allowlist', { allowedMethods: 'subscriptions.list' }],\n ['an empty allowlist', { allowedMethods: [] }],\n ['an empty method', { allowedMethods: [''] }],\n ['a whitespace-only method', { allowedMethods: [' '] }],\n ['a non-string method', { allowedMethods: [123] }],\n ['a wildcard method', { allowedMethods: ['subscriptions.*'] }],\n ['a non-function access value', { access: true, allowedMethods: ['subscriptions.list'] }],\n ])('should reject %s', (_description, rest) => {\n expect(() => sanitizeStripeRESTConfig({ rest })).toThrow(\n 'The Stripe REST endpoint now requires an object with a non-empty allowedMethods array',\n )\n })\n})\n"],"names":["describe","expect","it","vi","sanitizeStripeRESTConfig","rest","undefined","toBeUndefined","access","fn","allowedMethods","sanitized","not","toBe","toEqual","Object","isFrozen","each","_description","toThrow"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAEjD,SAASC,wBAAwB,QAAQ,gCAA+B;AAExEJ,SAAS,4BAA4B;IACnCE,GAAG,iEAAiE;QAClED,OAAOG,yBAAyB;YAAEC,MAAMC;QAAU,IAAIC,aAAa;IACrE;IAEAL,GAAG,gEAAgE;QACjED,OAAOG,yBAAyB;YAAEC,MAAM;QAAM,IAAIE,aAAa;IACjE;IAEAL,GAAG,+EAA+E;QAChF,MAAMM,SAASL,GAAGM,EAAE,CAAC,IAAM;QAC3B,MAAMJ,OAAO;YACXG;YACAE,gBAAgB;gBAAC;gBAAsB;gBAA6B;aAAqB;QAC3F;QAEA,MAAMC,YAAYP,yBAAyB;YAAEC;QAAK;QAElDJ,OAAOU,WAAWC,GAAG,CAACC,IAAI,CAACR;QAC3BJ,OAAOU,WAAWG,OAAO,CAAC;YACxBN;YACAE,gBAAgB;gBAAC;gBAAsB;aAA4B;QACrE;QACAT,OAAOc,OAAOC,QAAQ,CAACL,UAAUD,cAAc,GAAGG,IAAI,CAAC;IACzD;IAEAX,GAAGe,IAAI,CAAC;QACN;YAAC;YAA2B;SAAK;QACjC;YAAC;YAAY;gBAAC;oBAAEP,gBAAgB;wBAAC;qBAAqB;gBAAC;aAAE;SAAC;QAC1D;YAAC;YAAuB,CAAC;SAAE;QAC3B;YAAC;YAAyB;gBAAEA,gBAAgB;YAAqB;SAAE;QACnE;YAAC;YAAsB;gBAAEA,gBAAgB,EAAE;YAAC;SAAE;QAC9C;YAAC;YAAmB;gBAAEA,gBAAgB;oBAAC;iBAAG;YAAC;SAAE;QAC7C;YAAC;YAA4B;gBAAEA,gBAAgB;oBAAC;iBAAM;YAAC;SAAE;QACzD;YAAC;YAAuB;gBAAEA,gBAAgB;oBAAC;iBAAI;YAAC;SAAE;QAClD;YAAC;YAAqB;gBAAEA,gBAAgB;oBAAC;iBAAkB;YAAC;SAAE;QAC9D;YAAC;YAA+B;gBAAEF,QAAQ;gBAAME,gBAAgB;oBAAC;iBAAqB;YAAC;SAAE;KAC1F,EAAE,oBAAoB,CAACQ,cAAcb;QACpCJ,OAAO,IAAMG,yBAAyB;gBAAEC;YAAK,IAAIc,OAAO,CACtD;IAEJ;AACF"}
|