Why: - We need a clear state for each test run, because anonymous editing with temp accounts results in a temporary acocunt being generated, and the anonymous token is no longer valid. What: - Set a new REST client so that we have a clear cookie state before running a test Bug: T365647 Change-Id: I79666ff92de090e69d32f1152ba330cf913e64e2
259 lines
8.4 KiB
JavaScript
259 lines
8.4 KiB
JavaScript
'use strict';
|
|
|
|
const { action, assert, REST, utils } = require( 'api-testing' );
|
|
const supertest = require( 'supertest' );
|
|
|
|
describe( 'POST /page', () => {
|
|
// NOTE: the /page/{title} endpoint (PageSourceHandler) has to go to v1 first!
|
|
let client, mindy, anon, anonToken;
|
|
|
|
beforeEach( async () => {
|
|
// Reset the client and token before each test
|
|
// In a temp account context, making an anonymous edit generates an account
|
|
// so we want to reset state after each edit
|
|
mindy = await action.mindy();
|
|
client = new REST();
|
|
anon = await action.getAnon();
|
|
anonToken = await anon.token();
|
|
} );
|
|
|
|
const checkEditResponse = function ( title, reqBody, body ) {
|
|
assert.containsAllKeys( body, [ 'title', 'key', 'source', 'latest', 'id',
|
|
'license', 'content_model' ] );
|
|
assert.containsAllKeys( body.latest, [ 'id', 'timestamp' ] );
|
|
assert.nestedPropertyVal( body, 'source', reqBody.source );
|
|
assert.nestedPropertyVal( body, 'title', title );
|
|
assert.nestedPropertyVal( body, 'key', utils.dbkey( title ) );
|
|
assert.isAbove( body.latest.id, 0 );
|
|
|
|
if ( reqBody.content_model ) {
|
|
assert.nestedPropertyVal( body, 'content_model', reqBody.content_model );
|
|
}
|
|
};
|
|
|
|
const checkSourceResponse = function ( title, reqBody, body ) {
|
|
if ( reqBody.content_model ) {
|
|
assert.nestedPropertyVal( body, 'content_model', reqBody.content_model );
|
|
}
|
|
|
|
assert.nestedPropertyVal( body, 'title', title );
|
|
assert.nestedPropertyVal( body, 'key', utils.dbkey( title ) );
|
|
assert.nestedPropertyVal( body, 'source', reqBody.source );
|
|
};
|
|
|
|
describe( 'successful operation', () => {
|
|
it( 'should create a page if it does not exist', async () => {
|
|
const titleSuffix = utils.title();
|
|
const title = 'A B+C:D@E-' + titleSuffix;
|
|
const normalizedTitle = utils.dbkey( title );
|
|
|
|
// In "title style" encoding, spaces turn to underscores,
|
|
// colons are preserved, and slashes and pluses get encoded.
|
|
// FIXME: correct handling of encoded slashes depends on
|
|
// the server setup and can't be tested reliably.
|
|
const encodedTitle = 'A_B%2BC:D@E-' + titleSuffix;
|
|
|
|
const reqBody = {
|
|
token: anonToken,
|
|
source: 'Lörem Ipsüm',
|
|
comment: 'tästing',
|
|
title
|
|
};
|
|
|
|
const { status: editStatus, body: editBody, header } =
|
|
await client.post( '/page', reqBody );
|
|
assert.equal( editStatus, 201 );
|
|
assert.match( header[ 'content-type' ], /^application\/json/ );
|
|
|
|
assert.nestedProperty( header, 'location' );
|
|
const location = header.location;
|
|
assert.match( location, new RegExp( `^https?://.*/v1/page/${ encodedTitle }$` ) );
|
|
checkEditResponse( title, reqBody, editBody );
|
|
|
|
// follow redirect
|
|
const { status: redirStatus, body: redirBody, header: redirHeader } =
|
|
await supertest.agent( location ).get( '' );
|
|
assert.equal( redirStatus, 200 );
|
|
assert.match( redirHeader[ 'content-type' ], /^application\/json/ );
|
|
checkSourceResponse( title, reqBody, redirBody );
|
|
|
|
// construct request to fetch content
|
|
const { status: sourceStatus, body: sourceBody, header: sourceHeader } =
|
|
await client.get( `/page/${ normalizedTitle }` );
|
|
assert.equal( sourceStatus, 200 );
|
|
assert.match( sourceHeader[ 'content-type' ], /^application\/json/ );
|
|
checkSourceResponse( title, reqBody, sourceBody );
|
|
} );
|
|
|
|
it( 'should create a page with specified model', async () => {
|
|
const title = utils.title( 'Edit Test ' );
|
|
const normalizedTitle = utils.dbkey( title );
|
|
|
|
// TODO: Test with a model different from the default. This however requires
|
|
// the changecontentmodel permission, which anons don't have.
|
|
const reqBody = {
|
|
token: anonToken,
|
|
source: 'Lörem Ipsüm',
|
|
comment: 'tästing',
|
|
content_model: 'wikitext',
|
|
title
|
|
};
|
|
|
|
const { status: editStatus, body: editBody, header: editHeader } =
|
|
await client.post( '/page', reqBody );
|
|
assert.equal( editStatus, 201 );
|
|
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
|
|
checkEditResponse( title, reqBody, editBody );
|
|
|
|
const { status: sourceStatus, body: sourceBody, header: sourceHeader } =
|
|
await client.get( `/page/${ normalizedTitle }` );
|
|
assert.equal( sourceStatus, 200 );
|
|
assert.match( sourceHeader[ 'content-type' ], /^application\/json/ );
|
|
checkSourceResponse( title, reqBody, sourceBody );
|
|
} );
|
|
} );
|
|
|
|
describe( 'request validation', () => {
|
|
const requiredProps = [ 'source', 'comment', 'title' ];
|
|
|
|
requiredProps.forEach( ( missingPropName ) => {
|
|
const title = utils.title( 'Edit Test ' );
|
|
const reqBody = {
|
|
token: anonToken,
|
|
source: 'Lörem Ipsüm',
|
|
comment: 'tästing',
|
|
title
|
|
};
|
|
|
|
it( `should fail when ${ missingPropName } is missing from the request body`, async () => {
|
|
const incompleteBody = { ...reqBody };
|
|
delete incompleteBody[ missingPropName ];
|
|
|
|
const { status: editStatus, body: editBody, header: editHeader } =
|
|
await client.post( '/page', incompleteBody );
|
|
|
|
assert.equal( editStatus, 400 );
|
|
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
|
|
assert.nestedProperty( editBody, 'messageTranslations' );
|
|
} );
|
|
} );
|
|
|
|
it( 'should fail if no token is given', async () => {
|
|
const title = utils.title( 'Edit Test ' );
|
|
const reqBody = {
|
|
// no token
|
|
source: 'Lörem Ipsüm',
|
|
comment: 'tästing',
|
|
title
|
|
};
|
|
|
|
const { status: editStatus, body: editBody, header: editHeader } =
|
|
await client.post( '/page', reqBody );
|
|
|
|
assert.equal( editStatus, 400 );
|
|
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
|
|
assert.nestedProperty( editBody, 'messageTranslations' );
|
|
} );
|
|
|
|
it( 'should fail if a bad token is given', async () => {
|
|
const title = utils.title( 'Edit Test ' );
|
|
const reqBody = {
|
|
token: 'BAD',
|
|
source: 'Lörem Ipsüm',
|
|
comment: 'tästing',
|
|
title
|
|
};
|
|
|
|
const { status: editStatus, body: editBody, header: editHeader } =
|
|
await client.post( '/page', reqBody );
|
|
|
|
assert.equal( editStatus, 403 );
|
|
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
|
|
assert.nestedProperty( editBody, 'messageTranslations' );
|
|
} );
|
|
|
|
it( 'should fail if a bad content model is given', async () => {
|
|
const title = utils.title( 'Edit Test ' );
|
|
|
|
const reqBody = {
|
|
token: anonToken,
|
|
source: 'Lörem Ipsüm',
|
|
comment: 'tästing',
|
|
content_model: 'THIS DOES NOT EXIST!',
|
|
title
|
|
};
|
|
const { status: editStatus, body: editBody, header: editHeader } =
|
|
await client.post( '/page', reqBody );
|
|
|
|
assert.equal( editStatus, 400 );
|
|
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
|
|
assert.nestedProperty( editBody, 'messageTranslations' );
|
|
} );
|
|
|
|
it( 'should fail if a bad title is given', async () => {
|
|
const title = '_|_'; // not a valid page title
|
|
|
|
const reqBody = {
|
|
token: anonToken,
|
|
source: 'Lörem Ipsüm',
|
|
comment: 'tästing',
|
|
title
|
|
};
|
|
const { status: editStatus, body: editBody, header: editHeader } =
|
|
await client.post( '/page', reqBody );
|
|
|
|
assert.equal( editStatus, 400 );
|
|
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
|
|
assert.nestedProperty( editBody, 'messageTranslations' );
|
|
} );
|
|
} );
|
|
|
|
describe( 'failures due to system state', () => {
|
|
it( 'should detect a conflict if page exist', async () => {
|
|
const title = utils.title( 'Edit Test ' );
|
|
|
|
// create
|
|
await mindy.edit( title, {} );
|
|
|
|
const reqBody = {
|
|
token: anonToken,
|
|
source: 'Lörem Ipsüm',
|
|
comment: 'tästing',
|
|
title
|
|
};
|
|
const { status: editStatus, body: editBody, header: editHeader } =
|
|
await client.post( '/page', reqBody );
|
|
|
|
assert.equal( editStatus, 409 );
|
|
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
|
|
assert.nestedProperty( editBody, 'messageTranslations' );
|
|
} );
|
|
} );
|
|
|
|
describe( 'permission checks', () => {
|
|
it( 'should fail when trying to create a protected title without appropriate permissions', async () => {
|
|
const title = utils.title( 'Edit Test ' );
|
|
|
|
// protected a non-existing title against creation
|
|
await mindy.action( 'protect', {
|
|
title,
|
|
token: await mindy.token(),
|
|
protections: 'create=sysop'
|
|
}, 'POST' );
|
|
|
|
const reqBody = {
|
|
token: anonToken,
|
|
source: 'Lörem Ipsüm',
|
|
comment: 'tästing',
|
|
title
|
|
};
|
|
const { status: editStatus, body: editBody, header: editHeader } =
|
|
await client.post( '/page', reqBody );
|
|
|
|
assert.equal( editStatus, 403 );
|
|
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
|
|
assert.nestedProperty( editBody, 'messageTranslations' );
|
|
} );
|
|
|
|
} );
|
|
} );
|