后续版本:4.38.0
在我的node.js Web应用程序中。
我编写模型规格并运行npm测试。
在Create a model with associated data using set()
单元测试中。
但出现以下错误:
TypeError: hooks.concat is not a function
at Function.runHooks (node_modules/sequelize/lib/hooks.js:102:23)
at Promise.try.then (node_modules/sequelize/lib/model.js:3624:33)
at tryCatcher (node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (node_modules/bluebird/js/release/promise.js:512:31)
at Promise._settlePromise (node_modules/bluebird/js/release/promise.js:569:18)
at Promise._settlePromise0 (node_modules/bluebird/js/release/promise.js:614:10)
at Promise._settlePromises (node_modules/bluebird/js/release/promise.js:693:18)
at Async._drainQueue (node_modules/bluebird/js/release/async.js:133:16)
at Async._drainQueues (node_modules/bluebird/js/release/async.js:143:10)
at Immediate.Async.drainQueues (node_modules/bluebird/js/release/async.js:17:14)
以下是我的单元测试代码:
it('Create a model with associated data using set()', async () => {
// TODO:
// 1. create a User and use set() method to set 1 associated model data.
// 2. use model.findOne() with include to get model data with associated data.
// 3. use data as Passport data, use `fakeData.create3` as user data.
const data = {
token: '1',
workspaceName: 'ws1',
passwordHash: 'ws1ws1ws1',
provider: 'local',
};
let user = await models[SPEC_MODEL_NAME].create({
...fakeData.create3,
Passports: data
}, {
include: [models.Passport]
});
const passport = await models.Passport.create(data);
await user.setPassports(passport);
const userWithPassport = await models[SPEC_MODEL_NAME].findOne(
{
where: {
nickName: fakeData.create3.nickName
},
include: [models.Passport],
}
);
expect(userWithPassport.nickName).to.be.equal(fakeData.create3.nickName);
expect(userWithPassport.email).to.be.equal(fakeData.create3.email);
expect(userWithPassport.Passports.length).to.equal(1);
expect(userWithPassport.Passports[0].token).to.equal(data.token);
expect(userWithPassport.Passports[0].workspaceName).to.equal(data.workspaceName);
expect(userWithPassport.Passports[0].passwordHash).to.equal(data.passwordHash);
});
});
以下是我的用户模型:
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
nickName: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
});
User.associate = function (models) {
User.hasMany(models.UserOrder);
User.hasMany(models.Passport);
};
return User;
};
以下是我的护照型号:
const bcrypt = require('bcrypt');
const crypto = require('crypto');
module.exports = (sequelize, DataTypes) => {
const Passport = sequelize.define('Passport', {
provider: {
type: DataTypes.STRING,
allowNull: false,
},
token: {
type: DataTypes.STRING,
allowNull: true,
},
password: {
type: DataTypes.STRING,
allowNull: true,
},
workspaceName: {
type: DataTypes.STRING,
allowNull: true,
},
});
Passport.associate = function (models) {
Passport.belongsTo(models.User);
};
Passport.options.classMethod = {
hashPassword: async (passport) => {
// eslint-disable-next-line
await new Promise((defer, reject) => {
if (passport.passwordHash) {
bcrypt.hash(passport.passwordHash, 10, (err, hash) => {
if (err) reject(err);
// eslint-disable-next-line
passport.passwordHash = hash;
defer();
});
}
defer();
});
},
async createDefaultLocalProviderIfNotExist (user) {
try {
const localPassport = await Passport.findOne({
where: {
provider: 'local',
userId: user.id,
},
});
console.log('localPassport ==', localPassport);
if (localPassport == null) {
const newLocalPassport = {
provider: 'local',
password: 'password',
userId: user.id,
};
console.log('=== newLocalPassport ===', newLocalPassport);
await Passport.create(newLocalPassport);
}
} catch (e) {
throw e;
}
},
};
Passport.options.instanceMethod = {
async validatePassword (password) {
try {
const that = this;
// eslint-disable-next-line
let result = await new Promise((defer, reject) => {
if (password === that.password) {
defer(true);
}
// eslint-disable-next-line
bcrypt.compare(password, that.password, (err, result) => {
if (err) defer(false);
else defer(result);
});
});
if (result) return result;
console.log('=== this.salt ===', that.salt);
console.log('=== this.salt ===', result);
if (!this.salt) return result;
console.log('=== check two ===');
const comparePassword = crypto.pbkdf2Sync(password, Buffer.from(this.salt, 'base64'), 10000, 64).toString('base64');
if (comparePassword === that.password) {
result = true;
}
return result;
} catch (e) {
throw e;
}
},
};
Passport.options.hooks = {
async beforeCreate (passport) {
return new Promise(async (resolve, reject) => {
try {
await Passport.hashPassword(passport);
return resolve(passport);
} catch (e) {
return reject(e);
}
});
},
async beforeUpdate (passport) {
return new Promise(async (resolve, reject) => {
try {
await Passport.hashPassword(passport);
return resolve(passport);
} catch (e) {
return reject(e);
}
});
},
};
return Passport;
};
以下是我的userOrder模型:
module.exports = (sequelize, DataTypes) => {
const UserOrder = sequelize.define('UserOrder', {
price: {
type: DataTypes.INTEGER,
allowNull: false,
},
count: {
type: DataTypes.INTEGER,
allowNull: false,
},
subTotal: {
type: DataTypes.INTEGER,
allowNull: false,
},
remark: DataTypes.STRING,
});
UserOrder.associate = function (models) {
UserOrder.belongsTo(models.User);
UserOrder.belongsTo(models.GroupOrder);
UserOrder.belongsTo(models.Food);
};
return UserOrder;
};
谢谢!
答案 0 :(得分:1)
查看文档中的示例并稍微看一下源代码,我认为Sequelize希望您在调用define
时传递完整的选项对象。为Passport
模型创建一个选项对象,并在将其作为第二个参数传递给define
调用之前,将钩子分配给它(而不是在定义后修改选项)。这将允许它在初始化模型时正确检查该选项对象。