f98467611c
no issue Collection cards contain dynamic data that can change when there's any change to a published post but in Ghost all post/page content is rendered once on save and stored as a static string meaning we need a new approach for triggering a re-render of pages that plays well with caching. - fixed typo in the relations/authors code that meant we weren't correctly calling the prototype method on the Post model inside the `onFetchedCollection` event handler - updated Post model to clear the `html` field of all pages when saving or deleting a published post - updated Post model to re-render `html` fields when fetching individual posts or a collection of posts - modified `insertExtraPostsTags` fixture util to wrap it's concurrent post edits in a transaction otherwise MySQL errors because it hits a deadlock
934 lines
35 KiB
JavaScript
934 lines
35 KiB
JavaScript
// Utility Packages
|
|
const _ = require('lodash');
|
|
const path = require('path');
|
|
const fs = require('fs-extra');
|
|
const uuid = require('uuid');
|
|
const ObjectId = require('bson-objectid').default;
|
|
const KnexMigrator = require('knex-migrator');
|
|
const {sequence} = require('@tryghost/promise');
|
|
const knexMigrator = new KnexMigrator();
|
|
|
|
// Ghost Internals
|
|
const models = require('../../core/server/models');
|
|
const {fixtureManager} = require('../../core/server/data/schema/fixtures');
|
|
const emailAnalyticsService = require('../../core/server/services/email-analytics');
|
|
const permissions = require('../../core/server/services/permissions');
|
|
const settingsService = require('../../core/server/services/settings/settings-service');
|
|
const labsService = require('../../core/shared/labs');
|
|
|
|
// Other Test Utilities
|
|
const context = require('./fixtures/context');
|
|
const DataGenerator = require('./fixtures/data-generator');
|
|
const filterData = require('./fixtures/filter-param');
|
|
|
|
let postsInserted = 0;
|
|
|
|
/** TEST FIXTURES **/
|
|
const fixtures = {
|
|
insertPosts: function insertPosts(posts) {
|
|
const tasks = posts.map(post => () => {
|
|
return models.Post.add(post, context.internal);
|
|
});
|
|
|
|
return sequence(tasks);
|
|
},
|
|
|
|
insertPostsAndTags: function insertPostsAndTags() {
|
|
return Promise.all(DataGenerator.forKnex.tags.map((tag) => {
|
|
return models.Tag.add(tag, context.internal);
|
|
}))
|
|
.then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.posts).map(post => () => {
|
|
let postTagRelations = _.filter(DataGenerator.forKnex.posts_tags, {post_id: post.id});
|
|
let postAuthorsRelations = _.filter(DataGenerator.forKnex.posts_authors, {post_id: post.id});
|
|
|
|
postTagRelations = _.map(postTagRelations, function (postTagRelation) {
|
|
return _.find(DataGenerator.forKnex.tags, {id: postTagRelation.tag_id});
|
|
});
|
|
|
|
postAuthorsRelations = _.map(postAuthorsRelations, function (postAuthorsRelation) {
|
|
return _.find(DataGenerator.forKnex.users, {id: postAuthorsRelation.author_id});
|
|
});
|
|
|
|
post.tags = postTagRelations;
|
|
post.authors = postAuthorsRelations;
|
|
|
|
return models.Post.add(post, context.internal);
|
|
}));
|
|
})
|
|
.then(function () {
|
|
return Promise.all(DataGenerator.forKnex.posts_meta.map((postMeta) => {
|
|
return models.PostsMeta.add(postMeta, context.internal);
|
|
}));
|
|
});
|
|
},
|
|
|
|
insertMultiAuthorPosts: function insertMultiAuthorPosts() {
|
|
let i;
|
|
let k = 0;
|
|
let posts = [];
|
|
|
|
// NOTE: this variable should become a parameter as test logic depends on it
|
|
const count = 10;
|
|
|
|
// insert users of different roles
|
|
return Promise.resolve(fixtures.createUsersWithRoles()).then(function () {
|
|
return Promise.all(DataGenerator.forKnex.tags.map((tag) => {
|
|
return models.Tag.add(tag, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
return Promise.all([
|
|
models.User.fetchAll(_.merge({columns: ['id']}, context.internal)),
|
|
models.Tag.fetchAll(_.merge({columns: ['id']}, context.internal))
|
|
]);
|
|
}).then(function (results) {
|
|
let users = results[0];
|
|
let tags = results[1];
|
|
|
|
tags = tags.toJSON();
|
|
|
|
users = users.toJSON();
|
|
users = _.map(users, 'id');
|
|
|
|
// Let's insert posts with random authors
|
|
for (i = 0; i < count; i += 1) {
|
|
const author = users[i % users.length];
|
|
posts.push(DataGenerator.forKnex.createGenericPost(k, null, null, [{id: author}]));
|
|
k = k + 1;
|
|
}
|
|
|
|
return Promise.all(posts.map((post, index) => {
|
|
posts[index].authors = [{id: posts[index].authors[0].id}];
|
|
posts[index].tags = [tags[Math.floor(Math.random() * (tags.length - 1))]];
|
|
return models.Post.add(posts[index], context.internal);
|
|
}));
|
|
});
|
|
},
|
|
|
|
insertEmailedPosts: function insertEmailedPosts({postCount = 2} = {}) {
|
|
const posts = [];
|
|
|
|
for (let i = 0; i < postCount; i++) {
|
|
posts.push(DataGenerator.forKnex.createGenericPost);
|
|
}
|
|
},
|
|
|
|
insertExtraPosts: function insertExtraPosts(max) {
|
|
let lang;
|
|
let status;
|
|
const posts = [];
|
|
let i;
|
|
let j;
|
|
let k = postsInserted;
|
|
|
|
max = max || 50;
|
|
|
|
for (i = 0; i < 2; i += 1) {
|
|
lang = i % 2 ? 'en' : 'fr';
|
|
posts.push(DataGenerator.forKnex.createGenericPost(k, null, lang));
|
|
k = k + 1;
|
|
|
|
for (j = 0; j < max; j += 1) {
|
|
status = j % 2 ? 'draft' : 'published';
|
|
posts.push(DataGenerator.forKnex.createGenericPost(k, status, lang));
|
|
k = k + 1;
|
|
}
|
|
}
|
|
|
|
// Keep track so we can run this function again safely
|
|
postsInserted = k;
|
|
|
|
return models.User.getOwnerUser(context.internal)
|
|
.then(function (ownerUser) {
|
|
return Promise.all(posts.map((post, index) => {
|
|
posts[index].authors = [ownerUser.toJSON()];
|
|
return models.Post.add(posts[index], context.internal);
|
|
}));
|
|
});
|
|
},
|
|
|
|
insertTags: function insertTags() {
|
|
return Promise.all(DataGenerator.forKnex.tags.map((tag) => {
|
|
return models.Tag.add(tag, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertExtraTags: function insertExtraTags(max) {
|
|
max = max || 50;
|
|
const tags = [];
|
|
let tagName;
|
|
let i;
|
|
|
|
for (i = 0; i < max; i += 1) {
|
|
tagName = uuid.v4().split('-')[0];
|
|
tags.push(DataGenerator.forKnex.createBasic({name: tagName, slug: tagName}));
|
|
}
|
|
|
|
return Promise.all(tags.map((tag, index) => {
|
|
return models.Tag.add(tags[index], context.internal);
|
|
}));
|
|
},
|
|
|
|
insertExtraPostsTags: function insertExtraPostsTags(max) {
|
|
max = max || 50;
|
|
|
|
return Promise.all([
|
|
models.Post.fetchAll(_.merge({columns: ['id'], withRelated: 'tags'}, context.internal)),
|
|
models.Tag.fetchAll(_.merge({columns: ['id', 'name']}, context.internal))
|
|
]).then(function (results) {
|
|
let posts = results[0].toJSON();
|
|
let tags = results[1].toJSON();
|
|
|
|
const injectionTagId = _.chain(tags)
|
|
.filter({name: 'injection'})
|
|
.map('id')
|
|
.value()[0];
|
|
|
|
if (max > posts.length) {
|
|
throw new Error('Trying to add more posts_tags than the number of posts.');
|
|
}
|
|
|
|
return models.Base.transaction((transacting) => {
|
|
return Promise.all(posts.slice(0, max).map((post) => {
|
|
post.tags = post.tags ? post.tags : [];
|
|
|
|
return models.Post.edit({
|
|
tags: post.tags.concat([_.find(DataGenerator.Content.tags, {id: injectionTagId})])
|
|
}, _.merge({id: post.id, transacting}, context.internal));
|
|
}));
|
|
});
|
|
});
|
|
},
|
|
|
|
insertRoles: function insertRoles() {
|
|
return Promise.all(DataGenerator.forKnex.roles.map((role) => {
|
|
return models.Role.add(role, context.internal);
|
|
}));
|
|
},
|
|
|
|
initOwnerUser: function initOwnerUser() {
|
|
let user = DataGenerator.Content.users[0];
|
|
|
|
user = DataGenerator.forKnex.createBasic(user);
|
|
user = _.extend({}, user, {status: 'inactive'});
|
|
|
|
return Promise.all(DataGenerator.forKnex.roles.map((role) => {
|
|
return models.Role.add(role, context.internal);
|
|
})).then(function () {
|
|
const userRolesRelation = _.cloneDeep(DataGenerator.forKnex.roles_users[0]);
|
|
user.roles = _.filter(DataGenerator.forKnex.roles, {id: userRolesRelation.role_id});
|
|
return models.User.add(user, context.internal);
|
|
});
|
|
},
|
|
|
|
insertOwnerUser: function insertOwnerUser() {
|
|
const user = _.cloneDeep(DataGenerator.forKnex.users[0]);
|
|
user.roles = [DataGenerator.forKnex.roles[3]];
|
|
return models.User.add(user, context.internal);
|
|
},
|
|
|
|
overrideOwnerUser: function overrideOwnerUser(slug) {
|
|
return models.User.getOwnerUser(context.internal)
|
|
.then(function (ownerUser) {
|
|
const user = DataGenerator.forKnex.createUser(DataGenerator.Content.users[0]);
|
|
|
|
if (slug) {
|
|
user.slug = slug;
|
|
}
|
|
|
|
return models.User.edit(user, _.merge({id: ownerUser.id}, context.internal));
|
|
});
|
|
},
|
|
|
|
changeOwnerUserStatus: function changeOwnerUserStatus(options) {
|
|
return models.User.getOwnerUser(context.internal)
|
|
.then(function (user) {
|
|
return models.User.edit({status: options.status}, _.merge({id: user.id}, context.internal));
|
|
});
|
|
},
|
|
|
|
createUsersWithRoles: function createUsersWithRoles() {
|
|
return Promise.all(DataGenerator.forKnex.roles.map((role) => {
|
|
return models.Role.add(role, context.internal);
|
|
})).then(function () {
|
|
return Promise.all(_.cloneDeep(DataGenerator.forKnex.users).map((user) => {
|
|
let userRolesRelations = _.filter(DataGenerator.forKnex.roles_users, {user_id: user.id});
|
|
|
|
userRolesRelations = _.map(userRolesRelations, function (userRolesRelation) {
|
|
return _.find(DataGenerator.forKnex.roles, {id: userRolesRelation.role_id});
|
|
});
|
|
|
|
user.roles = userRolesRelations;
|
|
return models.User.add(user, context.internal);
|
|
}));
|
|
});
|
|
},
|
|
|
|
ensureUserForEachRole: async function ensureUserForEachRole() {
|
|
const usersWithoutOwner = _.cloneDeep(DataGenerator.forKnex.users.slice(1));
|
|
|
|
let roles = await models.Role.fetchAll();
|
|
roles = roles.toJSON();
|
|
|
|
return Promise.all(usersWithoutOwner.map((user) => {
|
|
let userRolesRelations = _.filter(DataGenerator.forKnex.roles_users, {user_id: user.id});
|
|
|
|
userRolesRelations = _.map(userRolesRelations, function (userRolesRelation) {
|
|
return _.find(roles, {name: userRolesRelation.role_name});
|
|
});
|
|
|
|
user.roles = userRolesRelations;
|
|
|
|
return models.User.add(user, context.internal);
|
|
}));
|
|
},
|
|
|
|
createInactiveUser() {
|
|
const user = DataGenerator.forKnex.createUser({
|
|
email: 'inactive@test.org',
|
|
slug: 'inactive',
|
|
status: 'inactive'
|
|
});
|
|
|
|
return models.User.add(user, context.internal);
|
|
},
|
|
|
|
createExtraUsers: function createExtraUsers() {
|
|
// grab 3 more users
|
|
let extraUsers = _.cloneDeep(DataGenerator.Content.users.slice(2, 6));
|
|
extraUsers = _.map(extraUsers, function (user) {
|
|
return DataGenerator.forKnex.createUser(_.extend({}, user, {
|
|
id: ObjectId().toHexString(),
|
|
email: 'a' + user.email,
|
|
slug: 'a' + user.slug
|
|
}));
|
|
});
|
|
|
|
const roles = {};
|
|
roles[extraUsers[0].id] = DataGenerator.Content.roles[0];
|
|
roles[extraUsers[1].id] = DataGenerator.Content.roles[1];
|
|
roles[extraUsers[2].id] = DataGenerator.Content.roles[2];
|
|
roles[extraUsers[3].id] = DataGenerator.Content.roles[4];
|
|
|
|
// @TODO: remove when overhauling test env
|
|
// tests need access to the extra created users (especially to the created id)
|
|
// replacement for admin2, editor2 etc
|
|
DataGenerator.Content.extraUsers = extraUsers;
|
|
|
|
return Promise.all(extraUsers.map((user) => {
|
|
user.roles = roles[user.id];
|
|
return models.User.add(user, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertOneUser: function insertOneUser(options) {
|
|
options = options || {};
|
|
|
|
return models.User.add({
|
|
name: options.name,
|
|
email: options.email,
|
|
slug: options.slug,
|
|
status: options.status
|
|
}, context.internal);
|
|
},
|
|
|
|
insertOne: function insertOne(modelName, tableName, fn, index) {
|
|
const obj = DataGenerator.forKnex[fn](DataGenerator.Content[tableName][index || 0]);
|
|
return models[modelName].add(obj, context.internal);
|
|
},
|
|
|
|
getImportFixturePath: function (filename) {
|
|
return path.resolve(__dirname + '/fixtures/import/' + filename);
|
|
},
|
|
|
|
getExportFixturePath: function (filename) {
|
|
const relativePath = '/fixtures/export/';
|
|
return path.resolve(__dirname + relativePath + filename + '.json');
|
|
},
|
|
|
|
loadExportFixture: function loadExportFixture(filename) {
|
|
const filePath = this.getExportFixturePath(filename);
|
|
|
|
return fs.readFile(filePath).then(function (fileContents) {
|
|
let data;
|
|
|
|
// Parse the json data
|
|
try {
|
|
data = JSON.parse(fileContents);
|
|
} catch (e) {
|
|
return new Error('Failed to parse the file');
|
|
}
|
|
|
|
return data;
|
|
});
|
|
},
|
|
|
|
permissionsFor: function permissionsFor(obj) {
|
|
let permsToInsert = _.cloneDeep(fixtureManager.findModelFixtures('Permission', {object_type: obj}).entries);
|
|
const permsRolesToInsert = fixtureManager.findPermissionRelationsForObject(obj).entries;
|
|
const actions = [];
|
|
const permissionsRoles = {};
|
|
|
|
const roles = {
|
|
Administrator: DataGenerator.Content.roles[0].id,
|
|
Editor: DataGenerator.Content.roles[1].id,
|
|
Author: DataGenerator.Content.roles[2].id,
|
|
Owner: DataGenerator.Content.roles[3].id,
|
|
Contributor: DataGenerator.Content.roles[4].id,
|
|
'Admin Integration': DataGenerator.Content.roles[5].id
|
|
};
|
|
|
|
// CASE: if empty db will throw SQLITE_MISUSE, hard to debug
|
|
if (_.isEmpty(permsToInsert)) {
|
|
return Promise.reject(new Error('no permission found:' + obj));
|
|
}
|
|
|
|
permsToInsert = _.map(permsToInsert, function (perms) {
|
|
perms.id = ObjectId().toHexString();
|
|
|
|
actions.push({type: perms.action_type, permissionId: perms.id});
|
|
return DataGenerator.forKnex.createBasic(perms);
|
|
});
|
|
|
|
_.each(permsRolesToInsert, function (perms, role) {
|
|
if (perms[obj]) {
|
|
if (perms[obj] === 'all') {
|
|
_.each(actions, function (action) {
|
|
if (!permissionsRoles[action.permissionId]) {
|
|
permissionsRoles[action.permissionId] = [];
|
|
}
|
|
|
|
permissionsRoles[action.permissionId].push(_.find(DataGenerator.Content.roles, {id: roles[role]}));
|
|
});
|
|
} else {
|
|
_.each(perms[obj], function (action) {
|
|
if (!permissionsRoles[_.find(actions, {type: action}).permissionId]) {
|
|
permissionsRoles[_.find(actions, {type: action}).permissionId] = [];
|
|
}
|
|
|
|
permissionsRoles[_.find(actions, {type: action}).permissionId].push(_.find(DataGenerator.Content.roles, {id: roles[role]}));
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
return Promise.all(permsToInsert.map((perm) => {
|
|
if (!_.isEmpty(permissionsRoles)) {
|
|
perm.roles = permissionsRoles[perm.id];
|
|
}
|
|
|
|
return models.Permission.add(perm, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertInvites: function insertInvites() {
|
|
return Promise.all(DataGenerator.forKnex.invites.map((invite) => {
|
|
return models.Invite.add(invite, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertWebhook: function (attributes) {
|
|
const webhook = DataGenerator.forKnex.createWebhook(
|
|
Object.assign(attributes, {
|
|
integration_id: DataGenerator.forKnex.integrations[0].id
|
|
})
|
|
);
|
|
|
|
return models.Webhook.add(webhook, context.internal);
|
|
},
|
|
|
|
insertWebhooks: function insertWebhooks() {
|
|
return Promise.all(DataGenerator.forKnex.webhooks.map((webhook) => {
|
|
return models.Webhook.add(webhook, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertIntegrations: function insertIntegrations() {
|
|
return Promise.all(DataGenerator.forKnex.integrations.map((integration) => {
|
|
return models.Integration.add(integration, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertApiKeys: function insertApiKeys() {
|
|
return Promise.all(DataGenerator.forKnex.api_keys.map((api_key) => {
|
|
return models.ApiKey.add(api_key, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertLinks: function insertLinks() {
|
|
return Promise.all(DataGenerator.forKnex.links.map((link) => {
|
|
return models.Redirect.add(link, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertMentions: function insertMentions() {
|
|
return Promise.all(DataGenerator.forKnex.mentions.map((mention) => {
|
|
return models.Mention.add(mention, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertEmails: function insertEmails() {
|
|
return Promise.all(DataGenerator.forKnex.emails.map((email) => {
|
|
return models.Email.add(email, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertArchivedTiers: function insertArchivedTiers() {
|
|
let archivedProduct = DataGenerator.forKnex.createProduct({
|
|
active: false
|
|
});
|
|
|
|
return models.Product.add(archivedProduct, context.internal);
|
|
},
|
|
|
|
insertHiddenTiers: function insertArchivedTiers() {
|
|
let hiddenTier = DataGenerator.forKnex.createProduct({
|
|
visibility: 'none'
|
|
});
|
|
|
|
return models.Product.add(hiddenTier, context.internal);
|
|
},
|
|
|
|
insertExtraTiers: async function insertExtraTiers() {
|
|
const extraTier = DataGenerator.forKnex.createProduct({});
|
|
const extraTier2 = DataGenerator.forKnex.createProduct({slug: 'silver', name: 'Silver'});
|
|
await models.Product.add(extraTier, context.internal);
|
|
await models.Product.add(extraTier2, context.internal);
|
|
},
|
|
|
|
insertProducts: async function insertProducts() {
|
|
let coreProductFixtures = fixtureManager.findModelFixtures('Product').entries;
|
|
await Promise.all(coreProductFixtures.map(async (product) => {
|
|
const found = await models.Product.findOne(product, context.internal);
|
|
if (!found) {
|
|
await models.Product.add(product, context.internal);
|
|
}
|
|
}));
|
|
|
|
const product = await models.Product.findOne({type: 'paid'}, context.internal);
|
|
|
|
await sequence(_.cloneDeep(DataGenerator.forKnex.stripe_products).map(stripeProduct => () => {
|
|
stripeProduct.product_id = product.id;
|
|
return models.StripeProduct.add(stripeProduct, context.internal);
|
|
}));
|
|
|
|
await sequence(_.cloneDeep(DataGenerator.forKnex.stripe_prices).map(stripePrice => () => {
|
|
return models.StripePrice.add(stripePrice, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertMembersAndLabelsAndProducts: function insertMembersAndLabelsAndProducts(newsletters = false) {
|
|
return Promise.all(DataGenerator.forKnex.labels.map((label) => {
|
|
return models.Label.add(label, context.internal);
|
|
})).then(function () {
|
|
let coreProductFixtures = fixtureManager.findModelFixtures('Product').entries;
|
|
return Promise.all(coreProductFixtures.map(async (product) => {
|
|
const found = await models.Product.findOne(product, context.internal);
|
|
if (!found) {
|
|
await models.Product.add(product, context.internal);
|
|
}
|
|
}));
|
|
}).then(async function () {
|
|
let testProductFixtures = DataGenerator.forKnex.products;
|
|
for (const productFixture of testProductFixtures) {
|
|
if (productFixture.id) { // Not currently used - this is used to add new text fixtures, e.g. a Bronze/Silver/Gold Tier
|
|
await models.Product.add(productFixture, context.internal);
|
|
} else { // Used to update the core fixtures
|
|
// If it doesn't exist we have invalid fixtures, so require: true to ensure we throw
|
|
const existing = await models.Product.findOne({slug: productFixture.slug}, {...context.internal, require: true});
|
|
await models.Product.edit(productFixture, {...context.internal, id: existing.id});
|
|
}
|
|
}
|
|
}).then(function () {
|
|
return models.Product.findOne({type: 'paid'}, context.internal);
|
|
}).then(function (product) {
|
|
return Promise.all([
|
|
sequence(_.cloneDeep(DataGenerator.forKnex.stripe_products).map(stripeProduct => () => {
|
|
stripeProduct.product_id = product.id;
|
|
return models.StripeProduct.add(stripeProduct, context.internal);
|
|
})),
|
|
sequence(_.cloneDeep(DataGenerator.forKnex.members).map(member => () => {
|
|
let memberLabelRelations = _.filter(DataGenerator.forKnex.members_labels, {member_id: member.id});
|
|
|
|
memberLabelRelations = _.map(memberLabelRelations, function (memberLabelRelation) {
|
|
return _.find(DataGenerator.forKnex.labels, {id: memberLabelRelation.label_id});
|
|
});
|
|
|
|
member.labels = memberLabelRelations;
|
|
|
|
if (newsletters) {
|
|
let memberNewsletterRelations = _.filter(DataGenerator.forKnex.members_newsletters, {member_id: member.id});
|
|
memberNewsletterRelations = _.map(memberNewsletterRelations, function (memberNewsletterRelation) {
|
|
return _.find(DataGenerator.forKnex.newsletters, {id: memberNewsletterRelation.newsletter_id});
|
|
});
|
|
|
|
member.newsletters = memberNewsletterRelations;
|
|
}
|
|
|
|
// TODO: replace with full member/product associations
|
|
if (member.email === 'with-product@test.com') {
|
|
member.products = [{id: product.id}];
|
|
}
|
|
|
|
return models.Member.add(member, context.internal);
|
|
}))
|
|
]);
|
|
}).then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.members_stripe_customers).map(customer => () => {
|
|
return models.MemberStripeCustomer.add(customer, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.stripe_prices).map(stripePrice => () => {
|
|
return models.StripePrice.add(stripePrice, context.internal);
|
|
}));
|
|
}).then(async function () {
|
|
// Add monthly/yearly prices to default product for testing
|
|
const defaultProduct = await models.Product.findOne({slug: 'default-product'}, context.internal);
|
|
return models.Product.edit({
|
|
...defaultProduct.toJSON(),
|
|
monthly_price_id: DataGenerator.forKnex.stripe_prices[1].id,
|
|
yearly_price_id: DataGenerator.forKnex.stripe_prices[2].id
|
|
}, _.merge({id: defaultProduct.id}, context.internal));
|
|
}).then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.stripe_customer_subscriptions).map(subscription => () => {
|
|
return models.StripeCustomerSubscription.add(subscription, context.internal);
|
|
}));
|
|
}).then(async function () {
|
|
const members = (await models.Member.findAll({
|
|
withRelated: [
|
|
'labels',
|
|
'stripeSubscriptions',
|
|
'stripeSubscriptions.customer',
|
|
'stripeSubscriptions.stripePrice',
|
|
'stripeSubscriptions.stripePrice.stripeProduct',
|
|
'products',
|
|
'offerRedemptions'
|
|
]
|
|
})).toJSON();
|
|
|
|
for (const member of members) {
|
|
for (const subscription of member.subscriptions) {
|
|
const product = subscription.price.product.product_id;
|
|
await models.Member.edit({products: member.products.concat({
|
|
id: product
|
|
})}, {id: member.id});
|
|
}
|
|
}
|
|
}).then(async function () {
|
|
for (const event of DataGenerator.forKnex.members_paid_subscription_events) {
|
|
await models.MemberPaidSubscriptionEvent.add(event);
|
|
}
|
|
}).then(async function () {
|
|
for (const event of DataGenerator.forKnex.members_created_events) {
|
|
await models.MemberCreatedEvent.add(event);
|
|
}
|
|
}).then(async function () {
|
|
for (const event of DataGenerator.forKnex.members_subscription_created_events) {
|
|
await models.SubscriptionCreatedEvent.add(event);
|
|
}
|
|
});
|
|
},
|
|
|
|
insertEmailsAndRecipients: function insertEmailsAndRecipients(withFailed = false) {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.emails).map(email => () => {
|
|
return models.Email.add(email, context.internal);
|
|
})).then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.email_batches).map(emailBatch => () => {
|
|
return models.EmailBatch.add(emailBatch, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
const email_recipients = withFailed ?
|
|
DataGenerator.forKnex.email_recipients
|
|
: DataGenerator.forKnex.email_recipients.filter(r => r.failed_at === null);
|
|
|
|
return sequence(_.cloneDeep(email_recipients).map(emailRecipient => () => {
|
|
return models.EmailRecipient.add(emailRecipient, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
if (!withFailed) {
|
|
return;
|
|
}
|
|
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.email_recipient_failures).map(failure => () => {
|
|
return models.EmailRecipientFailure.add(failure, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
const toAggregate = {
|
|
emailIds: DataGenerator.forKnex.emails.map(email => email.id),
|
|
memberIds: DataGenerator.forKnex.members.map(member => member.id)
|
|
};
|
|
|
|
return emailAnalyticsService.service.aggregateStats(toAggregate);
|
|
});
|
|
},
|
|
|
|
insertNewsletters: async function insertNewsletters() {
|
|
return Promise.all(DataGenerator.forKnex.newsletters.map((newsletter) => {
|
|
return models.Newsletter.add(newsletter, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertComments: async function insertComments() {
|
|
// First create the parents (can happen in parallel), because the children depend on those
|
|
const parents = DataGenerator.forKnex.comments.filter(c => !c.parent_id);
|
|
const children = DataGenerator.forKnex.comments.filter(c => !!c.parent_id);
|
|
|
|
await Promise.all(parents.map((comment) => {
|
|
return models.Comment.add(comment, context.internal);
|
|
}));
|
|
|
|
await Promise.all(children.map((comment) => {
|
|
return models.Comment.add(comment, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertRedirects: async function insertClicks() {
|
|
await Promise.all(DataGenerator.forKnex.redirects.map((click) => {
|
|
return models.Redirect.add(click, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertClicks: async function insertClicks() {
|
|
await Promise.all(DataGenerator.forKnex.members_click_events.map((click) => {
|
|
return models.MemberClickEvent.add(click, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertFeedback: async function insertFeedback() {
|
|
await Promise.all(DataGenerator.forKnex.members_feedback.map((feedback) => {
|
|
return models.MemberFeedback.add(feedback, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertSnippets: function insertSnippets() {
|
|
return Promise.all(DataGenerator.forKnex.snippets.map((snippet) => {
|
|
return models.Snippet.add(snippet, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertCustomThemeSettings: function insertCustomThemeSettings() {
|
|
return Promise.all(DataGenerator.forKnex.custom_theme_settings.map((setting) => {
|
|
return models.CustomThemeSetting.add(setting, context.internal);
|
|
}));
|
|
},
|
|
|
|
async enableAllLabsFeatures() {
|
|
const labsValue = Object.fromEntries(labsService.WRITABLE_KEYS_ALLOWLIST.map(key => [key, true]));
|
|
const labsSetting = DataGenerator.forKnex.createSetting({
|
|
key: 'labs',
|
|
group: 'labs',
|
|
type: 'object',
|
|
value: JSON.stringify(labsValue)
|
|
});
|
|
|
|
const existingLabsSetting = await models.Settings.findOne({key: 'labs'});
|
|
|
|
if (existingLabsSetting) {
|
|
delete labsSetting.id;
|
|
await models.Settings.edit(labsSetting);
|
|
} else {
|
|
await models.Settings.add(labsSetting);
|
|
}
|
|
|
|
await settingsService.init();
|
|
}
|
|
};
|
|
|
|
const toDoList = {
|
|
roles: function insertRoles() {
|
|
return fixtures.insertRoles();
|
|
},
|
|
tag: function insertTag() {
|
|
return fixtures.insertOne('Tag', 'tags', 'createTag');
|
|
},
|
|
member: function insertMember() {
|
|
return fixtures.insertOne('Member', 'members', 'createMember');
|
|
},
|
|
members: function insertMembersAndLabelsAndProducts() {
|
|
return fixtures.insertMembersAndLabelsAndProducts(false);
|
|
},
|
|
products: function insertProducts() {
|
|
return fixtures.insertProducts();
|
|
},
|
|
newsletters: function insertNewsletters() {
|
|
return fixtures.insertNewsletters();
|
|
},
|
|
'members:newsletters': function insertMembersAndLabelsAndProductsAndNewsletters() {
|
|
return fixtures.insertMembersAndLabelsAndProducts(true);
|
|
},
|
|
'members:emails': function insertEmailsAndRecipients() {
|
|
return fixtures.insertEmailsAndRecipients(false);
|
|
},
|
|
'members:emails:failed': function insertEmailsAndRecipients() {
|
|
return fixtures.insertEmailsAndRecipients(true);
|
|
},
|
|
posts: function insertPostsAndTags() {
|
|
return fixtures.insertPostsAndTags();
|
|
},
|
|
'posts:mu': function insertMultiAuthorPosts() {
|
|
return fixtures.insertMultiAuthorPosts();
|
|
},
|
|
tags: function insertTags() {
|
|
return fixtures.insertTags();
|
|
},
|
|
'tags:extra': function insertExtraTags() {
|
|
return fixtures.insertExtraTags();
|
|
},
|
|
settings: function populateSettings() {
|
|
return settingsService.init();
|
|
},
|
|
'users:roles': function createUsersWithRoles() {
|
|
return fixtures.createUsersWithRoles();
|
|
},
|
|
users: function ensureUserForEachRole() {
|
|
return fixtures.ensureUserForEachRole();
|
|
},
|
|
'user:inactive': function createInactiveUser() {
|
|
return fixtures.createInactiveUser();
|
|
},
|
|
'users:extra': function createExtraUsers() {
|
|
return fixtures.createExtraUsers();
|
|
},
|
|
owner: function insertOwnerUser() {
|
|
return fixtures.insertOwnerUser();
|
|
},
|
|
'owner:pre': function initOwnerUser() {
|
|
return fixtures.initOwnerUser();
|
|
},
|
|
'owner:post': function overrideOwnerUser() {
|
|
return fixtures.overrideOwnerUser();
|
|
},
|
|
'perms:init': function initPermissions() {
|
|
return permissions.init();
|
|
},
|
|
perms: function permissionsFor(obj) {
|
|
return fixtures.permissionsFor(obj);
|
|
},
|
|
filter: function createFilterParamFixtures() {
|
|
return filterData(DataGenerator);
|
|
},
|
|
invites: function insertInvites() {
|
|
return fixtures.insertInvites();
|
|
},
|
|
webhooks: function insertWebhooks() {
|
|
return fixtures.insertWebhooks();
|
|
},
|
|
integrations: function insertIntegrations() {
|
|
return fixtures.insertIntegrations();
|
|
},
|
|
api_keys: function insertApiKeys() {
|
|
return fixtures.insertApiKeys();
|
|
},
|
|
emails: function insertEmails() {
|
|
return fixtures.insertEmails();
|
|
},
|
|
snippets: function insertSnippets() {
|
|
return fixtures.insertSnippets();
|
|
},
|
|
'labs:enabled': function enableAllLabsFeatures() {
|
|
return fixtures.enableAllLabsFeatures();
|
|
},
|
|
custom_theme_settings: function insertCustomThemeSettings() {
|
|
return fixtures.insertCustomThemeSettings();
|
|
},
|
|
'tiers:extra': function insertExtraTiers() {
|
|
return fixtures.insertExtraTiers();
|
|
},
|
|
'tiers:archived': function insertArchivedTiers() {
|
|
return fixtures.insertArchivedTiers();
|
|
},
|
|
'tiers:hidden': function insertHiddenTiers() {
|
|
return fixtures.insertHiddenTiers();
|
|
},
|
|
comments: function insertComments() {
|
|
return fixtures.insertComments();
|
|
},
|
|
redirects: function insertRedirects() {
|
|
return fixtures.insertRedirects();
|
|
},
|
|
clicks: function insertClicks() {
|
|
return fixtures.insertClicks();
|
|
},
|
|
feedback: function insertFeedback() {
|
|
return fixtures.insertFeedback();
|
|
},
|
|
links: function insertLinks() {
|
|
return fixtures.insertLinks();
|
|
},
|
|
mentions: function insertMentions() {
|
|
return fixtures.insertMentions();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ## getFixtureOps
|
|
*
|
|
* Takes the arguments from a setup function and turns them into an array of promises to fullfil
|
|
*
|
|
* This is effectively a list of instructions with regard to which fixtures should be setup for this test.
|
|
* * `default` - a special option which will cause the full suite of normal fixtures to be initialized
|
|
* * `perms:init` - initialize the permissions object after having added permissions
|
|
* * `perms:obj` - initialize permissions for a particular object type
|
|
* * `users:roles` - create a full suite of users, one per role
|
|
* @param {Object} toDos
|
|
*/
|
|
const getFixtureOps = (toDos) => {
|
|
// default = default fixtures, if it isn't present, init with tables only
|
|
const tablesOnly = !toDos.default;
|
|
|
|
const fixtureOps = [];
|
|
|
|
// Database initialization
|
|
if (toDos.init || toDos.default) {
|
|
fixtureOps.push(function initDB() {
|
|
// skip adding all fixtures!
|
|
if (tablesOnly) {
|
|
return knexMigrator.init({skip: 2});
|
|
}
|
|
|
|
return knexMigrator.init();
|
|
});
|
|
|
|
delete toDos.default;
|
|
delete toDos.init;
|
|
}
|
|
|
|
fixtureOps.push(toDoList['labs:enabled']);
|
|
|
|
// Go through our list of things to do, and add them to an array
|
|
_.each(toDos, function (value, toDo) {
|
|
let tmp;
|
|
|
|
if ((toDo !== 'perms:init' && toDo.indexOf('perms:') !== -1)) {
|
|
tmp = toDo.split(':');
|
|
|
|
fixtureOps.push(function addCustomFixture() {
|
|
return toDoList[tmp[0]](tmp[1]);
|
|
});
|
|
} else {
|
|
if (!toDoList[toDo]) {
|
|
throw new Error(`The fixture ${toDo} does not exist.`);
|
|
}
|
|
|
|
fixtureOps.push(toDoList[toDo]);
|
|
}
|
|
});
|
|
|
|
fixtureOps.push(() => {
|
|
return require('../../core/server/services/tiers').repository?.init();
|
|
});
|
|
|
|
return fixtureOps;
|
|
};
|
|
|
|
const getCurrentOwnerUser = async () => {
|
|
return await models.User.getOwnerUser(context.internal);
|
|
};
|
|
|
|
module.exports = {
|
|
fixtures,
|
|
getFixtureOps,
|
|
DataGenerator,
|
|
getCurrentOwnerUser
|
|
};
|