diff --git a/src/PostgresDriver/Cursor.php b/src/PostgresDriver/Cursor.php
deleted file mode 100644
index 55a8e82d941dec8ac47978b08794d331c7134bbb..0000000000000000000000000000000000000000
--- a/src/PostgresDriver/Cursor.php
+++ /dev/null
@@ -1,196 +0,0 @@
-<?php declare(strict_types = 1);
-/**
- * This file is part of mappi/store.
- */
-
-namespace Grifart\Mappi\Store\PostgresDriver;
-
-use Dibi\Connection;
-
-/**
- * PostgreSQL cursor driver
- *
- * Basic envelope over PostgreSQL's cursor. This cursor envelope does not know
- * cursor position, thus cannot do boundary checks. If you reach end, you simply
- * get no more values.
- *
- * @package Grifart\Mappi\Store\PostgresDriver
- */
-class Cursor implements ICursor
-{
-
-	/** @var Connection */
-	private $connection;
-
-	/** @var string */
-	private $name;
-
-	public function __construct(Connection $connection, string $name)
-	{
-		$this->connection = $connection;
-		$this->name = $name;
-		// todo: does cursor really exits?
-		// todo: close; for now automatically closed on transaction end
-	}
-
-	public function getConnection() : Connection
-	{
-		return $this->connection;
-	}
-
-	public function getName() : string
-	{
-		return $this->name;
-	}
-
-	public function moveTo(int $index)
-	{
-		$result = $this->getConnection()->query(
-			"MOVE ABSOLUTE %i IN %n",
-			$index,
-			$this->getName()
-		);
-		if($result !== 1) {
-			throw CursorException::cursorOverflow();
-		}
-	}
-
-	public function moveBy(int $rows)
-	{
-		$result = $this->getConnection()->query(
-			"MOVE RELATIVE %i IN %n",
-			$rows,
-			$this->getName()
-		);
-		if($result !== 1) {
-			throw CursorException::cursorOverflow();
-		}
-	}
-
-	public function moveToBeginning()
-	{
-		$this->getConnection()->query(
-			"MOVE ABSOLUTE 0 IN %n;",
-			$this->getName()
-		);
-	}
-
-	public function moveToFirst()
-	{
-		$result = $this->getConnection()->query(
-			"MOVE FIRST IN %n;", // == ABSOLUTE 1
-			$this->getName()
-		);
-		if($result !== 1) {
-			throw CursorException::cursorOverflow();
-		}
-	}
-
-	public function moveToLast()
-	{
-		$result = $this->getConnection()->query(
-			"MOVE LAST IN %n", // == ABSOLUTE -1
-			$this->getName()
-		);
-		if($result !== 1) {
-			throw CursorException::cursorOverflow();
-		}
-	}
-
-	public function moveToEnd()
-	{
-		$this->getConnection()->query(
-			"MOVE ABSOLUTE -1 IN %n; MOVE NEXT IN %n;",
-			$this->getName(),
-			$this->getName()
-		);
-	}
-
-	// ----------------- FETCH ---------------------
-
-	public function fetch(int $rows)
-	{
-		$forward = $rows >= 0;
-		$rows = abs($rows);
-		return $this->connection->query(
-			$forward ? "FETCH FORWARD %i FROM %n" : "FETCH BACKWARD %i FROM %n",
-			$rows,
-			$this->getName()
-		)->fetchAll();
-
-	}
-
-	public function fetchNext()
-	{
-		// todo: throw and exception when 0 undefined
-		$row = $this->fetch(1);
-		if(isset($row[0])) {
-			return $row[0];
-		}
-		return FALSE;
-	}
-
-	public function fetchCurrent()
-	{
-		return $this->fetchOneBy(0);
-	}
-
-	public function fetchNextSingle()
-	{
-		$row = $this->fetchNext();
-		if($row === FALSE) {
-			throw CursorException::noDataToFetch();
-		}
-		return current(
-			$row->toArray()
-		);
-	}
-
-	public function fetchCurrentSingle()
-	{
-		$row = $this->fetchCurrent();
-		if($row === FALSE) {
-			throw CursorException::noDataToFetch();
-		}
-		return current(
-			$row->toArray()
-		);
-	}
-
-	public function fetchOneAt(int $index)
-	{
-		return $this->connection->query(
-			"FETCH ABSOLUTE %i FROM %n",
-			$index,
-			$this->getName()
-		)->fetch();
-	}
-
-	public function fetchOneBy(int $rows)
-	{
-		return $this->connection->query(
-			"FETCH RELATIVE %i FROM %n",
-			$rows,
-			$this->getName()
-		)->fetch();
-	}
-
-	public function fetchRemaining()
-	{
-		return $this->connection->query(
-			"FETCH FORWARD ALL FROM %n",
-			$this->getName()
-		)->fetchAll();
-	}
-
-	public function fetchForegoing()
-	{
-		return $this->connection->query(
-			"FETCH BACKWARD ALL FROM %n",
-			$this->getName()
-		)->fetchAll();
-	}
-
-
-
-}
\ No newline at end of file
diff --git a/src/PostgresDriver/CursorDriver.php b/src/PostgresDriver/CursorDriver.php
new file mode 100644
index 0000000000000000000000000000000000000000..b9fd8b302f720f4ebd8e73e52430a741079485cd
--- /dev/null
+++ b/src/PostgresDriver/CursorDriver.php
@@ -0,0 +1,145 @@
+<?php declare(strict_types = 1);
+/**
+ * This file is part of mappi/store.
+ */
+
+namespace Grifart\Mappi\Store\PostgresDriver;
+
+use Dibi\Connection;
+
+/**
+ * PostgreSQL cursor driver
+ *
+ * Basic envelope over PostgreSQL's cursor. This cursor envelope does not know
+ * cursor position, thus cannot do boundary checks. If you reach end, you simply
+ * get no more values. You can check if you reached the end by `isHeadOnRecord()`.
+ *
+ * @package Grifart\Mappi\Store\PostgresDriver
+ */
+class CursorDriver implements ICursorDriver
+{
+
+	/** @var Connection */
+	private $connection;
+
+	/** @var string */
+	private $name;
+
+	/** @var bool */
+	private $headOnRecord = false;
+
+	public function __construct(Connection $connection, string $name)
+	{
+		$this->connection = $connection;
+		$this->name = $name;
+		// todo: does cursor really exits?
+		// todo: close; for now automatically closed on transaction end
+	}
+
+	public function getConnection() : Connection
+	{
+		return $this->connection;
+	}
+
+	public function getName() : string
+	{
+		return $this->name;
+	}
+
+	public function isOnRecord() : bool
+	{
+		return $this->headOnRecord;
+	}
+
+	public function moveTo(int $index)
+	{
+		if($index < 0) {
+			throw new \InvalidArgumentException("Negative index not supported. Use moveFromEndTo() instead.");
+		}
+		$this->headOnRecord = !! $this->getConnection()->query(
+			"MOVE ABSOLUTE %i IN %n",
+			$index,
+			$this->getName()
+		);
+	}
+
+	public function moveFromEndTo(int $index)
+	{
+		if($index < 0) {
+			throw new \InvalidArgumentException("Negative index not supported. Use moveTo() instead.");
+		}
+		if($index === 0) { // END position
+			$this->headOnRecord = !! $this->getConnection()->query(
+				"MOVE ABSOLUTE -1 IN %n; MOVE NEXT IN %n;",
+				$this->getName(),
+				$this->getName()
+			);
+			return;
+		}
+
+		$negativeIndex = -$index;
+		$this->headOnRecord = !! $this->getConnection()->query(
+			"MOVE ABSOLUTE %i IN %n",
+			$negativeIndex,
+			$this->getName()
+		);
+	}
+
+	public function moveBy(int $rows)
+	{
+		$this->headOnRecord = !! $this->getConnection()->query(
+			"MOVE RELATIVE %i IN %n",
+			$rows,
+			$this->getName()
+		);
+	}
+
+	// ----------------- FETCH ---------------------
+
+	public function fetchRange(int $rows) : array
+	{
+		$forward = $rows >= 0;
+		$numberOfRowsSQLClause = ($rows === self::FETCH_FOREGOING || $rows === self::FETCH_REMAINING) ?
+			 "ALL" : abs($rows);
+
+		$recs = $this->connection->query(
+			$forward ? "FETCH FORWARD %sql FROM %n" : "FETCH BACKWARD %sql FROM %n",
+			$numberOfRowsSQLClause,
+			$this->getName()
+		)->fetchAll();
+
+		if($rows === 0) {
+			$this->headOnRecord = count($recs) === 1;
+		} else {
+			// todo: test this properly!!
+			$this->headOnRecord = count($recs) === abs($rows);
+		}
+		// todo: DibiRecord to array
+		return $recs;
+	}
+
+	public function fetchOneAt(int $index) : array
+	{
+		$record = $this->connection->query(
+			"FETCH ABSOLUTE %i FROM %n",
+			$index,
+			$this->getName()
+		)->fetch();
+		$this->headOnRecord = $record !== FALSE;
+		if($record === FALSE) return NULL;
+		return $record->toArray();
+	}
+
+	public function fetchOneBy(int $rows) : array
+	{
+		$record = $this->connection->query(
+			"FETCH RELATIVE %i FROM %n",
+			$rows,
+			$this->getName()
+		)->fetch();
+		$this->headOnRecord = $record !== FALSE;
+		if($record === FALSE) return NULL;
+		return $record->toArray();
+	}
+
+}
\ No newline at end of file
diff --git a/src/PostgresDriver/CursorFactory.php b/src/PostgresDriver/CursorDriverFactory.php
similarity index 81%
rename from src/PostgresDriver/CursorFactory.php
rename to src/PostgresDriver/CursorDriverFactory.php
index 5fb5206b1e5954180700cf3a0dc8fc331281eda5..fd8bc6306b3cece470eefd442645f7fc4e9ed286 100644
--- a/src/PostgresDriver/CursorFactory.php
+++ b/src/PostgresDriver/CursorDriverFactory.php
@@ -7,7 +7,7 @@ namespace Grifart\Mappi\Store\PostgresDriver;
 
 use Dibi\Connection;
 
-class CursorFactory implements ICursorFactory
+class CursorDriverFactory
 {
 	/** @var Connection */
 	private $connection;
@@ -22,16 +22,16 @@ class CursorFactory implements ICursorFactory
 	}
 
 	/**
-	 * Creates new instance of Cursor
+	 * Creates new instance of CursorDriver
 	 *
 	 * Warning: creating cursor requires to be IN transaction block.
 	 * Please enter transaction before calling create()
 	 *
 	 * @param string $sql
 	 * @param bool $scroll
-	 * @return ICursor
+	 * @return ICursorDriver
 	 */
-	public function create(string $sql, bool $scroll) : ICursor
+	public function create(string $sql, bool $scroll) : ICursorDriver
 	{
 		$this->connection->query(
 			"DECLARE %n %SQL CURSOR FOR (%SQL)",
@@ -40,7 +40,7 @@ class CursorFactory implements ICursorFactory
 			$sql
 		);
 		
-		return new Cursor(
+		return new CursorDriver(
 			$this->connection,
 			$name
 		);
diff --git a/src/PostgresDriver/ICursorDriver.php b/src/PostgresDriver/ICursorDriver.php
new file mode 100644
index 0000000000000000000000000000000000000000..5948303360616e73f276275139a73022467189de
--- /dev/null
+++ b/src/PostgresDriver/ICursorDriver.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * This file is part of mappi/store.
+ */
+namespace Grifart\Mappi\Store\PostgresDriver;
+use Dibi\Connection;
+
+/**
+ * Represents PostgreSQL cursor
+ * @link http://www.postgresql.org/docs/9.5/static/plpgsql-cursors.html
+ * @link http://www.postgresql.org/docs/8.1/static/sql-fetch.html
+ * @link http://www.postgresql.org/docs/8.1/static/sql-move.html
+ * @package Grifart\Mappi\Store\PostgresDriver
+ */
+interface ICursorDriver
+{
+	const FETCH_REMAINING = PHP_INT_MAX;
+	const FETCH_FOREGOING = PHP_INT_MIN;
+
+	public function getName() : string;
+
+	public function isOnRecord() : bool;
+
+	// ----- move ----
+
+	/**
+	 * @param int $rows
+	 * @return void
+	 */
+	public function moveBy(int $rows);
+
+	/**
+	 * @param int $index
+	 * @return void
+	 */
+	public function moveTo(int $index);
+
+	/**
+	 * @param int $index
+	 * @return mixed
+	 */
+	public function moveFromEndTo(int $index);
+
+//	/**
+//	 * Move cursor on the first row (index=1)
+//	 * @see fetchCurrent()
+//	 * @see fetchCurrentSingle()
+//	 * @return void
+//	 */
+//	public function moveToFirst();
+//
+//	/**
+//	 * Move cursor on the last row
+//	 * @see fetchCurrent()
+//	 * @see fetchCurrentSingle()
+//	 * @return void
+//	 */
+//	public function moveToLast();
+//
+//	/**
+//	 * Moves cursor to position BEFORE first row (index=0)
+//	 * @see fetchNext()
+//	 * @see fetchNextSingle()
+//	 * @return void
+//	 */
+//	public function moveToBeginning();
+//
+//	/**
+//	 * Moves cursor to position AFTER last row
+//	 * @return void
+//	 */
+//	public function moveToEnd();
+	
+	/**
+	 * Fetch the next/previous count rows. FORWARD 0 re-fetches the current row.
+	 * @param int $rows
+	 * @return array
+	 */
+	public function fetchRange(int $rows) : array;
+
+	/**
+	 * Fetch the count'th row of the query, or the abs(count)'th row from the end if count is negative. Position before first row or after last row if count is out of range; in particular, ABSOLUTE 0 positions before the first row.
+	 * @param int $index
+	 * @return array
+	 */
+	public function fetchOneAt(int $index) : array;
+
+	/**
+	 * Fetch the count'th succeeding row, or the abs(count)'th prior row if count is negative. RELATIVE 0 re-fetches the current row, if any.
+	 * @param int $rows
+	 * @return array
+	 */
+	public function fetchOneBy(int $rows) : array;
+
+//	/**
+//	 * Fetch all remaining rows.
+//	 * @return array of remaining rows
+//	 */
+//	public function fetchRemaining() : array;
+//
+//	/**
+//	 * Fetch all prior rows (scanning backwards).
+//	 * @return array of foregoing rows
+//	 */
+//	public function fetchForegoing() : array;
+}
\ No newline at end of file
diff --git a/src/PostgresDriver/ICursorDriverFactory.php b/src/PostgresDriver/ICursorDriverFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..5f8a6cef3ed6d145491e6d25a5fac74786c58857
--- /dev/null
+++ b/src/PostgresDriver/ICursorDriverFactory.php
@@ -0,0 +1,11 @@
+<?php declare(strict_types = 1);
+/**
+ * This file is part of mappi/store.
+ */
+
+namespace Grifart\Mappi\Store\PostgresDriver;
+
+interface ICursorDriverFactory
+{
+	public function create(string $sql, bool $scroll) : ICursorDriver;
+}
\ No newline at end of file
diff --git a/tests/Store/PostgresDriver/CursorDriverTest.phpt b/tests/Store/PostgresDriver/CursorDriverTest.phpt
new file mode 100644
index 0000000000000000000000000000000000000000..f897177ae1693fc0c9c03874e2cd4c93aa7ae2b3
--- /dev/null
+++ b/tests/Store/PostgresDriver/CursorDriverTest.phpt
@@ -0,0 +1,336 @@
+<?php
+/**
+ * @testCase
+ */
+
+namespace Grifart\Mappi\Tests\Store\Store\PostgresDriver;
+
+use Grifart\Mappi\Store\PostgresDriver\CursorDriver;
+use Grifart\Mappi\Store\PostgresDriver\CursorDriverFactory;
+use Grifart\Mappi\Store\PostgresDriver\CursorException;
+use Grifart\Mappi\Store\PostgresDriver\CursorFactory;
+use Grifart\Mappi\Store\PostgresDriver\ICursorDriver;
+use Grifart\Mappi\Tests\Store\BaseTest;
+use Tester\Assert;
+
+require_once __DIR__ . "/../../bootstrap.php";
+
+class CursorDriverTest extends BaseTest
+{
+	/** @var ICursorDriver */
+	private $uut;
+
+	protected function setUp()
+	{
+		global $connection, $SQL_thousandRowsAscending;
+		$connection->begin();
+
+		$factory = new CursorDriverFactory($connection);
+		$this->uut = $factory->create($SQL_thousandRowsAscending, true);
+//		parent::setUp();
+	}
+
+	public function tearDown()
+	{
+		global $connection;
+		$connection->rollback();
+
+//		parent::tearDown();
+	}
+
+
+	private function helper_fetchNextSingle()
+	{
+		$rows = $this->uut->fetchRange(1);
+		if(count($rows) === 0) return NULL;
+		return $rows[0]["n"];
+	}
+
+	private function helper_fetchCurrentSingle()
+	{
+		$rows = $this->uut->fetchRange(0);
+		if(count($rows) === 0) return NULL;
+		return $rows[0]["n"];
+	}
+
+	private function helper_fetchPrevSingle()
+	{
+		$rows = $this->uut->fetchRange(-1);
+		if(count($rows) === 0) return NULL;
+		return $rows[0]["n"];
+	}
+
+
+
+	// initial position
+	public function test_givenCursor_whenGetCurrentRow_thenGetNothing()
+	{
+		Assert::count(0, $this->uut->fetchRange(0)); // must fail because initial position is 0
+		Assert::count(0, $this->uut->fetchRange(-1));
+		Assert::count(1, $this->uut->fetchRange(1));
+	}
+
+	public function test_givenInitialPosition_whenFetch_thenGetFirstRow()
+	{
+		Assert::same(1, $this->helper_fetchNextSingle());
+	}
+
+	// fetch( x == 0 )
+	public function test_givenSecondPosition_whenFetch0_thenGetCurrentRow()
+	{
+		// head:           .
+		// index: 0  1  2  3  4
+		// value:    1  2  3  4
+		$this->uut->moveTo(3);
+		Assert::same(3, $this->helper_fetchCurrentSingle());
+
+		$result = $this->uut->fetchRange(0); // rows backwards + read current
+
+		Assert::count(1, $result);
+		Assert::same(3, current($result[0]->toArray()));
+	}
+
+	// fetch( x > 0 )
+	public function test_givenSecondPosition_whenFetchForward_thenGetRowsAfterCursor()
+	{
+		// head:           .
+		// index: 0  1  2  3  4
+		// value:    1  2  3  4
+		$this->uut->moveTo(3);
+		Assert::same(3, $this->helper_fetchCurrentSingle());
+
+		// head:                 .
+		// index: 0  1  2  3  4  5  6
+		// value:    1  2  3  4  5  6
+		$result = $this->uut->fetchRange(2); // rows backwards + read current
+		// todo: split into fetch decorator for those special fetch* methods
+
+		Assert::count(2, $result);
+		Assert::same(4, current($result[0]->toArray()));
+		Assert::same(5, current($result[1]->toArray()));
+		Assert::same(5, $this->helper_fetchCurrentSingle());
+	}
+
+	// fetch( x < 0 )
+	public function test_givenSecondPosition_whenFetchBackwards_thenGetRowsBeforeCursor()
+	{
+		// head:           .
+		// index: 0  1  2  3  4
+		// value:    1  2  3  4
+		$this->uut->moveTo(3);
+		Assert::same(3, $this->helper_fetchCurrentSingle());
+
+		// head:     .
+		// index: 0  1  2  3  4
+		// value:    1  2  3  4
+		$result = $this->uut->fetchRange(-2); // rows backwards + read current
+		// todo: split into fetch decorator for those special fetch* methods
+
+		Assert::count(2, $result);
+		Assert::same(2, current($result[0]->toArray()));
+		Assert::same(1, current($result[1]->toArray()));
+		Assert::same(1, $this->helper_fetchCurrentSingle());
+	}
+
+
+	// moveToBeginning()
+	public function test_givenLastPosition_whenMoveToTheBeginning_thenFetchingNextRowWillBeFirstRow()
+	{
+		// Arrange
+		$this->uut->moveFromEndTo(1);
+
+		// Act
+		$this->uut->moveTo(0);
+
+		//Assert
+		Assert::same(1, $this->helper_fetchNextSingle());
+	}
+
+
+	// moveToFirst()
+	public function test_givenLastPosition_whenMoveToTheFirst_thenFetchingCurrentWillBeFirstRow()
+	{
+		// Arrange
+		$this->uut->moveFromEndTo(1);
+
+		// Act
+		$this->uut->moveTo(1);
+
+		//Assert
+		Assert::same(1, $this->helper_fetchCurrentSingle());
+	}
+
+
+	// moveToEnd()
+	public function test_givenInitialPosition_whenMoveToEnd_thenFetchingOneBacwardsWillBeTheLastRow()
+	{
+		$this->uut->moveFromEndTo(0);
+
+		Assert::null($this->helper_fetchCurrentSingle());
+
+		$data = $this->helper_fetchPrevSingle();
+		Assert::same(1000, $data);
+	}
+
+
+	// moveToLast()
+	public function test_givenInitialPosition_whenMoveToLast_thenFetchingCurrentWillBeLastRow()
+	{
+		$this->uut->moveFromEndTo(1);
+		Assert::same(1000, $this->helper_fetchCurrentSingle());
+
+		Assert::null($this->helper_fetchNextSingle());
+	}
+
+
+	// moveBy()
+	public function test_givenInitialPosition_whenMoveBy2_thenGetSecondValue()
+	{
+		$this->uut->moveBy(2);
+		Assert::same(2, $this->helper_fetchCurrentSingle());
+	}
+
+	public function test_givenSecondPosition_whenMoveOneBack_thenGetFirstValue()
+	{
+		$this->uut->moveTo(2);
+		$this->uut->moveBy(-1);
+		Assert::same(1, $this->helper_fetchCurrentSingle());
+		$this->uut->moveBy(-1);
+		Assert::null($this->helper_fetchCurrentSingle());
+	}
+
+
+	// fetchOneAt()
+	public function test_givenInitialPosition_whenFetch5_thenGetFive()
+	{
+		$data = $this->uut->fetchOneAt(5);
+		Assert::same(5, $data["n"]);
+	}
+
+	public function test_givenNonInitialPosition_whenFetch5_thenGetFive()
+	{
+		$this->uut->moveTo(234); // wherever
+
+		$data = $this->uut->fetchOneAt(5);
+		Assert::same(5, $data["n"]);
+	}
+
+	// fetchRemaining()
+	public function test_given5BeforeEndPosition_whenFetchRemaining_thenGetLastFile()
+	{
+		$this->uut->moveTo(995);
+
+		$result = $this->uut->fetchRange(ICursorDriver::FETCH_REMAINING);
+
+		Assert::count(5, $result);
+		Assert::same(996,  current($result[0]->toArray()));
+		Assert::same(997,  current($result[1]->toArray()));
+		Assert::same(998,  current($result[2]->toArray()));
+		Assert::same(999,  current($result[3]->toArray()));
+		Assert::same(1000, current($result[4]->toArray()));
+
+		Assert::null($this->helper_fetchCurrentSingle());
+		Assert::equal(1000, $this->helper_fetchPrevSingle());
+
+	}
+
+
+	// fetchRemaining()
+	public function test_given5AfterStart_whenFetchForegoing_thenGetFirstFive()
+	{
+		$this->uut->moveTo(6);
+
+		$result = $this->uut->fetchRange(ICursorDriver::FETCH_FOREGOING);
+
+		Assert::count(5, $result);
+		Assert::same(5, current($result[0]->toArray()));
+		Assert::same(4, current($result[1]->toArray()));
+		Assert::same(3, current($result[2]->toArray()));
+		Assert::same(2, current($result[3]->toArray()));
+		Assert::same(1, current($result[4]->toArray()));
+
+		Assert::null($this->helper_fetchCurrentSingle());
+	}
+
+
+	// edge cases:
+	public function test_givenInitialPosition_whenMoveToLeft_thenGetError()
+	{
+		Assert::false($this->uut->isOnRecord()); // initial position
+
+		// start: index === 0
+		$this->uut->moveBy(-1);
+		Assert::false($this->uut->isOnRecord());
+
+		// index === 0
+		$data = $this->uut->fetchRange(-1);
+		Assert::count(0, $data);
+
+		$firstValue = $this->helper_fetchNextSingle();
+		Assert::equal(1, $firstValue);
+		// index === 1
+	}
+
+	public function test_givenEndPosition_whenMoveToRight_thenStayInPlace()
+	{
+		$this->uut->moveFromEndTo(0);
+
+		Assert::null($this->helper_fetchNextSingle());
+		Assert::null($this->helper_fetchCurrentSingle());
+		Assert::equal(1000, $this->helper_fetchPrevSingle());
+	}
+
+
+	public function test_giveSomePosition_whenMoveToAfterEnd_thenWillBeAtTheEnd()
+	{
+		$this->uut->moveTo(25); // some position
+
+		$this->uut->moveTo(9999);
+
+		Assert::null($this->helper_fetchCurrentSingle());
+		Assert::equal(1000, $this->helper_fetchPrevSingle());
+	}
+
+	public function test_givenFirstPosition_whenMoveToLeft_thenWillBeAtBeginning()
+	{
+		// Arrange
+		Assert::false($this->uut->isOnRecord());
+		$this->uut->moveTo(1);
+		Assert::true($this->uut->isOnRecord());
+
+		// Act
+		$this->uut->moveBy(-1);
+
+		// Assert
+		Assert::false($this->uut->isOnRecord());
+		Assert::null($this->helper_fetchCurrentSingle());
+		Assert::null($this->helper_fetchPrevSingle());
+
+		Assert::equal(1, $this->helper_fetchNextSingle());
+		Assert::true($this->uut->isOnRecord());
+	}
+
+	public function test_givenLastPosition_whenMoveToRight_thenReachTheEnd()
+	{
+		$this->uut->moveFromEndTo(1);
+		Assert::true($this->uut->isOnRecord());
+
+		$this->uut->moveBy(1);
+
+		Assert::false($this->uut->isOnRecord());
+		Assert::null($this->helper_fetchCurrentSingle());
+		Assert::null($this->helper_fetchNextSingle());
+
+		Assert::equal(1000, $this->helper_fetchPrevSingle());
+		Assert::true($this->uut->isOnRecord());
+	}
+
+	public function test_giveSomePosition_whenMoveToBeginning_thenWillBeAtBeginning()
+	{
+		$this->uut->moveTo(42);
+		$this->uut->moveTo(0);
+		Assert::equal(1,$this->helper_fetchNextSingle());
+	}
+}
+
+(new CursorDriverTest())->run();