blob: e29a22582af4aeaec9e485ebdbccfd38b80a9ec4 [file] [log] [blame]
<?php
/**
* Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Unit tests for the PushTask class.
*
*/
namespace google\appengine\api\taskqueue;
use google\appengine\api\taskqueue\PushTask;
use google\appengine\testing\ApiProxyTestBase;
use google\appengine\TaskQueueAddRequest\RequestMethod;
use google\appengine\TaskQueueBulkAddRequest;
use google\appengine\TaskQueueBulkAddResponse;
use google\appengine\TaskQueueServiceError\ErrorCode;
$mockTime = 12345.6;
// This mocks out PHP's microtime() function.
function microtime($get_as_float = false) {
if (!$get_as_float) {
die('microtime called with get_as_float=false');
}
global $mockTime;
return $mockTime;
}
class PushTaskTest extends ApiProxyTestBase {
/**
* Override time() in current namespace for testing.
*/
public function setUp() {
parent::setUp();
$this->_SERVER = $_SERVER;
}
public function tearDown() {
$_SERVER = $this->_SERVER;
parent::tearDown();
}
private static function buildBulkAddRequest() {
$req = new TaskQueueBulkAddRequest();
$task = $req->addAddRequest();
$task->setQueueName('default');
$task->setTaskName('');
$task->setUrl('/someUrl');
global $mockTime;
$task->setEtaUsec($mockTime * 1e6);
$task->setMethod(RequestMethod::POST);
return $req;
}
public function testConstructorUrlWrongType() {
$this->setExpectedException('\InvalidArgumentException',
'url_path must be a string. Actual type: integer');
$task = new PushTask(999, ['key' => 'some value']);
}
public function testConstructorUrlEmpty() {
$this->setExpectedException('\InvalidArgumentException',
"url_path must begin with '/'.");
$task = new PushTask('', ['key' => 'some value']);
}
public function testConstructorUrlWithoutLeadingSlash() {
$this->setExpectedException('\InvalidArgumentException',
"url_path must begin with '/'.");
$task = new PushTask('wrong', ['key' => 'some value']);
}
public function testConstructorUrlMustNotContainQueryString() {
$this->setExpectedException('\InvalidArgumentException',
'query strings not allowed in url_path.');
$task = new PushTask('/someurl?');
}
public function testConstructorQueryDataWrongType() {
$this->setExpectedException('\InvalidArgumentException',
'query_data must be an array. Actual type: string');
$task = new PushTask('/myUrl', 'abc');
}
public function testConstructorOptionsWrongType() {
$this->setExpectedException('\InvalidArgumentException',
'options must be an array. Actual type: integer');
$task = new PushTask('/someUrl', ['key' => 'some value'], 123);
}
public function testConstructorUnknownOptions() {
$this->setExpectedException('\InvalidArgumentException',
'Invalid options supplied: nonsense');
$task = new PushTask('/someUrl', ['key' => 'some value'],
['nonsense' => 123]);
}
public function testConstructorInvalidMethod() {
$this->setExpectedException('\InvalidArgumentException',
'Invalid method: POSTIT');
$task = new PushTask('/someUrl', ['key' => 'some value'],
['method' => 'POSTIT']);
}
public function testConstructorInvalidName() {
$this->setExpectedException('\InvalidArgumentException',
'name must be a string. Actual type: integer');
$task = new PushTask('/someUrl', ['key' => 'some value'],
['name' => 55]);
}
public function testConstructorNameTooLong() {
$name = str_repeat('a', 501);
$this->setExpectedException('\InvalidArgumentException',
'name exceeds maximum length of 500. First 1000 ' .
'characters of name: ' . $name);
$task = new PushTask('/someUrl', ['key' => 'some value'],
['name' => $name]);
}
public function testConstructorNameMaxLength() {
// Just tests that this doesn't throw an exception.
$task = new PushTask('/someUrl', ['key' => 'some value'],
['name' => str_repeat('a', 500)]);
}
public function testConstructorNameBadCharacters() {
$this->setExpectedException('\InvalidArgumentException',
'name must match pattern: ' . PushTask::NAME_PATTERN . '. name: @');
$task = new PushTask('/someUrl', ['key' => 'some value'],
['name' => '@']);
}
public function testConstructorInvalidDelaySeconds() {
$this->setExpectedException('\InvalidArgumentException',
'delay_seconds must be a numeric type.');
$task = new PushTask('/someUrl', ['key' => 'some value'],
['delay_seconds' => 'a few']);
}
public function testConstructorNegativeDelaySeconds() {
$this->setExpectedException('\InvalidArgumentException',
'delay_seconds must be between 0 and ' . PushTask::MAX_DELAY_SECONDS .
' (30 days). delay_seconds: -1');
$task = new PushTask('/someUrl', ['key' => 'some value'],
['delay_seconds' => -1]);
}
public function testConstructorDelaySecondsTooBig() {
$delay = 1 + 30 * 86400;
$this->setExpectedException('\InvalidArgumentException',
'delay_seconds must be between 0 and ' . PushTask::MAX_DELAY_SECONDS .
' (30 days). delay_seconds: ' . $delay);
$task = new PushTask('/someUrl', ['key' => 'some value'],
['delay_seconds' => $delay]);
}
public function testConstructorMaxDelaySecond() {
// Just tests that a 30 day delay_seconds doesn't throw an exception.
$task = new PushTask('/someUrl', ['key' => 'some value'],
['delay_seconds' => 30 * 86400]);
}
public function testConstructorUrlTooBig() {
$url = '/' . str_repeat('b', 2083);
$this->setExpectedException(
'\InvalidArgumentException',
'URL length greater than maximum of ' . PushTask::MAX_URL_LENGTH) .
'. URL: ' . $url;
$t = new PushTask($url);
}
public function testConstructorHeaderWrongType() {
$this->setExpectedException('\InvalidArgumentException',
'header must be a string. Actual type: double');
$t = new PushTask('/some-url', ['user-key' => 'user-data'],
['header' => 50.0]);
}
public function testConstructorHeaderWithoutColon() {
$this->setExpectedException('\InvalidArgumentException',
'Each header must contain a colon. Header: bad-header!');
$t = new PushTask('/some-url', ['user-key' => 'user-data'],
['header' => 'bad-header!']);
}
public function testConstructorInvalidContentType() {
$this->setExpectedException('\InvalidArgumentException',
'Content-type header may not be specified as it is set by the task.');
$t = new PushTask('/some-url', ['user-key' => 'user-data'],
['header' => 'content-type: application/pdf']);
}
public function testAddInvalidQueue() {
$this->setExpectedException('\InvalidArgumentException');
(new PushTask('/someUrl'))->add(999);
}
public function testAddTaskTooBig() {
$this->setExpectedException(
'\google\appengine\api\taskqueue\TaskQueueException',
'Task greater than maximum size of ' . PushTask::MAX_TASK_SIZE_BYTES);
// Althought 102400 is the max size, it's for the serialized proto which
// includes the URL etc.
(new PushTask('/someUrl', ['field' => str_repeat('a', 102395)]))->add();
}
public function testPushTaskSimplestAdd() {
$req = self::buildBulkAddRequest();
$resp = new TaskQueueBulkAddResponse();
$task_result = $resp->addTaskResult();
$task_result->setResult(ErrorCode::OK);
$task_result->setChosenTaskName('fred');
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $resp);
$task_name = (new PushTask('/someUrl'))->add();
$this->assertEquals('fred', $task_name);
$this->apiProxyMock->verify();
}
public function testPushTaskSimpleAddWithQueryData() {
$query_data = ['key' => 'some value'];
$req = self::buildBulkAddRequest();
$req->getAddRequest(0)->setBody(http_build_query($query_data));
$header = $req->getAddRequest(0)->addHeader();
$header->setKey('content-type');
$header->setValue('application/x-www-form-urlencoded');
$resp = new TaskQueueBulkAddResponse();
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $resp);
(new PushTask('/someUrl', $query_data))->add();
$this->apiProxyMock->verify();
}
public function testPushTaskAddToNonDefaultQueue() {
$req = self::buildBulkAddRequest();
$req->getAddRequest(0)->setQueueName('myqueue');
$resp = new TaskQueueBulkAddResponse();
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $resp);
(new PushTask('/someUrl'))->add('myqueue');
$this->apiProxyMock->verify();
}
public function testPushTaskAddBasicGetRequest() {
$req = self::buildBulkAddRequest();
$req->getAddRequest(0)->setMethod(RequestMethod::GET);
$resp = new TaskQueueBulkAddResponse();
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $resp);
(new PushTask('/someUrl', [], ['method' => 'GET']))->add();
$this->apiProxyMock->verify();
}
public function testPushTaskAddGetRequestWithQueryData() {
$req = self::buildBulkAddRequest();
$req->getAddRequest(0)->setMethod(RequestMethod::GET);
$req->getAddRequest(0)->setUrl('/someUrl?aKey=aValue');
$resp = new TaskQueueBulkAddResponse();
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $resp);
$task = new PushTask('/someUrl', ['aKey' => 'aValue'], ['method' => 'GET']);
$task->add();
$this->apiProxyMock->verify();
}
public function testPushTaskWithNameAndETA() {
$req = self::buildBulkAddRequest();
$add_req = $req->getAddRequest(0);
$add_req->setTaskName('customTaskName');
$add_req->setEtaUsec($add_req->getEtaUsec() + 5 * 1e6);
$resp = new TaskQueueBulkAddResponse();
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $resp);
$task = new PushTask('/someUrl', [],
['delay_seconds' => 5, 'name' => 'customTaskName']);
$task_name = $task->add();
$this->assertEquals('customTaskName', $task_name);
$this->apiProxyMock->verify();
}
public function testPushTaskAddWithHeader() {
$req = self::buildBulkAddRequest();
$add_req = $req->getAddRequest(0);
$header = $add_req->addHeader();
$header->setKey('custom-header');
$header->setValue('54321');
$resp = new TaskQueueBulkAddResponse();
$task_result = $resp->addTaskResult();
$task_result->setResult(ErrorCode::OK);
$task_result->setChosenTaskName('fred');
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $resp);
$task_name = (new PushTask('/someUrl', [],
['header' => 'custom-header: 54321']))->add();
$this->assertEquals('fred', $task_name);
$this->apiProxyMock->verify();
}
public function testPushTaskAddWithHeaderAndQueryData() {
$query_data = ['key' => 'some value'];
$req = self::buildBulkAddRequest();
$add_req = $req->getAddRequest(0);
$add_req->setBody(http_build_query($query_data));
$header = $add_req->addHeader();
$header->setKey('content-type');
$header->setValue('application/x-www-form-urlencoded');
$header = $add_req->addHeader();
$header->setKey('custom-header');
$header->setValue('xyz');
$resp = new TaskQueueBulkAddResponse();
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $resp);
(new PushTask('/someUrl', $query_data,
['header' => 'custom-header: xyz']))->add();
$this->apiProxyMock->verify();
}
public function testPushTaskAddWithTwoHeaders() {
$req = self::buildBulkAddRequest();
$add_req = $req->getAddRequest(0);
$header = $add_req->addHeader();
$header->setKey('custom-header');
$header->setValue('54321');
$header = $add_req->addHeader();
$header->setKey('another-custom-header');
$header->setValue('abc');
$resp = new TaskQueueBulkAddResponse();
$task_result = $resp->addTaskResult();
$task_result->setResult(ErrorCode::OK);
$task_result->setChosenTaskName('fred');
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $resp);
$task_name = (new PushTask('/someUrl', [],
['header' => "custom-header: 54321\r\n" .
'another-custom-header: abc']))->add();
$this->assertEquals('fred', $task_name);
$this->apiProxyMock->verify();
}
public function testUnknownQueueError() {
$req = self::buildBulkAddRequest();
$exception = new \google\appengine\runtime\ApplicationError(
ErrorCode::UNKNOWN_QUEUE, 'message');
$this->setExpectedException(
'\google\appengine\api\taskqueue\TaskQueueException',
'Unknown queue');
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $exception);
(new PushTask('/someUrl'))->add();
}
public function testTransientError() {
$req = self::buildBulkAddRequest();
$exception = new \google\appengine\runtime\ApplicationError(
ErrorCode::TRANSIENT_ERROR, 'message');
$this->setExpectedException(
'\google\appengine\api\taskqueue\TransientTaskQueueException');
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $exception);
(new PushTask('/someUrl'))->add();
}
public function testPermissionDeniedError() {
$req = self::buildBulkAddRequest();
$exception = new \google\appengine\runtime\ApplicationError(
ErrorCode::PERMISSION_DENIED, 'message');
$this->setExpectedException(
'\google\appengine\api\taskqueue\TaskQueueException',
'Permission Denied');
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $exception);
(new PushTask('/someUrl'))->add();
}
public function testTombstonedTaskError() {
$req = self::buildBulkAddRequest();
$exception = new \google\appengine\runtime\ApplicationError(
ErrorCode::TOMBSTONED_TASK, 'message');
$this->setExpectedException(
'\google\appengine\api\taskqueue\TaskAlreadyExistsException');
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $exception);
(new PushTask('/someUrl'))->add();
}
public function testTaskAlreadyExistsError() {
$req = self::buildBulkAddRequest();
$exception = new \google\appengine\runtime\ApplicationError(
ErrorCode::TASK_ALREADY_EXISTS, 'message');
$this->setExpectedException(
'\google\appengine\api\taskqueue\TaskAlreadyExistsException');
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $exception);
(new PushTask('/someUrl'))->add();
}
public function testTaskInvalidQueueModeError() {
$req = self::buildBulkAddRequest();
$exception = new \google\appengine\runtime\ApplicationError(
ErrorCode::INVALID_QUEUE_MODE, 'message');
$this->setExpectedException(
'\google\appengine\api\taskqueue\TaskQueueException',
'Cannot add a PushTask to a pull queue.');
$this->apiProxyMock->expectCall('taskqueue', 'BulkAdd', $req, $exception);
(new PushTask('/someUrl'))->add();
}
}