0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

🚨 database: change hard limits and field types (#7932)

refs #7432

🚨  database: change hard limits and field types

- we went over all schema fields and decided to decrease/increase the hard limits
- the core goal is to have more flexibility in the future
- we reconsidered string vs. text

There are 5 groups:

- small strings: 50 characters
    - static strings
    - status, visibility, language, role name, permission name, client name etc.
- medium strings: 191 characters
    - all unique fields or fields which can be unique in the future
    - slug, tokens, user name, password, tag name, email
- large strings: 1000-2000 characters
    - these fields need to be very flexible
    - these fields get a soft limit attached (in a different PR)
    - post title, meta title, meta description, urls
- medium text: 64kb characters
    - bio, settings, location, tour
- long text: 1000000000 chars
    - html, amp, mobiledoc, markdown

🙄  sort_order for tests

- sort order was not set for the tests, so it was always 0
- mysql could return a different result

in my case:
- field length 156 returned the following related tags ["bacon", "kitchen"]
- field length 157 returned the following related tags ["kitchen", "kitchen"]

Change client.secret to 191

Tweak field lengths

- Add 24 char limit for ids
- Limited fields are the exact length they need
- Unified 1000 and 2000 char string classes to all be 2000
- Changed descriptions to be either 2000, except user & tag which is text 65535 as these may be used to store HTML later?!
- Updated tests

🛠  Update importer tests

- The old 001-003 tests are kind of less relevant now.
- Rather than worrying about past versions of the data structure, we should check that the importer only imports what we consider to be valid data
- I've changed the tests to treat the title-length check as a length-validation check, rather than a test for each of the old versions

🔥 Remove foreign key from subscribers.post_id

- There's no real need to have an index on this column, it just makes deleting posts hard.
- Same as created_by type columns, we can reference ids without needing keys/indexes
This commit is contained in:
Katharina Irrgang 2017-02-17 23:20:59 +01:00 committed by GitHub
parent a353a9e0ea
commit 2e1d7fcc42
4 changed files with 209 additions and 215 deletions

View file

@ -1,228 +1,228 @@
module.exports = {
posts: {
id: {type: 'string', nullable: false, primary: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
title: {type: 'string', maxlength: 150, nullable: false},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
markdown: {type: 'text', maxlength: 16777215, fieldtype: 'medium', nullable: true},
title: {type: 'string', maxlength: 2000, nullable: false},
slug: {type: 'string', maxlength: 191, nullable: false, unique: true},
markdown: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
mobiledoc: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
html: {type: 'text', maxlength: 16777215, fieldtype: 'medium', nullable: true},
amp: {type: 'text', maxlength: 16777215, fieldtype: 'medium', nullable: true},
image: {type: 'text', maxlength: 2000, nullable: true},
html: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
amp: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
image: {type: 'string', maxlength: 2000, nullable: true},
featured: {type: 'bool', nullable: false, defaultTo: false},
page: {type: 'bool', nullable: false, defaultTo: false},
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'draft'},
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'draft'},
language: {type: 'string', maxlength: 6, nullable: false, defaultTo: 'en_US'},
visibility: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'public', validations: {isIn: [['public']]}},
meta_title: {type: 'string', maxlength: 150, nullable: true},
meta_description: {type: 'string', maxlength: 200, nullable: true},
author_id: {type: 'string', nullable: false},
visibility: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'public', validations: {isIn: [['public']]}},
meta_title: {type: 'string', maxlength: 2000, nullable: true},
meta_description: {type: 'string', maxlength: 2000, nullable: true},
author_id: {type: 'string', maxlength: 24, nullable: false},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true},
updated_by: {type: 'string', maxlength: 24, nullable: true},
published_at: {type: 'dateTime', nullable: true},
published_by: {type: 'string', nullable: true}
published_by: {type: 'string', maxlength: 24, nullable: true}
},
users: {
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
ghost_auth_access_token: {type: 'string', nullable: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
name: {type: 'string', maxlength: 191, nullable: false},
slug: {type: 'string', maxlength: 191, nullable: false, unique: true},
ghost_auth_access_token: {type: 'string', maxlength: 32, nullable: true},
password: {type: 'string', maxlength: 60, nullable: false},
email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}},
image: {type: 'text', maxlength: 2000, nullable: true},
cover: {type: 'text', maxlength: 2000, nullable: true},
bio: {type: 'string', maxlength: 200, nullable: true},
website: {type: 'text', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
image: {type: 'string', maxlength: 2000, nullable: true},
cover: {type: 'string', maxlength: 2000, nullable: true},
bio: {type: 'text', maxlength: 65535, nullable: true},
website: {type: 'string', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
location: {type: 'text', maxlength: 65535, nullable: true},
facebook: {type: 'text', maxlength: 2000, nullable: true},
twitter: {type: 'text', maxlength: 2000, nullable: true},
facebook: {type: 'string', maxlength: 2000, nullable: true},
twitter: {type: 'string', maxlength: 2000, nullable: true},
accessibility: {type: 'text', maxlength: 65535, nullable: true},
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'active'},
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'active'},
language: {type: 'string', maxlength: 6, nullable: false, defaultTo: 'en_US'},
visibility: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'public', validations: {isIn: [['public']]}},
meta_title: {type: 'string', maxlength: 150, nullable: true},
meta_description: {type: 'string', maxlength: 200, nullable: true},
visibility: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'public', validations: {isIn: [['public']]}},
meta_title: {type: 'string', maxlength: 2000, nullable: true},
meta_description: {type: 'string', maxlength: 2000, nullable: true},
tour: {type: 'text', maxlength: 65535, nullable: true},
last_login: {type: 'dateTime', nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
roles: {
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false, unique: true},
description: {type: 'string', maxlength: 200, nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
name: {type: 'string', maxlength: 50, nullable: false, unique: true},
description: {type: 'string', maxlength: 2000, nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
roles_users: {
id: {type: 'string', nullable: false, primary: true},
role_id: {type: 'string', nullable: false},
user_id: {type: 'string', nullable: false}
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
role_id: {type: 'string', maxlength: 24, nullable: false},
user_id: {type: 'string', maxlength: 24, nullable: false}
},
permissions: {
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false, unique: true},
object_type: {type: 'string', maxlength: 150, nullable: false},
action_type: {type: 'string', maxlength: 150, nullable: false},
object_id: {type: 'string', nullable: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
name: {type: 'string', maxlength: 50, nullable: false, unique: true},
object_type: {type: 'string', maxlength: 50, nullable: false},
action_type: {type: 'string', maxlength: 50, nullable: false},
object_id: {type: 'string', maxlength: 24, nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
permissions_users: {
id: {type: 'string', nullable: false, primary: true},
user_id: {type: 'string', nullable: false},
permission_id: {type: 'string', nullable: false}
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
user_id: {type: 'string', maxlength: 24, nullable: false},
permission_id: {type: 'string', maxlength: 24, nullable: false}
},
permissions_roles: {
id: {type: 'string', nullable: false, primary: true},
role_id: {type: 'string', nullable: false},
permission_id: {type: 'string', nullable: false}
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
role_id: {type: 'string', maxlength: 24, nullable: false},
permission_id: {type: 'string', maxlength: 24, nullable: false}
},
permissions_apps: {
id: {type: 'string', nullable: false, primary: true},
app_id: {type: 'string', nullable: false},
permission_id: {type: 'string', nullable: false}
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
app_id: {type: 'string', maxlength: 24, nullable: false},
permission_id: {type: 'string', maxlength: 24, nullable: false}
},
settings: {
id: {type: 'string', nullable: false, primary: true},
key: {type: 'string', maxlength: 150, nullable: false, unique: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
key: {type: 'string', maxlength: 50, nullable: false, unique: true},
value: {type: 'text', maxlength: 65535, nullable: true},
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'core', validations: {isIn: [['core', 'blog', 'theme', 'app', 'plugin', 'private']]}},
type: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'core', validations: {isIn: [['core', 'blog', 'theme', 'app', 'plugin', 'private']]}},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
tags: {
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false, validations: {matches: /^([^,]|$)/}},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
description: {type: 'string', maxlength: 200, nullable: true},
image: {type: 'text', maxlength: 2000, nullable: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
name: {type: 'string', maxlength: 191, nullable: false, validations: {matches: /^([^,]|$)/}},
slug: {type: 'string', maxlength: 191, nullable: false, unique: true},
description: {type: 'text', maxlength: 65535, nullable: true},
image: {type: 'string', maxlength: 2000, nullable: true},
parent_id: {type: 'string', nullable: true},
visibility: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'public', validations: {isIn: [['public', 'internal']]}},
meta_title: {type: 'string', maxlength: 150, nullable: true},
meta_description: {type: 'string', maxlength: 200, nullable: true},
visibility: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'public', validations: {isIn: [['public', 'internal']]}},
meta_title: {type: 'string', maxlength: 2000, nullable: true},
meta_description: {type: 'string', maxlength: 2000, nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
posts_tags: {
id: {type: 'string', nullable: false, primary: true},
post_id: {type: 'string', nullable: false, references: 'posts.id'},
tag_id: {type: 'string', nullable: false, references: 'tags.id'},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
post_id: {type: 'string', maxlength: 24, nullable: false, references: 'posts.id'},
tag_id: {type: 'string', maxlength: 24, nullable: false, references: 'tags.id'},
sort_order: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0}
},
apps: {
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: false, unique: true},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
version: {type: 'string', maxlength: 150, nullable: false},
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'inactive'},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
slug: {type: 'string', maxlength: 191, nullable: false, unique: true},
version: {type: 'string', maxlength: 50, nullable: false},
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'inactive'},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
app_settings: {
id: {type: 'string', nullable: false, primary: true},
key: {type: 'string', maxlength: 150, nullable: false, unique: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
key: {type: 'string', maxlength: 50, nullable: false, unique: true},
value: {type: 'text', maxlength: 65535, nullable: true},
app_id: {type: 'string', nullable: false, references: 'apps.id'},
app_id: {type: 'string', maxlength: 24, nullable: false, references: 'apps.id'},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
app_fields: {
id: {type: 'string', nullable: false, primary: true},
key: {type: 'string', maxlength: 150, nullable: false},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
key: {type: 'string', maxlength: 50, nullable: false},
value: {type: 'text', maxlength: 65535, nullable: true},
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'html'},
app_id: {type: 'string', nullable: false, references: 'apps.id'},
relatable_id: {type: 'string', nullable: false},
relatable_type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'posts'},
type: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'html'},
app_id: {type: 'string', maxlength: 24, nullable: false, references: 'apps.id'},
relatable_id: {type: 'string', maxlength: 24, nullable: false},
relatable_type: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'posts'},
active: {type: 'bool', nullable: false, defaultTo: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
clients: {
id: {type: 'string', nullable: false, primary: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
uuid: {type: 'string', maxlength: 36, nullable: false},
name: {type: 'string', maxlength: 150, nullable: false, unique: true},
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
secret: {type: 'string', maxlength: 150, nullable: false},
name: {type: 'string', maxlength: 50, nullable: false, unique: true},
slug: {type: 'string', maxlength: 50, nullable: false, unique: true},
secret: {type: 'string', maxlength: 191, nullable: false},
redirection_uri: {type: 'string', maxlength: 2000, nullable: true},
client_uri: {type: 'string', maxlength: 2000, nullable: true},
auth_uri: {type: 'string', maxlength: 2000, nullable: true},
logo: {type: 'string', maxlength: 2000, nullable: true},
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'development'},
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'ua', validations: {isIn: [['ua', 'web', 'native']]}},
description: {type: 'string', maxlength: 200, nullable: true},
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'development'},
type: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'ua', validations: {isIn: [['ua', 'web', 'native']]}},
description: {type: 'string', maxlength: 2000, nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
client_trusted_domains: {
id: {type: 'string', nullable: false, primary: true},
client_id: {type: 'string', nullable: false, references: 'clients.id'},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
client_id: {type: 'string', maxlength: 24, nullable: false, references: 'clients.id'},
trusted_domain: {type: 'string', maxlength: 2000, nullable: true}
},
accesstokens: {
id: {type: 'string', nullable: false, primary: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
token: {type: 'string', maxlength: 191, nullable: false, unique: true},
user_id: {type: 'string', nullable: false, references: 'users.id'},
client_id: {type: 'string', nullable: false, references: 'clients.id'},
user_id: {type: 'string', maxlength: 24, nullable: false, references: 'users.id'},
client_id: {type: 'string', maxlength: 24, nullable: false, references: 'clients.id'},
expires: {type: 'bigInteger', nullable: false}
},
refreshtokens: {
id: {type: 'string', nullable: false, primary: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
token: {type: 'string', maxlength: 191, nullable: false, unique: true},
user_id: {type: 'string', nullable: false, references: 'users.id'},
client_id: {type: 'string', nullable: false, references: 'clients.id'},
user_id: {type: 'string', maxlength: 24, nullable: false, references: 'users.id'},
client_id: {type: 'string', maxlength: 24, nullable: false, references: 'clients.id'},
expires: {type: 'bigInteger', nullable: false}
},
subscribers: {
id: {type: 'string', nullable: false, primary: true},
name: {type: 'string', maxlength: 150, nullable: true},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
name: {type: 'string', maxlength: 191, nullable: true},
email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}},
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'pending', validations: {isIn: [['subscribed', 'pending', 'unsubscribed']]}},
post_id: {type: 'string', nullable: true, references: 'posts.id'},
subscribed_url: {type: 'text', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
subscribed_referrer: {type: 'text', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
unsubscribed_url: {type: 'text', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'pending', validations: {isIn: [['subscribed', 'pending', 'unsubscribed']]}},
post_id: {type: 'string', maxlength: 24, nullable: true},
subscribed_url: {type: 'string', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
subscribed_referrer: {type: 'string', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
unsubscribed_url: {type: 'string', maxlength: 2000, nullable: true, validations: {isEmptyOrURL: true}},
unsubscribed_at: {type: 'dateTime', nullable: true},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
invites: {
id: {type: 'string', nullable: false, primary: true},
role_id: {type: 'string', nullable: false},
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'pending', validations: {isIn: [['pending', 'sent']]}},
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
role_id: {type: 'string', maxlength: 24, nullable: false},
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'pending', validations: {isIn: [['pending', 'sent']]}},
token: {type: 'string', maxlength: 191, nullable: false, unique: true},
email: {type: 'string', maxlength: 191, nullable: false, unique: true, validations: {isEmail: true}},
expires: {type: 'bigInteger', nullable: false},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'string', nullable: false},
created_by: {type: 'string', maxlength: 24, nullable: false},
updated_at: {type: 'dateTime', nullable: true},
updated_by: {type: 'string', nullable: true}
updated_by: {type: 'string', maxlength: 24, nullable: true}
},
brute: {
key: {type: 'string'},
key: {type: 'string', maxlength: 191},
firstRequest: {type: 'bigInteger'},
lastRequest: {type: 'bigInteger'},
lifetime: {type: 'bigInteger'},

View file

@ -220,47 +220,6 @@ describe('Import', function () {
}).catch(done);
});
it('doesn\'t import invalid post data from 001', function (done) {
var exportData;
testUtils.fixtures.loadExportFixture('export-001').then(function (exported) {
exportData = exported;
// change title to 151 characters
exportData.data.posts[0].title = new Array(152).join('a');
exportData.data.posts[0].tags = 'Tag';
return importer.doImport(exportData);
}).then(function () {
(1).should.eql(0, 'Data import should not resolve promise.');
}, function (error) {
error[0].message.should.eql('Value in [posts.title] exceeds maximum length of 150 characters.');
error[0].errorType.should.eql('ValidationError');
Promise.all([
knex('users').select(),
knex('posts').select(),
knex('tags').select()
]).then(function (importedData) {
should.exist(importedData);
importedData.length.should.equal(3, 'Did not get data successfully');
var users = importedData[0],
posts = importedData[1],
tags = importedData[2];
// we always have 1 user, the default user we added
users.length.should.equal(1, 'There should only be one user');
// Nothing should have been imported
posts.length.should.equal(0, 'Wrong number of posts');
tags.length.should.equal(0, 'no new tags');
done();
});
}).catch(done);
});
it('doesn\'t import invalid settings data from 001', function (done) {
var exportData;
@ -368,46 +327,6 @@ describe('Import', function () {
}).catch(done);
});
it('doesn\'t import invalid post data from 002', function (done) {
var exportData;
testUtils.fixtures.loadExportFixture('export-002').then(function (exported) {
exportData = exported;
// change title to 151 characters
exportData.data.posts[0].title = new Array(152).join('a');
exportData.data.posts[0].tags = 'Tag';
return importer.doImport(exportData);
}).then(function () {
(1).should.eql(0, 'Data import should not resolve promise.');
}, function (error) {
error[0].message.should.eql('Value in [posts.title] exceeds maximum length of 150 characters.');
error[0].errorType.should.eql('ValidationError');
Promise.all([
knex('users').select(),
knex('posts').select(),
knex('tags').select()
]).then(function (importedData) {
should.exist(importedData);
importedData.length.should.equal(3, 'Did not get data successfully');
var users = importedData[0],
posts = importedData[1],
tags = importedData[2];
// we always have 1 user, the owner user we added
users.length.should.equal(1, 'There should only be one user');
// Nothing should have been imported
posts.length.should.equal(0, 'Wrong number of posts');
tags.length.should.equal(0, 'no new tags');
done();
});
}).catch(done);
});
it('doesn\'t import invalid settings data from 002', function (done) {
var exportData;
@ -607,6 +526,51 @@ describe('Import', function () {
}).catch(done);
});
});
describe('Validation', function () {
beforeEach(testUtils.setup('roles', 'owner', 'settings'));
it('doesn\'t import a title which is too long', function (done) {
var exportData;
testUtils.fixtures.loadExportFixture('export-001').then(function (exported) {
exportData = exported;
// change title to 1001 characters
exportData.data.posts[0].title = new Array(2002).join('a');
exportData.data.posts[0].tags = 'Tag';
return importer.doImport(exportData);
}).then(function () {
(1).should.eql(0, 'Data import should not resolve promise.');
}, function (error) {
error[0].message.should.eql('Value in [posts.title] exceeds maximum length of 2000 characters.');
error[0].errorType.should.eql('ValidationError');
Promise.all([
knex('users').select(),
knex('posts').select(),
knex('tags').select()
]).then(function (importedData) {
should.exist(importedData);
importedData.length.should.equal(3, 'Did not get data successfully');
var users = importedData[0],
posts = importedData[1],
tags = importedData[2];
// we always have 1 user, the default user we added
users.length.should.equal(1, 'There should only be one user');
// Nothing should have been imported
posts.length.should.equal(0, 'Wrong number of posts');
tags.length.should.equal(0, 'no new tags');
done();
});
}).catch(done);
});
});
});
// Tests in here do an import-per-describe, and then have several tests to check various bits of data

View file

@ -20,7 +20,7 @@ should.equal(true, true);
// both of which are required for migrations to work properly.
describe('DB version integrity', function () {
// Only these variables should need updating
var currentSchemaHash = 'fa72ab0ca7ce8ee20bafb6e73b61a324',
var currentSchemaHash = 'e648a7d1f9b9c5eb19512999756cd4db',
currentFixturesHash = 'b9e684a87353c592df9b23948e364c05';
// If this test is failing, then it is likely a change has been made that requires a DB version bump,

View file

@ -529,12 +529,42 @@ DataGenerator.forKnex = (function () {
// this is not pretty, but the fastest
// it relies on the created posts/tags
posts_tags = [
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[0].id, tag_id: DataGenerator.Content.tags[0].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[0].id, tag_id: DataGenerator.Content.tags[1].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[1].id, tag_id: DataGenerator.Content.tags[0].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[1].id, tag_id: DataGenerator.Content.tags[1].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[2].id, tag_id: DataGenerator.Content.tags[2].id},
{id: ObjectId.generate(), post_id: DataGenerator.Content.posts[3].id, tag_id: DataGenerator.Content.tags[3].id}
{
id: ObjectId.generate(),
post_id: DataGenerator.Content.posts[0].id,
tag_id: DataGenerator.Content.tags[0].id,
sort_order: 0
},
{
id: ObjectId.generate(),
post_id: DataGenerator.Content.posts[0].id,
tag_id: DataGenerator.Content.tags[1].id,
sort_order: 1
},
{
id: ObjectId.generate(),
post_id: DataGenerator.Content.posts[1].id,
tag_id: DataGenerator.Content.tags[0].id,
sort_order: 2
},
{
id: ObjectId.generate(),
post_id: DataGenerator.Content.posts[1].id,
tag_id: DataGenerator.Content.tags[1].id,
sort_order: 3
},
{
id: ObjectId.generate(),
post_id: DataGenerator.Content.posts[2].id,
tag_id: DataGenerator.Content.tags[2].id,
sort_order: 4
},
{
id: ObjectId.generate(),
post_id: DataGenerator.Content.posts[3].id,
tag_id: DataGenerator.Content.tags[3].id,
sort_order: 5
}
];
apps = [