rewritten API models

This commit is contained in:
jelhan 2015-08-22 23:47:31 +02:00
parent 0ae62f31f1
commit 09c8310bb6
14 changed files with 615 additions and 370 deletions

View file

@ -1,247 +0,0 @@
<?php
/*
* handles the data for api.php
*/
class Datahandler {
// (string) folder to store data in relative to position of api.php
// webserver has to have write access to this folder
// must end with a slash
// (int) length of ids used to identify data
const ID_LENGTH = 10;
// (string) characters used to generate a new id
const ID_CHARACTERS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
public function __construct() {
// check if data folder exists and is writeable
// if data folder does not exist, try to create it
if (!is_writeable(DATA_FOLDER)) {
// check if data folder exists
if (!file_exists(DATA_FOLDER)) {
// try to create data folder
if(mkdir(DATA_FOLDER)) {
// put empty index.html in data folder to prevent directory listing
file_put_contents(DATA_FOLDER . "index.html", '');
// check if newly created data folder is writeable
if (!is_writeable(DATA_FOLDER)) {
throw new Exception("data folder created but is not writeable");
}
}
else {
throw new Exception("data folder does not exists and can not be created");
}
}
else {
throw new Exception("data folder already exists but is not writeable");
}
}
}
public function deletePoll($poll_id) {
$folder = DATA_FOLDER . "/" . $poll_id;
$user_folder = $folder . "/user";
// delete user folder
if (is_dir($user_folder)) {
$dir = opendir($user_folder);
while(false !== ($file = readdir($dir))) {
if($file === '.' || $file === '..') {
continue;
}
unlink($user_folder . '/' . $file);
}
closedir($dir);
rmdir($user_folder);
}
unlink($folder . '/poll_data');
rmdir($folder);
}
/*
* read poll data
* return false, if there is no data with this id or if data could not be read
*/
public function get($poll_id) {
// file with absolut path
$folder = DATA_FOLDER . "/" . $poll_id;
$poll_file = $folder . "/poll_data";
// check if file exists and is readable
if (!is_readable($poll_file)) {
return false;
}
$poll_data_json = file_get_contents($poll_file);
$poll_data = json_decode($poll_data_json);
// check expiration date
if (
!empty($poll_data->poll->serverExpirationDate) &&
self::isExpired($poll_data->poll->serverExpirationDate)
) {
$this->deletePoll($poll_id);
return false;
}
// set id to poll
$poll_data->poll->id = $poll_id;
$poll_data->poll->users = array();
// check for existing user
$user_folder = $folder . "/user";
if (is_dir($user_folder)) {
// read all existing user data and embedded it into poll
$files = scandir($user_folder);
// natural sorting of files array to have it in creation order
natsort($files);
// iterate over all files in folder
foreach ($files as $file) {
// embedding full user data
// read file and extend poll_data;
$user_data_json = file_get_contents($user_folder . "/" . $file);
if ($user_data_json) {
$user_data = json_decode($user_data_json);
// extend id to user object
$user_data->user->id = $poll_id . '_' . $file;
// embedd user into poll
$poll_data->poll->users[] = $user_data->user;
}
}
}
// do not include properties prefixed by server in response
foreach ($poll_data->poll as $key => $value) {
if(strpos($key, "server") === 0) {
unset($poll_data->poll->$key);
}
}
return json_encode($poll_data);
}
/*
* generates a new Id
*/
protected function generateNewId()
{
// generate random string
$randomString = '';
for ($i = 0; $i < self::ID_LENGTH; $i++) {
$characters = self::ID_CHARACTERS;
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
// check if id is already used, generate new one if necessary
if (file_exists(DATA_FOLDER."/".$randomString)) {
$randomString = $this->generateNewId();
}
return $randomString;
}
/*
* get next user id for given poll
*/
protected function getNextUserId($poll_id)
{
$user_folder = DATA_FOLDER . "/" . $poll_id . "/user";
// check if user folder exists
if (!file_exists($user_folder)) {
return 0;
}
// get all files in user folder
$files = scandir($user_folder);
// get highest existing id
$highest_id = 0;
foreach ($files as $f) {
if ((int) $f > $highest_id) {
$highest_id = (int) $f;
}
}
return $highest_id + 1;
}
static function isExpired ($expirationDateString) {
return
( $expirationDate = DateTime::createFromFormat('Y-m-d\TH:i:s.uO', $expirationDateString) ) &&
$expirationDate < new DateTime();
}
/*
* store poll data
* returns new id or false on error
*/
public function writePoll ($data) {
// get a new id
$new_id = $this->generateNewId();
$folder = DATA_FOLDER."/".$new_id;
$file = $folder . "/poll_data";
// create folder for new poll
if (!mkdir($folder)) {
return false;
}
// write poll data
if (file_put_contents($file, $data, LOCK_EX) === false) {
return false;
}
return $new_id;
}
/*
* stores user data belonging to poll
* returns new id or false on error
*/
public function writeUser ($poll_id, $data) {
// get a new id
$new_id = $this->getNextUserId($poll_id);
// check if poll exists
if (!is_dir(DATA_FOLDER."/".$poll_id)) {
throw new Exception("poll does not exists");
return false;
}
$folder = DATA_FOLDER."/".$poll_id."/user";
$file = $folder . "/" . $new_id;
// check if user folder allready exists
if (!file_exists($folder)) {
// user folder does not exists, try to create it
if (!mkdir($folder)) {
return false;
}
}
// write user data
if (file_put_contents($file, $data, LOCK_EX) === false) {
return false;
}
return $new_id;
}
}
?>

236
api/classes/model.php Normal file
View file

@ -0,0 +1,236 @@
<?php
class Model {
const ENCRYPTED_PROPERTIES = [];
const PLAIN_PROPERTIES = [];
const SERVER_PROPERTIES = [];
protected $data;
public function __construct() {
if(!defined('DATA_FOLDER')) {
throw new Exception('DATA_FOLDER is not defined');
}
if(!is_writable(DATA_FOLDER)) {
throw new Exception('DATA_FOLDER (' . DATA_FOLDER . ') is not writeable');
}
$this->data = new stdClass();
}
private static function convertFromStorage($string) {
return json_decode($string);
}
private static function convertToStorage($dataObj) {
$obj = clone($dataObj);
unset($obj->id);
return json_encode($obj);
}
/*
* get new object
*/
public static function create($data) {
if (!is_object($data)) {
throw new Exception('data must be an object');
}
$properties = array_merge(
static::ENCRYPTED_PROPERTIES,
static::PLAIN_PROPERTIES,
static::SERVER_PROPERTIES
);
// check if all properties exist on data object
foreach ($properties as $property) {
if (!property_exists($data, $property)) {
throw new Exception('property ' . $property . ' does not exist on data object');
}
}
// check that encrypted properties are valid AES
foreach (static::ENCRYPTED_PROPERTIES as $encryptedProperty) {
if (!self::isValidSJCL($data->$encryptedProperty)) {
throw new Exception('property ' . $property . ' is not valid AES');
}
}
$modelName = get_called_class();
$model = new $modelName;
foreach ($properties as $property) {
$model->set($property, $data->$property);
}
return $model;
}
public function export() {
$data = new stdClass();
$publicProperties = array_merge(
static::ENCRYPTED_PROPERTIES,
static::PLAIN_PROPERTIES,
array('id')
);
foreach($publicProperties as $publicProperty) {
$data->$publicProperty = $this->get($publicProperty);
}
if (method_exists($this, 'includeRelationships')) {
$this->includeRelationships($data);
}
return $data;
}
protected function get($property) {
if(property_exists($this->data, $property)) {
return $this->data->$property;
}
else {
return null;
}
}
protected function getDir() {
throw new Exception ('getDir must be implemented by model');
}
protected function getPath() {
throw new Exception ('getPath must be implemented by model');
}
/*
* Checks if a json string is a proper SJCL encrypted message.
* False if format is incorrect.
*
* Taken from: https://github.com/sebsauvage/ZeroBin/blob/8cae64d6eab99fb0d31868df77846285c0958ed0/index.php#L76-L109
* Copyright (c) 2012 Sébastien SAUVAGE (sebsauvage.net)
* License: https://github.com/sebsauvage/ZeroBin/blob/8cae64d6eab99fb0d31868df77846285c0958ed0/README.md
*/
private static function isValidSJCL($jsonstring) {
$accepted_keys=array('iv','v','iter','ks','ts','mode','adata','cipher','salt','ct');
// Make sure content is valid json
$decoded = json_decode($jsonstring);
if ($decoded==null) return false;
$decoded = (array)$decoded;
// Make sure required fields are present
foreach($accepted_keys as $k)
{
if (!array_key_exists($k,$decoded)) { return false; }
}
// Make sure some fields are base64 data
if (base64_decode($decoded['iv'],$strict=true)==null) { return false; }
if (base64_decode($decoded['salt'],$strict=true)==null) { return false; }
if (base64_decode($decoded['cipher'],$strict=true)==null) { return false; }
// Make sure no additionnal keys were added.
if (count(array_intersect(array_keys($decoded),$accepted_keys))!=10) { return false; }
// Reject data if entropy is too low
$ct = base64_decode($decoded['ct'], $strict=true);
if (strlen($ct) > strlen(gzdeflate($ct))) return false;
// Make sure some fields have a reasonable size.
if (strlen($decoded['iv'])>24) return false;
if (strlen($decoded['salt'])>14) return false;
return true;
}
/*
* restore object from storage
*/
public static function restore($id) {
if (!static::isValidId($id)) {
throw new Exception($id . ' is not a valid id');
}
$modelName = get_called_class();
$model = new $modelName;
$model->set('id', $id);
try {
$storageObject = file_get_contents(
$model->getPath()
);
if ($storageObject === false) {
// no poll with this id
return false;
}
}
catch (Exception $e) {
// no poll with this id
return false;
}
$data = self::convertFromStorage($storageObject);
$properties = array_merge(
static::ENCRYPTED_PROPERTIES,
static::PLAIN_PROPERTIES,
static::SERVER_PROPERTIES
);
foreach ($properties as $property) {
$model->set($property, $data->$property);
}
return $model;
}
/*
* save object to storage
* gives back new id
*/
public function save() {
// create dir for data if it does not exists
$counter = 0;
while (true) {
$this->set('id', $this->generateNewId());
try {
if (!is_dir($this->getDir())) {
if (mkdir($this->getDir()) === false) {
throw new Exception('could not create the directory for data object');
}
}
if (!is_writable($this->getDir())) {
throw new Exception('dir is not writeable');
}
// save data
if(
file_put_contents(
$this->getPath(),
self::convertToStorage($this->data),
LOCK_EX
) === false
) {
throw new Exception('write failed...');
}
}
catch (Exception $e) {
if ($counter > 5) {
throw new Exception('write failed more than five times', 0, $e);
}
$counter++;
continue;
}
// successfully run
break;
}
}
private function set($key, $value) {
$this->data->$key = $value;
}
}

76
api/classes/poll.php Normal file
View file

@ -0,0 +1,76 @@
<?php
require_once 'model.php';
require_once 'user.php';
class Poll extends model {
const ENCRYPTED_PROPERTIES = [
'anonymousUser',
'answers',
'answerType',
'creationDate',
'description',
'expirationDate',
'forceAnswer',
'isDateTime',
'options',
'pollType',
'timezone',
'title'
];
const ID_LENGTH = 10;
const ID_CHARACTERS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const PLAIN_PROPERTIES = [
'version'
];
const SERVER_PROPERTIES = [
'serverExpirationDate'
];
protected function generateNewId() {
$characters = self::ID_CHARACTERS;
$randomString = '';
for ($i = 0; $i < self::ID_LENGTH; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return $randomString;
}
protected function getDir() {
return DATA_FOLDER . $this->get('id') . '/';
}
protected function getPath() {
return $this->getDir() . 'poll_data';
}
protected function getUsers() {
$users = [];
$userDir = DATA_FOLDER . $this->get('id') . '/users/';
if (is_dir($userDir)) {
$dir = opendir($userDir);
while(false !== ($file = readdir($dir))) {
if($file === '.' || $file === '..') {
continue;
}
$users[] = User::restore($this->get('id') . '_' . $file)->export();
}
}
return $users;
}
protected function includeRelationships(&$data) {
$data->users = $this->getUsers();
}
public static function isValidId($id) {
$idCharacters = str_split($id);
return count(array_diff($idCharacters, str_split(self::ID_CHARACTERS))) === 0;
}
}

67
api/classes/user.php Normal file
View file

@ -0,0 +1,67 @@
<?php
require_once 'model.php';
require_once 'poll.php';
class User extends Model {
const ENCRYPTED_PROPERTIES = [
'creationDate',
'name',
'selections'
];
const PLAIN_PROPERTIES = [
'poll',
'version'
];
protected function generateNewId() {
$userDir = $this->getDir();
// check if user folder exists
if (!file_exists($userDir)) {
return $this->get('poll') . '_0';
}
// get all files in user folder
$files = scandir($userDir);
// get highest existing id
$highestId = 0;
foreach ($files as $f) {
if ((int) $f > $highestId) {
$highestId = (int) $f;
}
}
return $this->get('poll') .
'_' .
(string) $highestId + 1;
}
protected function getDir() {
if ($this->get('poll') !== null) {
$pollId = $this->get('poll');
}
else {
$pollId = explode('_', $this->get('id'))[0];
}
return DATA_FOLDER . $pollId . '/users/';
}
protected function getPath() {
return $this->getDir() . explode('_', $this->get('id'))[1];
}
protected function includeRelationships(&$data) {
return $data;
}
public static function isValidId($id) {
$parts = explode('_', $id);
return count($parts) === 2 &&
Poll::isValidId($parts[0]) &&
intval($parts[1]) == $parts[1];
}
}

View file

@ -12,13 +12,16 @@ else {
} }
require 'vendor/autoload.php'; require 'vendor/autoload.php';
require 'classes/datahandler.php'; require_once 'classes/poll.php';
require_once 'classes/user.php';
function pollIdIsValid($pollId) { function pollIdIsValid($pollId) {
return preg_match('/[^A-Za-z0-9]/', $pollId) === 0; return preg_match('/[^A-Za-z0-9]/', $pollId) === 0;
} }
$app = new \Slim\Slim(); $app = new \Slim\Slim(array(
'debug' => false
));
/* /*
* default response headers * default response headers
@ -27,68 +30,68 @@ $app->response->headers->set('Content-Type', 'application/json; charset=utf-8');
// prevent Internet Explorer from caching AJAX requests // prevent Internet Explorer from caching AJAX requests
$app->expires('-1'); $app->expires('-1');
/*
* api endpoints
*/
$app->get('/polls/:id', function ($pollId) use ($app) { $app->get('/polls/:id', function ($pollId) use ($app) {
if(!pollIdIsValid($pollId)) { $poll = Poll::restore($pollId);
$app->halt(400, 'requested id must only contain letters and numbers');
}
$datahandler = new Datahandler(); if (!$poll) {
$data = $datahandler->get($pollId); $app->halt(404);
if ($data === false) {
// there was no data with this id or it could not be readen
$app->response->setStatus(404);
}
else {
$app->response->setBody($data);
}
});
$app->post('/polls', function() use ($app) {
$datahandler = new Datahandler();
$pollCreatedId = $datahandler->writePoll(
$app->request->getBody()
);
if (!$pollCreatedId) {
$app->halt(500, 'saving poll failed');
} }
$app->response->setBody( $app->response->setBody(
$datahandler->get($pollCreatedId) json_encode(
array(
'poll' => $poll->export()
)
)
);
});
$app->post('/polls', function() use ($app) {
$poll = Poll::create(
json_decode(
$app->request->getBody()
)->poll
);
$poll->save();
$app->response->setBody(
json_encode(
array(
'poll' => $poll->export()
)
)
); );
}); });
$app->post('/users', function() use ($app) { $app->post('/users', function() use ($app) {
$datahandler = new Datahandler(); $user = User::create(
json_decode(
// get poll id
$dataObject = json_decode(
$app->request->getBody() $app->request->getBody()
)->user
); );
$pollId = $dataObject->user->poll; $user->save();
if (!pollIdIsValid($pollId)) {
$app->halt(400, 'poll id must only contain letters and numbers');
}
// write user
$userCreatedId = $datahandler->writeUser($pollId, $app->request->getBody());
if ($userCreatedId === false) {
$app->halt(500, 'saving user failed');
}
// add user id to user object
$dataObject->user->id = $pollId . '_' . $userCreatedId;
$app->response->setBody( $app->response->setBody(
json_encode($dataObject) json_encode(
array(
'user' => $user->export()
)
)
); );
}); });
$app->notFound(function () use ($app) { /*
// die("verdammte schieße..."); * error handling
$app->halt(404, "verdammte scheiße\n" . $app->request->getResourceUri() . "\n"); */
$app->error(function() use ($app) {
$app->halt(500);
});
$app->notFound(function() use ($app) {
$app->halt(404);
}); });
$app->run(); $app->run();

View file

@ -1,2 +1,32 @@
<?php <?php
// This is global bootstrap for autoloading // clear tmp dir
define('TEST_DATA_DIR', 'tests/_tmp/data/');
function deleteDirRecursively($dir) {
$handle = opendir($dir);
while(false !== ($entry = readdir($handle))) {
if($entry === '.' || $entry === '..') {
continue;
}
if(is_dir($dir . '/' . $entry)) {
$function = __FUNCTION__;
$function($dir .'/' . $entry);
}
else {
unlink($dir . '/' . $entry);
}
}
rmdir($dir);
}
if(!is_dir('tests/_tmp')) {
mkdir('tests/_tmp');
}
if(is_dir(TEST_DATA_DIR)) {
deleteDirRecursively(TEST_DATA_DIR);
}
mkdir(TEST_DATA_DIR);

View file

@ -1,28 +1,12 @@
<?php <?php
$pollData = [
"poll" => [ $pollJson = '{"poll":{"title":"{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}","description":"{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}","pollType":"{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}","answerType":"{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO/yFmk\"}","answers":"{\"iv\":\"WRdAwEa0DF+E83ginLYtPw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"Oaer31ct2PXkmXkzJ1EXRPM3LMf6vGfzMZqjODwey4f7EhqSCUhYov+N7AZKCAAXYVS4WR84kKizxXBK2PQBSFrlB3Bll74ED9ZzRJSJD00otMG9BbgUR90aFws+1jMBP5vpti9+POsii85zLbDPkNg/Th/C4Ufv5YWwg/4ZV0bFMyOgfdjtOWaG5YAMTGUIkz9U9+VCesYJQaTb497qTD/Wmtz8J/2pUxdL5/b5xkdh2DJ4/N5q0Kz/CEbaoKwbexnQDlSr3ldlIhs7UmBjC9gkpgG2l9fu6a0VZFBE8hvzYrw=\"}","options":"{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs/Jm5XK/thQW0phxKd0OxKt9NZ3FE/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}","creationDate":"{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p/ANinfanE/51DbcDNw==\"}","forceAnswer":"{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}","anonymousUser":"{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}","isDateTime":"{\"iv\":\"3y9OmTJDG0mLqU5zLoZwgQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"yyGaGitGrunDSpsRpw==\"}","timezone":"{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}","expirationDate":"{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}","serverExpirationDate":"2015-11-22T22:05:15.065Z","version":"v0.3.0+0ae62f31"}}';
"encryptedTitle" => "{\"iv\":\"FUekCOJ/UOf91y9etFuh9w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"q8Iy+2AOwjcWjob1uLw=\"}",
"encryptedDescription" => null,
"encryptedPollType" => "{\"iv\":\"3iCPGIdKeMFbebcuJlmuMA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"0HDBwqya/9M7Ya/Y60R34uGGlw==\"}",
"encryptedAnswerType" => "{\"iv\":\"re8c+PWzoZndx8IxQ+JZfA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"qKY/EzZQbGqvkpABchEy\"}",
"encryptedAnswers" => "{\"iv\":\"Ln9SCj+dvBzTR8hOgoPP0g==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"lVu2owiRla6/R/lZB4SeJT3iNWcb8DqsBehaSxuROdpp6t1MIJX+TuMdiOae2jp36tsCTG2BWeEnoclbPA7ddDuWpxDA6zyM4OM+svJdYt6A3FkKPSUq8uWCJXmomkPmu9t9Cx4gWxDaluYGLGjU2BNIhm2ICTnxq94WkZsEjmM6qtUW3xmRPV+xkszOby5S4dxGZuK49TEfepWqBH6VLt4sDX3NoaU3Pyo6cyQQTLjlQ6nIIvKMrdx4Uk5/03oVTupFq+XsSNCI3fJ35YmPlc8v7qRhflXzDTr2pTOXmuP5w4I=\"}",
"encryptedOptions" => "{\"iv\":\"GDp5DyeuXMj2nLIiPXRa7w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"yxHxXBvZ9DDMVY+9fNIr6qM6gaMNHXkffxkk87a1Q9rWh0E8O0AVJpl9RYdv3/QkfYRqwjyQqA==\"}",
"encryptedCreationDate" => "{\"iv\":\"grXGp40HmdMlYd8mpRJb2A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"y9gx6YaU9f6UA/RMD+2ptWXeNQ9HdPt+nkH1bwrM1J85WA==\"}",
"encryptedForceAnswer" => "{\"iv\":\"cPQxZDytHCCBzyyGB7Zhew==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"U+Xd8Wua3XPDvbPe\"}",
"encryptedAnonymousUser" => "{\"iv\":\"Rv75z29GDIbZ/RGRs+uq0Q==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"/7sj+sLWPYOWJgvslg==\"}",
"encryptedIsDateTime" => "{\"iv\":\"noz0JF1Uzv74e27gMu55Kw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"Igefluift7+Wedo1Cw==\"}",
"encryptedTimezone" => "{\"iv\":\"/sBs7oP15FsJ7qSUSHvewA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"TqJBGIjSGIcVsQ==\"}",
"expirationDate" => "2015-11-01T17:57:03.713Z",
"version" => "v0.3.0+d26cd827"
]
];
$I = new ApiTester($scenario); $I = new ApiTester($scenario);
$I->wantTo('create a poll'); $I->wantTo('create a poll');
$I->sendPOST('/polls', json_encode($pollData)); $I->sendPOST('/polls', $pollJson);
$I->seeResponseCodeIs(200); $I->seeResponseCodeIs(200);
$I->seeResponseIsJson(); $I->seeResponseIsJson();
$I->seeResponseContainsJson($pollData);
$I->seeResponseJsonMatchesJsonPath('poll.id'); $I->seeResponseJsonMatchesJsonPath('poll.id');
$pollId = $I->grabDataFromResponseByJsonPath('poll.id')[0]; $pollId = $I->grabDataFromResponseByJsonPath('poll.id')[0];
\PHPUnit_Framework_Assert::assertNotRegExp( \PHPUnit_Framework_Assert::assertNotRegExp(
@ -45,3 +29,14 @@ $users = $I->grabDataFromResponseByJsonPath('poll.users')[0];
0, 0,
'user array should be empty' 'user array should be empty'
); );
$pollData = json_decode($pollJson, true);
unset($pollData["poll"]["serverExpirationDate"]);
$I->seeResponseContainsJson(
$pollData,
'all data except serverExpirationDate is in response'
);
$I->dontSeeResponseJsonMatchesJsonPath(
'poll.serverExpirationDate',
'serverExpirationDate is not in response.'
);

View file

@ -0,0 +1,36 @@
<?php
$pollId = 'qwerQWER12';
$userJson = '{"user":{"name":"{\"iv\":\"kizIqK7FPNmRuQB7VHsMOw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"UsYMzrww3HKR8vl2TKVE\"}","selections":"{\"iv\":\"hRmiZagEhQVhw2cg6UJNrg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"2zIPGpiSC6wJHRoAMYBFPXx3qmlZg0Z/Jt/15mY+sHPLCqoAn97TKGN6KIvl/5gmgCFqLQFNo6uppCTUhljoV5y2kMtGvm0g3+NdpcejWGOeMACDPcp1mpXII87ZTfC6WrtxcWCB6UGYN8EynOdndFTGp+WVZnXCCya7YPThk/QRwoHoPWS6+TJFT9WeHV4i4kUIg2K3kdz3Op7S/c7l7KbOc8GsyjZzv0bRDnAm68/+FlJyZnvfMfU8vTxExsIsd0pBy4JBV4hg9SlCPectb5BAvBCULLDPA08prf262RUmVKJ+M3P1+5KkBQcnQwnUW/fzAQ7lqA==\"}","creationDate":"{\"iv\":\"xqdDY/A7MHLeAsoU9S/j+A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"TQOhbjveZbvdiyYpxfwNyu5pi1PLia9FApJJRmr3QoyrWA==\"}","version":"v0.3.0+0ae62f31","poll":"' . $pollId . '"}}';
$pollDir = TEST_DATA_DIR . $pollId . '/';
$usersDir = $pollDir . 'users/';
mkdir($pollDir);
$I = new ApiTester($scenario);
$I->wantTo('create a user');
$I->sendPOST('/users', $userJson);
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$I->seeResponseJsonMatchesJsonPath('user.id');
$userId = $I->grabDataFromResponseByJsonPath('user.id')[0];
\PHPUnit_Framework_Assert::assertEquals(
count(explode('_', $userId)),
2,
'user id has two parts seperated by _'
);
\PHPUnit_Framework_Assert::assertEquals(
explode('_', $userId)[0],
$pollId,
'user id starts by poll id'
);
\PHPUnit_Framework_Assert::assertEquals(
(int) explode('_', $userId)[1],
explode('_', $userId)[1],
'second part contains numbers'
);
$userData = json_decode($userJson, true);
foreach($userData["user"] as $key => $value) {
$I->seeResponseContainsJson(["user" => [$key => $value]]);
}

View file

@ -0,0 +1,6 @@
<?php
$I = new ApiTester($scenario);
$I->wantTo('get an not existing poll');
$I->sendGet('/polls/abcdEFGH12');
$I->seeResponseCodeIs(404);
$I->seeResponseEquals('');

View file

@ -1,25 +1,8 @@
<?php <?php
$pollId = "abcDEF0123"; $pollId = "abcDEF0123";
$pollData = [ $pollJson = '{"anonymousUser":"{\"iv\":\"SOqei2Y7QZt1PFR6IXR4qg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"WAg0oSjCiMAO+JqzIg==\"}","answers":"{\"iv\":\"WRdAwEa0DF+E83ginLYtPw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"Oaer31ct2PXkmXkzJ1EXRPM3LMf6vGfzMZqjODwey4f7EhqSCUhYov+N7AZKCAAXYVS4WR84kKizxXBK2PQBSFrlB3Bll74ED9ZzRJSJD00otMG9BbgUR90aFws+1jMBP5vpti9+POsii85zLbDPkNg\/Th\/C4Ufv5YWwg\/4ZV0bFMyOgfdjtOWaG5YAMTGUIkz9U9+VCesYJQaTb497qTD\/Wmtz8J\/2pUxdL5\/b5xkdh2DJ4\/N5q0Kz\/CEbaoKwbexnQDlSr3ldlIhs7UmBjC9gkpgG2l9fu6a0VZFBE8hvzYrw=\"}","answerType":"{\"iv\":\"z1V+GmSWJxSng0bXxnYNRA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ZDf5sBxR6rO+DdO\/yFmk\"}","creationDate":"{\"iv\":\"DBKid4Yiyr61GVLigJj20w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"ORRPzySTa6vt7GQrJOGBvNZXXq4p\/ANinfanE\/51DbcDNw==\"}","description":"{\"iv\":\"aohDHKaO7c7Fl5vIueBkcA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"+ygmsnYAsEBLZRUV\"}","expirationDate":"{\"iv\":\"Y0O4n9+Tj+4LSmLoFTaNow==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"jCz8DFIS5eLI4tsjfpr+F4lG+F27BItHPdj85o5+gaDayA==\"}","forceAnswer":"{\"iv\":\"P5Dg5Y9fS7EFxvqzP8u20A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"90G4jQ1PbalZyyzz\"}","isDateTime":"{\"iv\":\"3y9OmTJDG0mLqU5zLoZwgQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"yyGaGitGrunDSpsRpw==\"}","options":"{\"iv\":\"79HYzanMnjtgvBMowUWHaA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"HuFz0AFCpupdmXYdCcAX4OiwpMs\/Jm5XK\/thQW0phxKd0OxKt9NZ3FE\/rMAiYVqRKBqFp+KLhBnbs9ewTFW0Xrvw6paTnvpY9Ftcz1MB\"}","pollType":"{\"iv\":\"suOomfYe6kKBxjln091tCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"7iDQ2y571OBiJNxdaUY0PjqlgQ==\"}","timezone":"{\"iv\":\"l0VeY3CPUvMtoDPrw7+iCw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"qBlHlZ0nLd3mqA==\"}","title":"{\"iv\":\"szAOrvhM+bODnldJJP0pGw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3gtpUTAyVK4=\",\"ct\":\"KwMkE7bneP0MX6hQEnM=\"}","version":"v0.3.0+0ae62f31","serverExpirationDate":"2015-11-22T22:05:15.065Z"}';
"poll" => [
"encryptedTitle" => "{\"iv\":\"FUekCOJ/UOf91y9etFuh9w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"q8Iy+2AOwjcWjob1uLw=\"}",
"encryptedDescription" => null,
"encryptedPollType" => "{\"iv\":\"3iCPGIdKeMFbebcuJlmuMA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"0HDBwqya/9M7Ya/Y60R34uGGlw==\"}",
"encryptedAnswerType" => "{\"iv\":\"re8c+PWzoZndx8IxQ+JZfA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"qKY/EzZQbGqvkpABchEy\"}",
"encryptedAnswers" => "{\"iv\":\"Ln9SCj+dvBzTR8hOgoPP0g==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"lVu2owiRla6/R/lZB4SeJT3iNWcb8DqsBehaSxuROdpp6t1MIJX+TuMdiOae2jp36tsCTG2BWeEnoclbPA7ddDuWpxDA6zyM4OM+svJdYt6A3FkKPSUq8uWCJXmomkPmu9t9Cx4gWxDaluYGLGjU2BNIhm2ICTnxq94WkZsEjmM6qtUW3xmRPV+xkszOby5S4dxGZuK49TEfepWqBH6VLt4sDX3NoaU3Pyo6cyQQTLjlQ6nIIvKMrdx4Uk5/03oVTupFq+XsSNCI3fJ35YmPlc8v7qRhflXzDTr2pTOXmuP5w4I=\"}",
"encryptedOptions" => "{\"iv\":\"GDp5DyeuXMj2nLIiPXRa7w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"yxHxXBvZ9DDMVY+9fNIr6qM6gaMNHXkffxkk87a1Q9rWh0E8O0AVJpl9RYdv3/QkfYRqwjyQqA==\"}",
"encryptedCreationDate" => "{\"iv\":\"grXGp40HmdMlYd8mpRJb2A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"y9gx6YaU9f6UA/RMD+2ptWXeNQ9HdPt+nkH1bwrM1J85WA==\"}",
"encryptedForceAnswer" => "{\"iv\":\"cPQxZDytHCCBzyyGB7Zhew==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"U+Xd8Wua3XPDvbPe\"}",
"encryptedAnonymousUser" => "{\"iv\":\"Rv75z29GDIbZ/RGRs+uq0Q==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"/7sj+sLWPYOWJgvslg==\"}",
"encryptedIsDateTime" => "{\"iv\":\"noz0JF1Uzv74e27gMu55Kw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"Igefluift7+Wedo1Cw==\"}",
"encryptedTimezone" => "{\"iv\":\"/sBs7oP15FsJ7qSUSHvewA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"3GQYS4Ils60=\",\"ct\":\"TqJBGIjSGIcVsQ==\"}",
"encryptedExpirationDate" => "{\"iv\":\"Jmclqi7ZDjKZ1O9t6HgkyQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"CnrCEy2AX1c=\",\"ct\":\"jg0bpNknoJcB4CAumtSEjyRNy845vzbfM6QQeNx0M60ZAw==\"}",
"serverExpirationDate" => "2015-11-01T17:57:03.713Z",
"version" => "v0.3.0+d26cd827"
]
];
mkdir('tests/_tmp/data/' . $pollId); mkdir('tests/_tmp/data/' . $pollId);
file_put_contents('tests/_tmp/data/' . $pollId . '/poll_data', json_encode($pollData)); file_put_contents('tests/_tmp/data/' . $pollId . '/poll_data', $pollJson);
$I = new ApiTester($scenario); $I = new ApiTester($scenario);
$I->wantTo('get an existing poll'); $I->wantTo('get an existing poll');
@ -27,9 +10,24 @@ $I->sendGET('/polls/' . $pollId);
$I->seeResponseCodeIs(200); $I->seeResponseCodeIs(200);
$I->seeResponseIsJson(); $I->seeResponseIsJson();
unset($pollData["poll"]["serverExpirationDate"]); $pollData = json_decode($pollJson, true);
$I->seeResponseContainsJson($pollData); unset($pollData["serverExpirationDate"]);
$I->seeResponseContainsJson(
array(
'poll' => $pollData
)
);
$I->seeResponseContainsJson(["poll" => ["id" => $pollId]]); $I->seeResponseContainsJson(["poll" => ["id" => $pollId]]);
$I->dontSeeResponseJsonMatchesJsonPath('poll.serverExpirationDate'); $I->dontSeeResponseJsonMatchesJsonPath('poll.serverExpirationDate');
$I->seeResponseJsonMatchesJsonPath('poll.users');
$users = $I->grabDataFromResponseByJsonPath('poll.users')[0];
\PHPUnit_Framework_Assert::assertTrue(
is_array($users),
'user should be an array'
);
\PHPUnit_Framework_Assert::assertEquals(
count($users),
0,
'user array should be empty'
);

View file

@ -0,0 +1,67 @@
<?php
$pollId = "xyzQWER1234";
$pollJson = '{"anonymousUser":"{\"iv\":\"gVHZSXyMm10Fn+kDooa7uw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"GJsQQYA7TdAa+v3Rvg==\"}","answers":"{\"iv\":\"aK1JcI3viLPIlOO45K+ePA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"Bx4SRcww+hJ46NIiVcWBUZHADADX\/XPsxXMx4XzMQZWqu6M0690D4oTflSRJoqxe0egxdfMOUxuWhmACG\/UYXSYJQjcSg+QTq6KJbaXG+SvsCMZ7iz12a\/uf9lXyiag4IbLldgL4vE3LfZO6oih\/o\/yG4hechjNdSkqUa2IvsRbXWB2aHen6a5Ch5WjqWrr4xRRrukPvf7aumilT2Cf0LswHJ2fwYNilylV0h9oegKYp+qWphm4SL8x2ogRemSCt7u7ByEOwZV0w6D9bz9RvGLTRRLJaLIm\/VlE3k7R6Hz1vyps=\"}","answerType":"{\"iv\":\"ILkAzgUfAGNUtLr7CbEJEQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"nMOp+QApQGgP9dwefNpi\"}","creationDate":"{\"iv\":\"6tWbieK03uXUR+E0AMbs0A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"YkkLVBkFyx4xFldZ7qnDESG0teHJmXaPMUB05p9L0xUIMg==\"}","description":"{\"iv\":\"fWvHh47So4WBNfEHXrwLiA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"5W7nauOakSoFD52V\"}","expirationDate":"{\"iv\":\"HRsMvEQaoCp8QdqBGHevnA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"LXYamNRDyhIY5xY+CLqI4GHbocc9NoHQtePKU9fHpJn9zg==\"}","forceAnswer":"{\"iv\":\"bh4iZ4pKe0GnXcM764702g==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"q5VBynWGotXRrc2P\"}","isDateTime":"{\"iv\":\"mlDCtvsJZaDlZD9kqfJHuA==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"53g42C6Am+0s25\/DsA==\"}","options":"{\"iv\":\"ZneP\/x45NGh\/DC26GI4kvg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"4MvV9SNQq2dB6b\/MdX47R0KaRSfyZOZMEVUFDv7G3\/EcDBv7Z0pgSU9JXoF8BoSOz40rYrRtTw==\"}","pollType":"{\"iv\":\"j3P6eN0ZmNMMxLTAVD6gjQ==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"opwiZHAQi+I8R5HDxLfLK59DcQ==\"}","timezone":"{\"iv\":\"HKkSqcJONggGT9QQ+jZdUg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"BANN8sJlk8JK9A==\"}","title":"{\"iv\":\"4DX7dAJt7JIBHaR1V0Ct8A==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"f1VUZf69nB94TF3\/HA==\"}","version":"v0.3.0+0ae62f31","serverExpirationDate":"2015-11-22T20:35:03.764Z"}';
$user1Json = '{"name":"{\"iv\":\"GJXPSYYmTVfEsst31BD92w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"smbuxujRLF/xNS1syTWFguE=\"}","selections":"{\"iv\":\"WXlkCM3pGyD+SyIhccmHYg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"UHs2ArTVTHfS04J3HNvwrV68xFqra0Q0qtVpLZRLPUbqgdXmn960FjEFiLa9Cnk5OcKEQtbOgWJXehHlAJFAWFpdyDE/gcCOKG62c2/hVauroeycQE16wDCjrEwor/FV9HxNjTbYxoJASjCy9ROLdOUhSlFfQfHLcvVpsTgPpnKPr7aYBgODu5XIdRI8Pf5nYF0K96KE9xn+mkg3ZjyXWSk1LBaBDpIOCrcj+8zl7tLtkgPNfh8aNVgQHC5hRrIbL9kZwD4XXEUPImRFITEy2rUWKp8Q0/jAgHCnqSzOLOFS8KrJOktDX++DjK3cB4oT5ttLvcRxRQ==\"}","creationDate":"{\"iv\":\"3/KUsITWzJNWx5fDzYC9Xg==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"llgQE2GpZDg1ZKRRlXliGlF09VsrVZ1R57EIQ21+dej5yg==\"}","version":"v0.3.0+0ae62f31","poll":"l3zyFJUWcQ"}';
$user2Json = '{"name":"{\"iv\":\"DVNTCOFfACEOrgtVNVMyww==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"eug7bstOm7T\/CCFs32o=\"}","selections":"{\"iv\":\"ubEuXoXzw4QFuzjAyvXC6w==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"PfQ4v4hkBf+S0GX7JmnIp2LO5sh\/jg9nEIPn8NeU2Gn9Rb7cqsjCLgKOQ2xkiIzCyimVBOYg0fjGCyzM\/b6ZPQnY+86teNGogEteD4fjqGHhO832FNOy7Oci0YC8VAM1x9SlQNBI9V+vFc706JbZgwA8JY46UMiGK3HU49pgbYMpdnWEmt4dGzGrLMnNbh4J1Or5JydKmrp4dXaMiiggSXhmUTgBJSRhF7dxQm16oaA1lJpCWoQBvu+WTJv34LnBXHbgg6JcAEEONaQRw1jmMeqo36tQJxSdjiVfcDWzMifWiz\/nhQMqDHkc19iOAmDBo2Rf+yrGWA==\"}","creationDate":"{\"iv\":\"Sj4pVW\/maHa8DUNFHhyUrw==\",\"v\":1,\"iter\":1000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"mhO9ROu+dr4=\",\"ct\":\"HaY9MtEzVmEg3dxtI\/pfaIrsivBJSNeC5l5iJHQrvyYQGA==\"}","version":"v0.3.0+0ae62f31","poll":"l3zyFJUWcQ"}';
$pollDir = 'tests/_tmp/data/' . $pollId . '/';
$userDir = $pollDir . 'users/';
mkdir($pollDir);
file_put_contents($pollDir . 'poll_data', $pollJson);
mkdir($userDir);
file_put_contents($userDir . '0', $user1Json);
file_put_contents($userDir . '1', $user2Json);
$I = new ApiTester($scenario);
$I->wantTo('get an existing poll');
$I->sendGET('/polls/' . $pollId);
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$pollData = json_decode($pollJson, true);
unset($pollData["serverExpirationDate"]);
foreach($pollData as $key => $value) {
$I->seeResponseContainsJson(
array(
'poll' => array(
$key => $value
)
)
);
}
$I->seeResponseContainsJson(["poll" => ["id" => $pollId]]);
$I->dontSeeResponseJsonMatchesJsonPath('poll.serverExpirationDate');
$I->seeResponseJsonMatchesJsonPath('poll.users');
$users = $I->grabDataFromResponseByJsonPath('poll.users')[0];
\PHPUnit_Framework_Assert::assertTrue(
is_array($users),
'user should be an array'
);
\PHPUnit_Framework_Assert::assertEquals(
count($users),
2,
'user array should contain 2 users'
);
$I->seeResponseContainsJson([
"poll" => [
"users" => [
json_decode($user1Json, true),
json_decode($user2Json, true)
]
]
]);
$I->seeResponseJsonMatchesJsonPath('poll.users.0.id');
$I->seeResponseJsonMatchesJsonPath('poll.users.1.id');
$user1Id = $I->grabDataFromResponseByJsonPath('poll.users.0.id')[0];
$user2Id = $I->grabDataFromResponseByJsonPath('poll.users.1.id')[0];
\PHPUnit_Framework_Assert::assertTrue(
$user1Id !== $user2Id,
'user ids are unique'
);
\PHPUnit_Framework_Assert::assertEquals(
explode('_', $user1Id)[0],
$pollId,
'user id starts by poll id'
);

View file

@ -0,0 +1,6 @@
<?php
$I = new ApiTester($scenario);
$I->wantTo('raise an error');
$I->sendGet('/polls/ab!cd');
$I->seeResponseCodeIs(500);
$I->seeResponseEquals('');

View file

@ -1,31 +1 @@
<?php <?php
// clear tmp dir
$tmpDataDir = 'tests/_tmp/data';
function deleteDirRecursively($dir) {
$handle = opendir($dir);
while(false !== ($entry = readdir($handle))) {
if($entry === '.' || $entry === '..') {
continue;
}
if(is_dir($dir . '/' . $entry)) {
$function = __FUNCTION__;
$function($dir .'/' . $entry);
}
else {
unlink($dir . '/' . $entry);
}
}
rmdir($dir);
}
if(!is_dir('tests/_tmp')) {
mkdir('tests/_tmp');
}
if(!is_dir($tmpDataDir)) {
mkdir($tmpDataDir);
}
deleteDirRecursively($tmpDataDir);

View file

@ -6,7 +6,9 @@ export default DS.Model.extend({
// properties // properties
title : DS.attr('string'), title : DS.attr('string'),
description : DS.attr('string'), description : DS.attr('string', {
defaultValue: ''
}),
pollType : DS.attr('string'), pollType : DS.attr('string'),
answerType: DS.attr('string'), answerType: DS.attr('string'),
answers : DS.attr('array'), answers : DS.attr('array'),