@budibase/worker 1.2.58-alpha.4 → 1.2.58
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 +6 -7
- package/scripts/jestSetup.js +0 -6
- package/scripts/load/users.js +97 -0
- package/src/api/controllers/global/auth.ts +1 -14
- package/src/api/controllers/global/users.ts +8 -6
- package/src/api/{index.ts → index.js} +16 -12
- package/src/api/routes/global/configs.js +0 -1
- package/src/api/routes/index.js +0 -2
- package/src/api/routes/{global/tests/auth.spec.ts → tests/auth.spec.js} +56 -32
- package/src/api/routes/{global/tests/configs.spec.ts → tests/configs.spec.js} +47 -76
- package/src/api/routes/{global/tests/email.spec.ts → tests/email.spec.js} +15 -7
- package/src/api/routes/{global/tests/realEmail.spec.ts → tests/realEmail.spec.js} +21 -20
- package/src/api/routes/{global/tests/self.spec.ts → tests/self.spec.js} +18 -10
- package/src/api/routes/tests/users.spec.js +390 -0
- package/src/{environment.ts → environment.js} +9 -10
- package/src/index.ts +4 -6
- package/src/sdk/index.ts +0 -1
- package/src/sdk/users/users.ts +77 -217
- package/src/tests/TestConfiguration.js +231 -0
- package/src/tests/index.js +12 -0
- package/src/tests/mocks/index.js +5 -0
- package/src/tests/structures/index.js +14 -0
- package/src/tests/structures/users.ts +4 -12
- package/src/utilities/redis.js +0 -1
- package/src/api/controllers/system/accounts.ts +0 -21
- package/src/api/routes/global/tests/users.spec.ts +0 -464
- package/src/api/routes/system/accounts.ts +0 -19
- package/src/api/routes/system/tests/accounts.spec.ts +0 -57
- package/src/sdk/accounts/accounts.ts +0 -53
- package/src/sdk/accounts/index.ts +0 -1
- package/src/tests/TestConfiguration.ts +0 -273
- package/src/tests/api/accounts.ts +0 -28
- package/src/tests/api/auth.ts +0 -48
- package/src/tests/api/configs.ts +0 -40
- package/src/tests/api/email.ts +0 -24
- package/src/tests/api/index.ts +0 -25
- package/src/tests/api/self.ts +0 -21
- package/src/tests/api/users.ts +0 -95
- package/src/tests/index.ts +0 -14
- package/src/tests/mocks/index.ts +0 -7
- package/src/tests/structures/accounts.ts +0 -24
- package/src/tests/structures/index.ts +0 -20
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/worker",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "1.2.58
|
|
4
|
+
"version": "1.2.58",
|
|
5
5
|
"description": "Budibase background service",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"author": "Budibase",
|
|
36
36
|
"license": "GPL-3.0",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@budibase/backend-core": "1.2.58
|
|
39
|
-
"@budibase/pro": "1.2.
|
|
40
|
-
"@budibase/string-templates": "1.2.58
|
|
41
|
-
"@budibase/types": "1.2.58
|
|
38
|
+
"@budibase/backend-core": "^1.2.58",
|
|
39
|
+
"@budibase/pro": "1.2.57",
|
|
40
|
+
"@budibase/string-templates": "^1.2.58",
|
|
41
|
+
"@budibase/types": "^1.2.58",
|
|
42
42
|
"@koa/router": "8.0.8",
|
|
43
43
|
"@sentry/node": "6.17.7",
|
|
44
44
|
"@techpass/passport-openidconnect": "0.3.2",
|
|
@@ -74,7 +74,6 @@
|
|
|
74
74
|
"@types/koa-router": "7.4.4",
|
|
75
75
|
"@types/koa__router": "8.0.11",
|
|
76
76
|
"@types/node": "14.18.20",
|
|
77
|
-
"@types/uuid": "8.3.4",
|
|
78
77
|
"@typescript-eslint/parser": "5.12.0",
|
|
79
78
|
"copyfiles": "2.4.1",
|
|
80
79
|
"eslint": "6.8.0",
|
|
@@ -103,5 +102,5 @@
|
|
|
103
102
|
"./scripts/jestSetup.js"
|
|
104
103
|
]
|
|
105
104
|
},
|
|
106
|
-
"gitHead": "
|
|
105
|
+
"gitHead": "2922b47620173f41097d36b9dc5b1c0f8f7b063a"
|
|
107
106
|
}
|
package/scripts/jestSetup.js
CHANGED
|
@@ -14,9 +14,3 @@ const tk = require("timekeeper")
|
|
|
14
14
|
tk.freeze(mocks.date.MOCK_DATE)
|
|
15
15
|
|
|
16
16
|
global.console.log = jest.fn() // console.log are ignored in tests
|
|
17
|
-
|
|
18
|
-
if (!process.env.CI) {
|
|
19
|
-
// set a longer timeout in dev for debugging
|
|
20
|
-
// 100 seconds
|
|
21
|
-
jest.setTimeout(100000)
|
|
22
|
-
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// get the JWT secret etc
|
|
2
|
+
require("../../src/environment")
|
|
3
|
+
require("@budibase/backend-core").init()
|
|
4
|
+
const {
|
|
5
|
+
getProdAppID,
|
|
6
|
+
generateGlobalUserID,
|
|
7
|
+
} = require("@budibase/backend-core/db")
|
|
8
|
+
const { doInTenant, getGlobalDB } = require("@budibase/backend-core/tenancy")
|
|
9
|
+
const users = require("../../src/sdk/users")
|
|
10
|
+
const { publicApiUserFix } = require("../../src/utilities/users")
|
|
11
|
+
const { hash } = require("@budibase/backend-core/utils")
|
|
12
|
+
|
|
13
|
+
const USER_LOAD_NUMBER = 10000
|
|
14
|
+
const BATCH_SIZE = 200
|
|
15
|
+
const PASSWORD = "test"
|
|
16
|
+
const TENANT_ID = "default"
|
|
17
|
+
|
|
18
|
+
const APP_ID = process.argv[2]
|
|
19
|
+
|
|
20
|
+
const words = [
|
|
21
|
+
"test",
|
|
22
|
+
"testing",
|
|
23
|
+
"budi",
|
|
24
|
+
"mail",
|
|
25
|
+
"age",
|
|
26
|
+
"risk",
|
|
27
|
+
"load",
|
|
28
|
+
"uno",
|
|
29
|
+
"arm",
|
|
30
|
+
"leg",
|
|
31
|
+
"pen",
|
|
32
|
+
"glass",
|
|
33
|
+
"box",
|
|
34
|
+
"chicken",
|
|
35
|
+
"bottle",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
if (!APP_ID) {
|
|
39
|
+
console.error("Must supply app ID as first CLI option!")
|
|
40
|
+
process.exit(-1)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const WORD_1 = words[Math.floor(Math.random() * words.length)]
|
|
44
|
+
const WORD_2 = words[Math.floor(Math.random() * words.length)]
|
|
45
|
+
let HASHED_PASSWORD
|
|
46
|
+
|
|
47
|
+
function generateUser(count) {
|
|
48
|
+
return {
|
|
49
|
+
_id: generateGlobalUserID(),
|
|
50
|
+
password: HASHED_PASSWORD,
|
|
51
|
+
email: `${WORD_1}${count}@${WORD_2}.com`,
|
|
52
|
+
roles: {
|
|
53
|
+
[getProdAppID(APP_ID)]: "BASIC",
|
|
54
|
+
},
|
|
55
|
+
status: "active",
|
|
56
|
+
forceResetPassword: false,
|
|
57
|
+
firstName: "John",
|
|
58
|
+
lastName: "Smith",
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function run() {
|
|
63
|
+
HASHED_PASSWORD = await hash(PASSWORD)
|
|
64
|
+
return doInTenant(TENANT_ID, async () => {
|
|
65
|
+
const db = getGlobalDB()
|
|
66
|
+
for (let i = 0; i < USER_LOAD_NUMBER; i += BATCH_SIZE) {
|
|
67
|
+
let userSavePromises = []
|
|
68
|
+
for (let j = 0; j < BATCH_SIZE; j++) {
|
|
69
|
+
// like the public API
|
|
70
|
+
const ctx = publicApiUserFix({
|
|
71
|
+
request: {
|
|
72
|
+
body: generateUser(i + j),
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
userSavePromises.push(
|
|
76
|
+
users.save(ctx.request.body, {
|
|
77
|
+
hashPassword: false,
|
|
78
|
+
requirePassword: true,
|
|
79
|
+
bulkCreate: true,
|
|
80
|
+
})
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
const allUsers = await Promise.all(userSavePromises)
|
|
84
|
+
await db.bulkDocs(allUsers)
|
|
85
|
+
console.log(`${i + BATCH_SIZE} users have been created.`)
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
run()
|
|
91
|
+
.then(() => {
|
|
92
|
+
console.log(`Generated ${USER_LOAD_NUMBER} users!`)
|
|
93
|
+
})
|
|
94
|
+
.catch(err => {
|
|
95
|
+
console.error("Failed for reason: ", err)
|
|
96
|
+
process.exit(-1)
|
|
97
|
+
})
|
|
@@ -227,22 +227,9 @@ export const oidcPreAuth = async (ctx: any, next: any) => {
|
|
|
227
227
|
|
|
228
228
|
setCookie(ctx, configId, Cookies.OIDC_CONFIG)
|
|
229
229
|
|
|
230
|
-
const db = getGlobalDB()
|
|
231
|
-
const config = await core.db.getScopedConfig(db, {
|
|
232
|
-
type: Configs.OIDC,
|
|
233
|
-
group: ctx.query.group,
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
|
|
237
|
-
|
|
238
|
-
let authScopes =
|
|
239
|
-
chosenConfig.scopes?.length > 0
|
|
240
|
-
? chosenConfig.scopes
|
|
241
|
-
: ["profile", "email", "offline_access"]
|
|
242
|
-
|
|
243
230
|
return passport.authenticate(strategy, {
|
|
244
231
|
// required 'openid' scope is added by oidc strategy factory
|
|
245
|
-
scope:
|
|
232
|
+
scope: ["profile", "email", "offline_access"], //auth0 offline_access scope required for the refresh token behaviour.
|
|
246
233
|
})(ctx, next)
|
|
247
234
|
}
|
|
248
235
|
|
|
@@ -3,7 +3,7 @@ import { checkInviteCode } from "../../../utilities/redis"
|
|
|
3
3
|
import { sendEmail } from "../../../utilities/email"
|
|
4
4
|
import { users } from "../../../sdk"
|
|
5
5
|
import env from "../../../environment"
|
|
6
|
-
import {
|
|
6
|
+
import { CloudAccount, User } from "@budibase/types"
|
|
7
7
|
import {
|
|
8
8
|
accounts,
|
|
9
9
|
cache,
|
|
@@ -46,8 +46,8 @@ export const bulkCreate = async (ctx: any) => {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
try {
|
|
49
|
-
|
|
50
|
-
await groupUtils.bulkSaveGroupUsers(groupsToSave, response
|
|
49
|
+
let response = await users.bulkCreate(newUsersRequested, groups)
|
|
50
|
+
await groupUtils.bulkSaveGroupUsers(groupsToSave, response)
|
|
51
51
|
|
|
52
52
|
ctx.body = response
|
|
53
53
|
} catch (err: any) {
|
|
@@ -138,15 +138,17 @@ export const destroy = async (ctx: any) => {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
export const bulkDelete = async (ctx: any) => {
|
|
141
|
-
const { userIds } = ctx.request.body
|
|
141
|
+
const { userIds } = ctx.request.body
|
|
142
142
|
if (userIds?.indexOf(ctx.user._id) !== -1) {
|
|
143
143
|
ctx.throw(400, "Unable to delete self.")
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
try {
|
|
147
|
-
let
|
|
147
|
+
let usersResponse = await users.bulkDelete(userIds)
|
|
148
148
|
|
|
149
|
-
ctx.body =
|
|
149
|
+
ctx.body = {
|
|
150
|
+
message: `${usersResponse.length} user(s) deleted`,
|
|
151
|
+
}
|
|
150
152
|
} catch (err) {
|
|
151
153
|
ctx.throw(err)
|
|
152
154
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
const Router = require("@koa/router")
|
|
2
2
|
const compress = require("koa-compress")
|
|
3
3
|
const zlib = require("zlib")
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const { routes } = require("./routes")
|
|
5
|
+
const {
|
|
6
|
+
buildAuthMiddleware,
|
|
7
|
+
auditLog,
|
|
8
|
+
buildTenancyMiddleware,
|
|
9
|
+
buildCsrfMiddleware,
|
|
10
|
+
} = require("@budibase/backend-core/auth")
|
|
11
|
+
const { middleware: pro } = require("@budibase/pro")
|
|
12
|
+
const { errors } = require("@budibase/backend-core")
|
|
8
13
|
|
|
9
14
|
const PUBLIC_ENDPOINTS = [
|
|
10
15
|
// old deprecated endpoints kept for backwards compat
|
|
@@ -92,9 +97,9 @@ router
|
|
|
92
97
|
})
|
|
93
98
|
)
|
|
94
99
|
.use("/health", ctx => (ctx.status = 200))
|
|
95
|
-
.use(
|
|
96
|
-
.use(
|
|
97
|
-
.use(
|
|
100
|
+
.use(buildAuthMiddleware(PUBLIC_ENDPOINTS))
|
|
101
|
+
.use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS))
|
|
102
|
+
.use(buildCsrfMiddleware({ noCsrfPatterns: NO_CSRF_ENDPOINTS }))
|
|
98
103
|
.use(pro.licensing())
|
|
99
104
|
// for now no public access is allowed to worker (bar health check)
|
|
100
105
|
.use((ctx, next) => {
|
|
@@ -109,22 +114,21 @@ router
|
|
|
109
114
|
}
|
|
110
115
|
return next()
|
|
111
116
|
})
|
|
112
|
-
.use(
|
|
117
|
+
.use(auditLog)
|
|
113
118
|
|
|
114
119
|
// error handling middleware - TODO: This could be moved to backend-core
|
|
115
120
|
router.use(async (ctx, next) => {
|
|
116
121
|
try {
|
|
117
122
|
await next()
|
|
118
|
-
} catch (err
|
|
123
|
+
} catch (err) {
|
|
119
124
|
ctx.log.error(err)
|
|
120
125
|
ctx.status = err.status || err.statusCode || 500
|
|
121
126
|
const error = errors.getPublicError(err)
|
|
122
|
-
|
|
127
|
+
ctx.body = {
|
|
123
128
|
message: err.message,
|
|
124
129
|
status: ctx.status,
|
|
125
130
|
error,
|
|
126
131
|
}
|
|
127
|
-
ctx.body = body
|
|
128
132
|
}
|
|
129
133
|
})
|
|
130
134
|
|
package/src/api/routes/index.js
CHANGED
|
@@ -12,7 +12,6 @@ const statusRoutes = require("./system/status")
|
|
|
12
12
|
const selfRoutes = require("./global/self")
|
|
13
13
|
const licenseRoutes = require("./global/license")
|
|
14
14
|
const migrationRoutes = require("./system/migrations")
|
|
15
|
-
const accountRoutes = require("./system/accounts")
|
|
16
15
|
|
|
17
16
|
let userGroupRoutes = api.groups
|
|
18
17
|
exports.routes = [
|
|
@@ -30,5 +29,4 @@ exports.routes = [
|
|
|
30
29
|
licenseRoutes,
|
|
31
30
|
userGroupRoutes,
|
|
32
31
|
migrationRoutes,
|
|
33
|
-
accountRoutes,
|
|
34
32
|
]
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
jest.mock("nodemailer")
|
|
2
|
-
|
|
2
|
+
const { config, request, mocks, structures } = require("../../../tests")
|
|
3
3
|
const sendMailMock = mocks.email.mock()
|
|
4
|
-
|
|
4
|
+
const { events } = require("@budibase/backend-core")
|
|
5
|
+
|
|
6
|
+
const TENANT_ID = structures.TENANT_ID
|
|
5
7
|
|
|
6
8
|
describe("/api/global/auth", () => {
|
|
7
|
-
const config = new TestConfiguration()
|
|
8
|
-
const api = new API(config)
|
|
9
9
|
|
|
10
10
|
beforeAll(async () => {
|
|
11
11
|
await config.beforeAll()
|
|
@@ -19,32 +19,56 @@ describe("/api/global/auth", () => {
|
|
|
19
19
|
jest.clearAllMocks()
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
+
const requestPasswordReset = async () => {
|
|
23
|
+
await config.saveSmtpConfig()
|
|
24
|
+
await config.saveSettingsConfig()
|
|
25
|
+
await config.createUser()
|
|
26
|
+
const res = await request
|
|
27
|
+
.post(`/api/global/auth/${TENANT_ID}/reset`)
|
|
28
|
+
.send({
|
|
29
|
+
email: "test@test.com",
|
|
30
|
+
})
|
|
31
|
+
.expect("Content-Type", /json/)
|
|
32
|
+
.expect(200)
|
|
33
|
+
const emailCall = sendMailMock.mock.calls[0][0]
|
|
34
|
+
const parts = emailCall.html.split(`http://localhost:10000/builder/auth/reset?code=`)
|
|
35
|
+
const code = parts[1].split("\"")[0].split("&")[0]
|
|
36
|
+
return { code, res }
|
|
37
|
+
}
|
|
38
|
+
|
|
22
39
|
it("should logout", async () => {
|
|
23
|
-
await
|
|
40
|
+
await request
|
|
41
|
+
.post("/api/global/auth/logout")
|
|
42
|
+
.set(config.defaultHeaders())
|
|
43
|
+
.expect(200)
|
|
24
44
|
expect(events.auth.logout).toBeCalledTimes(1)
|
|
25
45
|
})
|
|
26
46
|
|
|
27
47
|
it("should be able to generate password reset email", async () => {
|
|
28
|
-
const { res, code } = await
|
|
48
|
+
const { res, code } = await requestPasswordReset()
|
|
29
49
|
const user = await config.getUser("test@test.com")
|
|
30
50
|
|
|
31
|
-
expect(res.body).toEqual({
|
|
32
|
-
message: "Please check your email for a reset link.",
|
|
33
|
-
})
|
|
51
|
+
expect(res.body).toEqual({ message: "Please check your email for a reset link." })
|
|
34
52
|
expect(sendMailMock).toHaveBeenCalled()
|
|
35
|
-
|
|
53
|
+
|
|
36
54
|
expect(code).toBeDefined()
|
|
37
55
|
expect(events.user.passwordResetRequested).toBeCalledTimes(1)
|
|
38
56
|
expect(events.user.passwordResetRequested).toBeCalledWith(user)
|
|
39
57
|
})
|
|
40
58
|
|
|
41
59
|
it("should allow resetting user password with code", async () => {
|
|
42
|
-
const { code } = await
|
|
60
|
+
const { code } = await requestPasswordReset()
|
|
43
61
|
const user = await config.getUser("test@test.com")
|
|
44
|
-
delete user.password
|
|
45
|
-
|
|
46
|
-
const res = await api.auth.updatePassword(code)
|
|
62
|
+
delete user.password
|
|
47
63
|
|
|
64
|
+
const res = await request
|
|
65
|
+
.post(`/api/global/auth/${TENANT_ID}/reset/update`)
|
|
66
|
+
.send({
|
|
67
|
+
password: "newpassword",
|
|
68
|
+
resetCode: code,
|
|
69
|
+
})
|
|
70
|
+
.expect("Content-Type", /json/)
|
|
71
|
+
.expect(200)
|
|
48
72
|
expect(res.body).toEqual({ message: "password reset successfully." })
|
|
49
73
|
expect(events.user.passwordReset).toBeCalledTimes(1)
|
|
50
74
|
expect(events.user.passwordReset).toBeCalledWith(user)
|
|
@@ -55,15 +79,15 @@ describe("/api/global/auth", () => {
|
|
|
55
79
|
|
|
56
80
|
const passportSpy = jest.spyOn(auth.passport, "authenticate")
|
|
57
81
|
let oidcConf
|
|
58
|
-
let chosenConfig
|
|
59
|
-
let configId
|
|
82
|
+
let chosenConfig
|
|
83
|
+
let configId
|
|
60
84
|
|
|
61
85
|
// mock the oidc strategy implementation and return value
|
|
62
86
|
let strategyFactory = jest.fn()
|
|
63
87
|
let mockStrategyReturn = jest.fn()
|
|
64
88
|
let mockStrategyConfig = jest.fn()
|
|
65
89
|
auth.oidc.fetchStrategyConfig = mockStrategyConfig
|
|
66
|
-
|
|
90
|
+
|
|
67
91
|
strategyFactory.mockReturnValue(mockStrategyReturn)
|
|
68
92
|
auth.oidc.strategyFactory = strategyFactory
|
|
69
93
|
|
|
@@ -75,34 +99,34 @@ describe("/api/global/auth", () => {
|
|
|
75
99
|
})
|
|
76
100
|
|
|
77
101
|
afterEach(() => {
|
|
78
|
-
expect(strategyFactory).toBeCalledWith(
|
|
102
|
+
expect(strategyFactory).toBeCalledWith(
|
|
103
|
+
chosenConfig,
|
|
104
|
+
expect.any(Function)
|
|
105
|
+
)
|
|
79
106
|
})
|
|
80
107
|
|
|
81
108
|
describe("oidc configs", () => {
|
|
82
109
|
it("should load strategy and delegate to passport", async () => {
|
|
83
|
-
await api
|
|
110
|
+
await request.get(`/api/global/auth/${TENANT_ID}/oidc/configs/${configId}`)
|
|
84
111
|
|
|
85
112
|
expect(passportSpy).toBeCalledWith(mockStrategyReturn, {
|
|
86
|
-
scope: ["profile", "email", "offline_access"]
|
|
113
|
+
scope: ["profile", "email", "offline_access"]
|
|
87
114
|
})
|
|
88
|
-
expect(passportSpy.mock.calls.length).toBe(1)
|
|
115
|
+
expect(passportSpy.mock.calls.length).toBe(1);
|
|
89
116
|
})
|
|
90
117
|
})
|
|
91
118
|
|
|
92
119
|
describe("oidc callback", () => {
|
|
93
120
|
it("should load strategy and delegate to passport", async () => {
|
|
94
|
-
await
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
},
|
|
102
|
-
expect.anything()
|
|
103
|
-
)
|
|
104
|
-
expect(passportSpy.mock.calls.length).toBe(1)
|
|
121
|
+
await request.get(`/api/global/auth/${TENANT_ID}/oidc/callback`)
|
|
122
|
+
.set(config.getOIDConfigCookie(configId))
|
|
123
|
+
|
|
124
|
+
expect(passportSpy).toBeCalledWith(mockStrategyReturn, {
|
|
125
|
+
successRedirect: "/", failureRedirect: "/error"
|
|
126
|
+
}, expect.anything())
|
|
127
|
+
expect(passportSpy.mock.calls.length).toBe(1);
|
|
105
128
|
})
|
|
106
129
|
})
|
|
130
|
+
|
|
107
131
|
})
|
|
108
132
|
})
|