From 0d4208bd4934d83654fc3893867b2557546b404a Mon Sep 17 00:00:00 2001 From: Matthias Held Date: Mon, 18 Jun 2018 14:14:17 +0200 Subject: Add Nextcloud application --- tests/Unit/Db/FileOperationMapperTest.php | 245 ++++++++++++++++++++++++++++++ tests/Unit/Db/FileOperationTest.php | 93 ++++++++++++ tests/Unit/Db/MapperTestUtility.php | 227 +++++++++++++++++++++++++++ 3 files changed, 565 insertions(+) create mode 100644 tests/Unit/Db/FileOperationMapperTest.php create mode 100644 tests/Unit/Db/FileOperationTest.php create mode 100644 tests/Unit/Db/MapperTestUtility.php (limited to 'tests/Unit/Db') diff --git a/tests/Unit/Db/FileOperationMapperTest.php b/tests/Unit/Db/FileOperationMapperTest.php new file mode 100644 index 0000000..20fb584 --- /dev/null +++ b/tests/Unit/Db/FileOperationMapperTest.php @@ -0,0 +1,245 @@ + + * @author Matthias Held + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\RansomwareDetection\tests\Unit\Db; + +use OCA\RansomwareDetection\Db\FileOperation; +use OCA\RansomwareDetection\Db\FileOperationMapper; + +class FileOperationMapperTest extends MapperTestUtility +{ + /** @var FileOperationMapper */ + protected $mapper; + + /** @var array */ + protected $fileOperations; + + public function setUp() + { + parent::setUp(); + + $this->mapper = new FileOperationMapper($this->db); + + // create mock FileOperation + $fileOperation1 = new FileOperation(); + $fileOperation2 = new FileOperation(); + + $this->fileOperations = [$fileOperation1, $fileOperation2]; + + $this->twoRows = [ + ['id' => $this->fileOperations[0]->getId()], + ['id' => $this->fileOperations[1]->getId()], + ]; + } + + public function testFind() + { + $userId = 'john'; + $id = 3; + $rows = [['id' => $this->fileOperations[0]->getId()]]; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` '. + 'WHERE `id` = ? AND `user_id` = ?'; + + $this->setMapperResult($sql, [$id, $userId], $rows); + + $result = $this->mapper->find($id, $userId); + $this->assertEquals($this->fileOperations[0], $result); + } + + public function testFindNotFound() + { + $userId = 'john'; + $id = 3; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` '. + 'WHERE `id` = ? AND `user_id` = ?'; + + $this->setMapperResult($sql, [$id, $userId]); + $this->setExpectedException( + '\OCP\AppFramework\Db\DoesNotExistException' + ); + $this->mapper->find($id, $userId); + } + + public function testFindMoreThanOneResultFound() + { + $userId = 'john'; + $id = 3; + $rows = $this->twoRows; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` '. + 'WHERE `id` = ? AND `user_id` = ?'; + + $this->setMapperResult($sql, [$id, $userId], $rows); + $this->setExpectedException( + '\OCP\AppFramework\Db\MultipleObjectsReturnedException' + ); + $this->mapper->find($id, $userId); + } + + public function testFindOneByFileName() + { + $userId = 'john'; + $name = 'test'; + $rows = [['id' => $this->fileOperations[0]->getId()]]; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` '. + 'WHERE `original_name` = ? AND `user_id` = ?'; + + $this->setMapperResult($sql, [$name, $userId], $rows); + + $result = $this->mapper->findOneByFileName($name, $userId); + $this->assertEquals($this->fileOperations[0], $result); + } + + public function testFindOneByFileNameNotFound() + { + $userId = 'john'; + $name = 'test'; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` '. + 'WHERE `original_name` = ? AND `user_id` = ?'; + + $this->setMapperResult($sql, [$name, $userId]); + $this->setExpectedException( + '\OCP\AppFramework\Db\DoesNotExistException' + ); + $this->mapper->findOneByFileName($name, $userId); + } + + public function testFindOneByFileNameMoreThanOneResultFound() + { + $userId = 'john'; + $name = 'test'; + $rows = $this->twoRows; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` '. + 'WHERE `original_name` = ? AND `user_id` = ?'; + + $this->setMapperResult($sql, [$name, $userId], $rows); + $this->setExpectedException( + '\OCP\AppFramework\Db\MultipleObjectsReturnedException' + ); + $this->mapper->findOneByFileName($name, $userId); + } + + public function testFindOneWithHighestId() + { + $userId = 'john'; + $rows = [['id' => $this->fileOperations[0]->getId()]]; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` WHERE `user_id` = ?'. + 'ORDER BY id DESC LIMIT 1'; + + $this->setMapperResult($sql, [$userId], $rows); + + $result = $this->mapper->findOneWithHighestId($userId); + $this->assertEquals($this->fileOperations[0], $result); + } + + public function testFindOneWithHighestIdNotFound() + { + $userId = 'john'; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` WHERE `user_id` = ?'. + 'ORDER BY id DESC LIMIT 1'; + + $this->setMapperResult($sql, [$userId]); + $this->setExpectedException( + '\OCP\AppFramework\Db\DoesNotExistException' + ); + $this->mapper->findOneWithHighestId($userId); + } + + public function testFindOneWithHighestIdMoreThanOneResultFound() + { + $userId = 'john'; + $rows = $this->twoRows; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` WHERE `user_id` = ?'. + 'ORDER BY id DESC LIMIT 1'; + + $this->setMapperResult($sql, [$userId], $rows); + $this->setExpectedException( + '\OCP\AppFramework\Db\MultipleObjectsReturnedException' + ); + $this->mapper->findOneWithHighestId($userId); + } + + public function testFindAll() + { + $userId = 'john'; + $rows = $this->twoRows; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` WHERE `user_id` = ?'; + + $this->setMapperResult($sql, [$userId], $rows); + $result = $this->mapper->findAll([$userId]); + $this->assertEquals($this->fileOperations, $result); + } + + public function testFindSequenceById() + { + $userId = 'john'; + $sequence = '1'; + $rows = $this->twoRows; + $sql = 'SELECT * FROM `*PREFIX*ransomware_detection_file_operation` WHERE `sequence` = ? AND `user_id` = ?'; + + $this->setMapperResult($sql, [$sequence, $userId], $rows); + $result = $this->mapper->findSequenceById([$sequence, $userId]); + $this->assertEquals($this->fileOperations, $result); + } + + public function testDelete() + { + $fileOperation = new FileOperation(); + $fileOperation->setId(3); + + $sql = 'DELETE FROM `*PREFIX*ransomware_detection_file_operation` WHERE `id` = ?'; + $arguments = [$fileOperation->getId()]; + + $this->setMapperResult($sql, $arguments, [], null, null, true); + + $this->mapper->delete($fileOperation); + } + + public function testDeleteById() + { + $userId = 'john'; + $fileOperation = new FileOperation(); + $fileOperation->setUserId($userId); + $fileOperation->setId(3); + + $sql = 'DELETE FROM `*PREFIX*ransomware_detection_file_operation` WHERE `id` = ? AND `user_id` = ?'; + $arguments = [$fileOperation->getId(), $userId]; + + $this->setMapperResult($sql, $arguments, [], null, null, true); + + $this->mapper->deleteById($fileOperation->getId(), $userId); + } + + public function testDeleteSequenceById() + { + $userId = 'john'; + $fileOperation = new FileOperation(); + $fileOperation->setId(3); + $fileOperation->setUserId($userId); + $fileOperation->setSequence(1); + + $sql = 'DELETE FROM `*PREFIX*ransomware_detection_file_operation` WHERE `sequence` = ? AND `user_id` = ?'; + $arguments = [$fileOperation->getSequence(), $userId]; + + $this->setMapperResult($sql, $arguments, [], null, null, true); + + $this->mapper->deleteSequenceById($fileOperation->getSequence(), $userId); + } +} diff --git a/tests/Unit/Db/FileOperationTest.php b/tests/Unit/Db/FileOperationTest.php new file mode 100644 index 0000000..9a86ea9 --- /dev/null +++ b/tests/Unit/Db/FileOperationTest.php @@ -0,0 +1,93 @@ + + * @author Matthias Held + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\RansomwareDetection\tests\Unit\Db; + +use OCA\RansomwareDetection\Monitor; +use OCA\RansomwareDetection\Analyzer\EntropyResult; +use OCA\RansomwareDetection\Analyzer\FileNameResult; +use OCA\RansomwareDetection\Classifier; +use OCA\RansomwareDetection\Db\FileOperation; +use Test\TestCase; + +class FileOperationTest extends TestCase +{ + /** @var FileOperation */ + protected $entity; + + public function setUp() + { + parent::setUp(); + + $this->entity = new FileOperation(); + } + + public function dataFileOperation() + { + $data = [ + ['field' => 'userId', 'value' => 'john'], + ['field' => 'path', 'value' => 'files/'], + ['field' => 'originalName', 'value' => 'test.jpg'], + ['field' => 'newName', 'value' => 'test.jpg'], + ['field' => 'type', 'value' => 'file'], + ['field' => 'mimeType', 'value' => 'image/jpg'], + ['field' => 'size', 'value' => 123], + ['field' => 'corrupted', 'value' => true], + ['field' => 'timestamp', 'value' => new \DateTime()], + ['field' => 'command', 'value' => Monitor::WRITE], + ['field' => 'command', 'value' => Monitor::READ], + ['field' => 'command', 'value' => Monitor::RENAME], + ['field' => 'command', 'value' => Monitor::DELETE], + ['field' => 'sequence', 'value' => 1], + ['field' => 'entropy', 'value' => 7.99], + ['field' => 'standardDeviation', 'value' => 0.004], + ['field' => 'fileNameEntropy', 'value' => 4.0], + ['field' => 'fileClass', 'value' => EntropyResult::NORMAL], + ['field' => 'fileClass', 'value' => EntropyResult::ENCRYPTED], + ['field' => 'fileClass', 'value' => EntropyResult::COMPRESSED], + ['field' => 'fileNameClass', 'value' => FileNameResult::NORMAL], + ['field' => 'fileNameClass', 'value' => FileNameResult::SUSPICIOUS_FILE_EXTENSION], + ['field' => 'fileNameClass', 'value' => FileNameResult::SUSPICIOUS_FILE_NAME], + ['field' => 'fileNameClass', 'value' => FileNameResult::SUSPICIOUS], + ['field' => 'suspicionClass', 'value' => Classifier::NO_INFORMATION], + ['field' => 'suspicionClass', 'value' => Classifier::NOT_SUSPICIOUS], + ['field' => 'suspicionClass', 'value' => Classifier::MIDDLE_LEVEL_OF_SUSPICION], + ['field' => 'suspicionClass', 'value' => Classifier::LOW_LEVEL_OF_SUSPICION], + ['field' => 'suspicionClass', 'value' => Classifier::HIGH_LEVEL_OF_SUSPICION], + ]; + + return $data; + } + + /** + * @dataProvider dataFileOperation + * + * @param string $field + * @param mixed $value + */ + public function testFileOperation($field, $value) + { + $setMethod = 'set'.ucfirst($field); + $this->entity->$setMethod($value); + $getMethod = 'get'.ucfirst($field); + $this->assertEquals($this->entity->$getMethod(), $value); + } +} diff --git a/tests/Unit/Db/MapperTestUtility.php b/tests/Unit/Db/MapperTestUtility.php new file mode 100644 index 0000000..fc4bff6 --- /dev/null +++ b/tests/Unit/Db/MapperTestUtility.php @@ -0,0 +1,227 @@ + + * @copyright 2012 Bernhard Posselt dev@bernhard-posselt.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + */ + +namespace OCA\RansomwareDetection\Tests\Unit\Db; + +use Test\TestCase; + +/** + * Simple utility class for testing mappers. + */ +abstract class MapperTestUtility extends TestCase +{ + protected $db; + private $query; + private $queryAt; + private $prepareAt; + private $fetchAt; + private $iterators; + + /** + * Run this function before the actual test to either set or initialize the + * db. After this the db can be accessed by using $this->db. + */ + protected function setUp() + { + parent::setUp(); + + $this->db = $this->getMockBuilder( + '\OCP\IDBConnection' + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->query = $this->createMock('\PDOStatement'); + $this->queryAt = 0; + $this->prepareAt = 0; + $this->iterators = []; + $this->fetchAt = 0; + } + + /** + * Checks if an array is associative. + * + * @param array $array + * + * @return bool true if associative + */ + private function isAssocArray(array $array) + { + return array_values($array) !== $array; + } + + /** + * Returns the correct PDO constant based on the value type. + * + * @param $value + * + * @return PDO constant + */ + private function getPDOType($value) + { + switch (gettype($value)) { + case 'integer': + return \PDO::PARAM_INT; + case 'boolean': + return \PDO::PARAM_BOOL; + default: + return \PDO::PARAM_STR; + } + } + + /** + * Create mocks and set expected results for database queries. + * + * @param string $sql the sql query that you expect to receive + * @param array $arguments the expected arguments for the prepare query + * method + * @param array $returnRows the rows that should be returned for the result + * of the database query. If not provided, it wont be assumed that fetch + * will be called on the result + */ + protected function setMapperResult( + $sql, + $arguments = array(), + $returnRows = array(), + $limit = null, + $offset = null, + $expectClose = false + ) { + if ($limit === null && $offset === null) { + $this->db->expects($this->at($this->prepareAt)) + ->method('prepare') + ->with($this->equalTo($sql)) + ->will(($this->returnValue($this->query))); + } elseif ($limit !== null && $offset === null) { + $this->db->expects($this->at($this->prepareAt)) + ->method('prepare') + ->with($this->equalTo($sql), $this->equalTo($limit)) + ->will(($this->returnValue($this->query))); + } elseif ($limit === null && $offset !== null) { + $this->db->expects($this->at($this->prepareAt)) + ->method('prepare') + ->with( + $this->equalTo($sql), + $this->equalTo(null), + $this->equalTo($offset) + ) + ->will(($this->returnValue($this->query))); + } else { + $this->db->expects($this->at($this->prepareAt)) + ->method('prepare') + ->with( + $this->equalTo($sql), + $this->equalTo($limit), + $this->equalTo($offset) + ) + ->will(($this->returnValue($this->query))); + } + + $this->iterators[] = new ArgumentIterator($returnRows); + + $iterators = $this->iterators; + $fetchAt = $this->fetchAt; + + $this->query->expects($this->any()) + ->method('fetch') + ->will($this->returnCallback( + function () use ($iterators, $fetchAt) { + $iterator = $iterators[$fetchAt]; + $result = $iterator->next(); + + if ($result === false) { + $fetchAt++; + } + + $this->queryAt++; + + return $result; + } + )); + + if ($this->isAssocArray($arguments)) { + foreach ($arguments as $key => $argument) { + $pdoConstant = $this->getPDOType($argument); + $this->query->expects($this->at($this->queryAt)) + ->method('bindValue') + ->with( + $this->equalTo($key), + $this->equalTo($argument), + $this->equalTo($pdoConstant) + ); + $this->queryAt++; + } + } else { + $index = 1; + foreach ($arguments as $argument) { + $pdoConstant = $this->getPDOType($argument); + $this->query->expects($this->at($this->queryAt)) + ->method('bindValue') + ->with( + $this->equalTo($index), + $this->equalTo($argument), + $this->equalTo($pdoConstant) + ); + $index++; + $this->queryAt++; + } + } + + $this->query->expects($this->at($this->queryAt)) + ->method('execute') + ->will($this->returnCallback(function ($sql, $p = null, $o = null, $s = null) { + })); + $this->queryAt++; + + if ($expectClose) { + $closing = $this->at($this->queryAt); + } else { + $closing = $this->any(); + } + $this->query->expects($closing)->method('closeCursor'); + $this->queryAt++; + + $this->prepareAt++; + $this->fetchAt++; + } +} + +class ArgumentIterator +{ + private $arguments; + + public function __construct($arguments) + { + $this->arguments = $arguments; + } + + public function next() + { + $result = array_shift($this->arguments); + if ($result === null) { + return false; + } else { + return $result; + } + } +} -- cgit v1.2.3