From 7440860ae4e2762b36f8d022c4e0b2b7aae6e842 Mon Sep 17 00:00:00 2001
From: pbarroca <pbarroca@git-test.afi-sa.fr>
Date: Thu, 19 Jul 2012 14:42:06 +0000
Subject: [PATCH] =?UTF-8?q?Multimedia:=20Prise=20en=20charge=20de=20l'auth?=
 =?UTF-8?q?entification=20pour=20un=20poste=20et=20cr=C3=A9ation=20r=C3=A9?=
 =?UTF-8?q?servation=20virtuelle=20si=20possible?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .gitattributes                                |   1 +
 .../opac/controllers/AbonneController.php     |   2 +-
 library/Class/Multimedia/Device.php           | 131 +++++++
 library/Class/Multimedia/DeviceGroup.php      |  30 ++
 library/Class/Multimedia/DeviceHold.php       |  29 +-
 library/Class/Multimedia/Location.php         |  61 ++-
 .../AbonneControllerMultimediaTest.php        | 357 ++++++++++--------
 tests/library/Class/Multimedia/DeviceTest.php | 217 +++++++++++
 8 files changed, 658 insertions(+), 170 deletions(-)
 create mode 100644 tests/library/Class/Multimedia/DeviceTest.php

diff --git a/.gitattributes b/.gitattributes
index 3ade8d4fc52..b607dfc7367 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3680,6 +3680,7 @@ tests/library/Class/ModererTest.php -text
 tests/library/Class/MultiUpload/FactoryTest.php -text
 tests/library/Class/MultiUpload/HandlersTest.php -text
 tests/library/Class/MultiUploadTest.php -text
+tests/library/Class/Multimedia/DeviceTest.php -text
 tests/library/Class/MultimediaTest.php -text
 tests/library/Class/NewsletterMailingTest.php -text
 tests/library/Class/NewsletterSubscriptionTest.php -text
diff --git a/application/modules/opac/controllers/AbonneController.php b/application/modules/opac/controllers/AbonneController.php
index 811e2904c8a..76db7896408 100644
--- a/application/modules/opac/controllers/AbonneController.php
+++ b/application/modules/opac/controllers/AbonneController.php
@@ -516,7 +516,7 @@ class AbonneController extends Zend_Controller_Action {
 		$response->date_naissance = $user->getDateNaissanceIso8601();
 
 		if (null != ($device = $request->getDevice())
-			and null != ($hold = Class_Multimedia_DeviceHold::getLoader()->getCurrentHoldOfUserOnDevice($user, $device))
+			and null != ($hold = $device->getCurrentHoldForUser($user))
 		) {
 			$response->auth = 1;
 			$response->until = date('c', $hold->getEnd());
diff --git a/library/Class/Multimedia/Device.php b/library/Class/Multimedia/Device.php
index e4d1b79898a..ac9864b9c8c 100644
--- a/library/Class/Multimedia/Device.php
+++ b/library/Class/Multimedia/Device.php
@@ -60,6 +60,9 @@ class Multimedia_DeviceLoader extends Storm_Model_Loader {
 
 
 class Class_Multimedia_Device extends Storm_Model_Abstract {
+	/** @var Class_TimeSource */
+	protected static $_time_source;
+		
 	protected $_loader_class = 'Multimedia_DeviceLoader';
 	protected $_table_name = 'multimedia_device';
 	protected $_belongs_to = array(
@@ -77,6 +80,29 @@ class Class_Multimedia_Device extends Storm_Model_Abstract {
 	}
 
 
+	/**
+	 * @category testing
+	 * @return int
+	 */
+	public function getCurrentTime() {
+		return self::getTimeSource()->time();
+	}
+
+
+	/** @return Class_TimeSource */
+	public static function getTimeSource() {
+		if (null == self::$_time_source)
+			self::$_time_source = new Class_TimeSource();
+		return self::$_time_source;
+	}
+
+
+	/** @param $time_source Class_TimeSource */
+	public static function setTimeSource($time_source) {
+		self::$_time_source = $time_source;
+	}
+
+
 	/**
 	 * @param $start int
 	 * @param $end int
@@ -98,6 +124,7 @@ class Class_Multimedia_Device extends Storm_Model_Abstract {
 		return 0 < $this->numberOfHoldBetweenTimes($start, $end);
 	}
 
+
 	/**
 	 * @param $start int
 	 * @param $end int
@@ -109,6 +136,70 @@ class Class_Multimedia_Device extends Storm_Model_Abstract {
 	}
 
 
+	/**
+	 * @param $user Class_Users
+	 * @return Class_Multimedia_DeviceHold
+	 */
+	public function getCurrentHoldForUser($user) {
+		if (null !== ($hold = $this->getCurrentHold())
+			and $user->getId() == $hold->getIdUser())
+			return $hold;
+
+		return $this->autoHoldByUser($user, $hold);
+	}
+
+
+	/**
+	 * @param $user Class_Users
+	 * @param $current_hold Class_Multimedia_DeviceHold
+	 * @return Class_Multimedia_DeviceHold
+	 */
+	public function autoHoldByUser($user, $current_hold) {
+		// pas de résa auto, on sort
+		if (!$this->isAutohold())
+			return null;
+
+		// une résa courante et on est dans le délai d'auth, on sort
+		if (null !== $current_hold
+			and $this->getCurrentTime() <= ($current_hold->getStart() + (60 * $this->getAuthDelay())))
+			return null;
+
+		// si je n'ai pas de début de créneau, on sort
+		if (null == ($start = $this->getPreviousStartTime()))
+			return null;
+
+		// fin de créneau par défaut selon config
+		$end = $start + (60 * $this->getAutoholdSlotsMax() * $this->getSlotSize());
+				
+		// si on dépasse la fin de journée on se limite à la fin de journée
+		if ($end > ($next_closing = $this->getMaxTimeForToday()))
+			$end = $next_closing;
+				
+		// si on dépasse la prochaine résa on se limite au début de la prochaine résa
+		if (null != ($next_start = $this->getNextHoldStart())
+			and $end > $next_start)
+			$end = $next_start;
+
+		if ($end <= $start)
+			return null;
+
+		$hold = Class_Multimedia_DeviceHold::getLoader()
+				->newInstance()
+				->setDevice($this)
+				->setUser($user)
+				->setStart($start)
+				->setEnd($end);
+		return $hold;
+	}
+
+
+	/** @return Class_Multimedia_DeviceHold */
+	public function getCurrentHold() {
+		return Class_Multimedia_DeviceHold::getLoader()
+				->getHoldOnDeviceAtTime($this, $this->getCurrentTime());
+	}
+
+
 	/** @return boolean */
 	public function isDisabled() {
 		return 1 == $this->getDisabled();
@@ -132,4 +223,44 @@ class Class_Multimedia_Device extends Storm_Model_Abstract {
 	public function getAuthDelay() {
 		return $this->getGroup()->getAuthDelay();
 	}
+
+
+	/** @return boolean */
+	public function isAutohold() {
+		return $this->getGroup()->isAutohold();
+	}
+
+
+	/** @return int */
+	public function getPreviousStartTime() {
+		return $this->getGroup()->getPreviousStartTime();
+	}
+
+
+	/** @return int */
+	public function getNextHoldStart() {
+		$from = $this->getCurrentTime();
+		$to = $this->getMaxTimeForToday();
+		if (null != ($hold = Class_Multimedia_DeviceHold::getLoader()
+				                   ->getFirstHoldOnDeviceBetweenTimes($this, $from, $to)))
+			return $hold->getStart();
+	}
+
+
+	/** @return int */
+	public function getMaxTimeForToday() {
+		return $this->getGroup()->getMaxTimeForToday();
+	}
+
+
+	/** @return int */
+	public function getAutoholdSlotsMax() {
+		return $this->getGroup()->getAutoholdSlotsMax();
+	}
+
+
+	/** @return int */
+	public function getSlotSize() {
+		return $this->getGroup()->getSlotSize();
+	}
 }
\ No newline at end of file
diff --git a/library/Class/Multimedia/DeviceGroup.php b/library/Class/Multimedia/DeviceGroup.php
index 0988bf516fc..fd9ef849f7c 100644
--- a/library/Class/Multimedia/DeviceGroup.php
+++ b/library/Class/Multimedia/DeviceGroup.php
@@ -85,4 +85,34 @@ class Class_Multimedia_DeviceGroup extends Storm_Model_Abstract {
 	public function getAuthDelay() {
 		return $this->getLocation()->getAuthDelay();
 	}
+
+
+	/** @return boolean */
+	public function isAutohold() {
+		return $this->getLocation()->isAutohold();
+	}
+
+
+	/** @return int */
+	public function getPreviousStartTime() {
+		return $this->getLocation()->getPreviousStartTime();
+	}
+
+
+	/** @return int */
+	public function getMaxTimeForToday() {
+		return $this->getLocation()->getMaxTimeForToday();
+	}
+
+
+	/** @return int */
+	public function getAutoholdSlotsMax() {
+		return $this->getLocation()->getAutoholdSlotsMax();
+	}
+
+
+	/** @return int */
+	public function getSlotSize() {
+		return $this->getLocation()->getSlotSize();
+	}
 }
\ No newline at end of file
diff --git a/library/Class/Multimedia/DeviceHold.php b/library/Class/Multimedia/DeviceHold.php
index a7d711fb838..98ba1424c87 100644
--- a/library/Class/Multimedia/DeviceHold.php
+++ b/library/Class/Multimedia/DeviceHold.php
@@ -70,18 +70,15 @@ class Multimedia_DeviceHoldloader extends Storm_Model_Loader {
 
 
 	/**
-	 * @param $user Class_Users
 	 * @param $device Class_Multimedia_Device
+	 * @param $time int
 	 * @return Class_Multimedia_DeviceHold
-	 */
-	public function getCurrentHoldOfUserOnDevice($user, $device) {
-		$min_start = $start = time();
-		$min_start -= 60 * $device->getAuthDelay();
+	 */	
+	public function getHoldOnDeviceAtTime($device, $time) {
 		$holds = $this->findAll($this->getTable()->select()
-			                       ->where('id_user = ' . $user->getId())
 			                       ->where('id_device = ' . $device->getId())
-			                       ->where('start >= ' . $min_start)
-			                       ->where('start <= ' . $start));
+			                       ->where('start <= ' . $time)
+			                       ->where('end >= ' . $time));
 
 		if (count($holds) == 0)
 			return null;
@@ -89,6 +86,22 @@ class Multimedia_DeviceHoldloader extends Storm_Model_Loader {
 		$this->cacheInstance($holds[0]);
 		return $holds[0];
 	}
+		
+
+	/**
+	 * @param $device Class_Multimedia_Device
+	 * @param $from int
+	 * @param $to int
+	 * @return Class_Multimedia_DeviceHold
+	 */
+	public function getFirstHoldOnDeviceBetweenTimes($device, $from, $to) {
+		return $this->findFirstBy(array(
+				'role' => 'device',
+				'model' => $device,
+				'where' => '(start >= ' . (int)$from . ' and start < ' . (int)$to . ')',
+				'order' => 'start'
+		));
+	}
 
 
 	/**
diff --git a/library/Class/Multimedia/Location.php b/library/Class/Multimedia/Location.php
index ca9e7eeb49d..efb3d8ea52e 100644
--- a/library/Class/Multimedia/Location.php
+++ b/library/Class/Multimedia/Location.php
@@ -36,19 +36,15 @@ class Multimedia_LocationLoader extends Storm_Model_Loader {
 
 	/**
 	 * @param $increment int
+	 * @param $from int
+	 * @param $to int
 	 * @return array
 	 */
 	public function getPossibleHours($increment, $from = null, $to = null) {
 		if (0 == $increment)
 			return array();
 
-		if (null == $from)
-			$from = strtotime('today');
-
-		if (null == $to)
-			$to = strtotime('tomorrow');
-
-		$steps = range($from, $to, 60 * $increment);
+		$steps = $this->getPossibleTimes($increment, $from, $to);
 
 		$hours = array();
 		foreach ($steps as $step)
@@ -58,6 +54,26 @@ class Multimedia_LocationLoader extends Storm_Model_Loader {
 	}
 
 
+	/**
+	 * @param $increment int minutes
+	 * @param $from int timestamp
+	 * @param $to int timestamp
+	 * @return array
+	 */
+	public function getPossibleTimes($increment, $from = null, $to = null) {
+		if (null == $from)
+			$from = strtotime('today');
+
+		if (null == $to)
+			$to = strtotime('tomorrow');
+
+		if ($from > $to)
+			return array();
+				
+		return range($from, $to, 60 * $increment);
+	}
+
+
 	/**
 	 * @param $json_model stdClass
 	 * @return Class_Multimedia_Location
@@ -129,6 +145,25 @@ class Class_Multimedia_Location extends Storm_Model_Abstract {
 	}
 
 
+	/** @return int */
+	public function getPreviousStartTime() {
+		$current = $this->getCurrentTime();
+		$times = $this->getLoader()->getPossibleTimes($this->getSlotSize());
+
+		if (0 == count($times))
+			return null;
+
+		$previous = reset($times);
+		foreach ($times as $time) {
+			if ($time > $current)
+				return $previous;
+			$previous = $time;
+		}
+
+		return null;
+	}
+
+
 	/**
 	 * @category testing
 	 * @return int
@@ -168,6 +203,12 @@ class Class_Multimedia_Location extends Storm_Model_Abstract {
 	public function getMaxTimeForDate($date) {
 		return strtotime($date . ' ' . $this->getCloseHour() . ':00');
 	}
+
+
+	/** @return int */
+	public function getMaxTimeForToday() {
+		return $this->getMaxTimeForDate(date('Y-m-d', $this->getCurrentTime()));
+	}
 	
 
 	/** @return array */
@@ -247,4 +288,10 @@ class Class_Multimedia_Location extends Storm_Model_Abstract {
 			$label .= $minutes . 'mn';
 		return $label;
 	}
+
+
+	/** @return boolean */
+	public function isAutohold() {
+		return 1 == $this->getAutohold();
+	}
 }
\ No newline at end of file
diff --git a/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php b/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php
index 48d48b2d7ca..cf4ed960b7b 100644
--- a/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php
+++ b/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php
@@ -21,242 +21,252 @@
 
 require_once 'AbstractControllerTestCase.php';
 
-class AbonneControllerMultimediaAuthenticateTest extends AbstractControllerTestCase{
+abstract class AbonneControllerMultimediaAuthenticateTestCase extends AbstractControllerTestCase {
+	protected $_json;
+
 	public function setUp() {
 		parent::setUp();
 		Zend_Auth::getInstance()->clearIdentity();
-		$laurent= Class_Users::getLoader()->newInstanceWithId(8)
-									->setLogin("laurent")
-									->setPassword("afi")
-									->setNom('laffont')
-									->setPrenom('laurent')
-									->setRoleLevel(4)
-									->setIdabon('bca2')
-									->setNaissance('1978-02-17');
-		
-		$baptiste= Class_Users::getLoader()->newInstanceWithId(9)
-									->setLogin("baptiste")
-									->setPassword("afi")
-									->setRoleLevel(2)
-									->setNaissance('2005-02-17')
-									->setDateFin('3000-01-01');
-		
-		$mireille= Class_Users::getLoader()->newInstanceWithId(10)
-									->setLogin("mireille")
-									->setPassword("afi")
-									->setDateFin('1999-01-01');
-		
-		$arnaud= Class_Users::getLoader()->newInstanceWithId(11)
-									->setLogin("arnaud")
-									->setPassword("lelache");
-		
+	}
+
+
+	/**
+	 * @param $url string
+	 * @return stdClass
+	 */
+	protected function getJson($url) {
+		$this->dispatch($url, true);
+		return json_decode($this->_response->getBody());
+	}
+
+
+	/**
+	 * @param $user Class_Users
+	 */
+	protected function _expectUserToLoad($user) {
 		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users')
-						->whenCalled('findFirstBy')
-						->with(array('login'=> 'laurent'))
-						->answers($laurent)
-						
-						->whenCalled('findFirstBy')
-						->with(array('login'=> 'baptiste'))
-						->answers($baptiste)
-						
-						->whenCalled('findFirstBy')
-						->with(array('login'=> 'mireille'))
-						->answers($mireille)
-						
-						->whenCalled('findFirstBy')
-						->with(array('login'=> 'arnaud'))
-						->answers($arnaud)
-						
-						->whenCalled('findFirstBy')
-						->answers(null);
+			->whenCalled('findFirstBy')
+			->with(array('login'=> $user->getLogin()))
+			->answers($user)
+				
+			->whenCalled('findFirstBy')
+			->answers(null);
+	}
 
+
+	/**
+	 * @param $user Class_Users
+	 * @param $group_label string
+	 */
+	protected function _expectGroupForUser($user, $group_label) {
 		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_UserGroupMembership')
 				->whenCalled('findAllBy')
-				->with(array('role' => 'user', 'model' => $laurent))
+				->with(array('role' => 'user', 'model' => $user))
 				->answers(array(Class_UserGroupMembership::getLoader()
 						->newInstance()
 						->setUserGroup(Class_UserGroup::getLoader()
 							->newInstanceWithId(1)
-							->setLibelle('Devs agiles'))))
+							->setLibelle($group_label))));
+	}
+}
 
-				->whenCalled('findAllBy')
-				->with(array('role' => 'user', 'model' => $baptiste))
-				->answers(array(Class_UserGroupMembership::getLoader()
-						->newInstance()
-						->setUserGroup(Class_UserGroup::getLoader()
-							->newInstanceWithId(2)
-							->setLibelle('Devs Oldschool'))))
 
-				->whenCalled('findAllBy')
-				->with(array('role' => 'user', 'model' => $arnaud))
-				->answers(array(Class_UserGroupMembership::getLoader()
-						->newInstance()
-						->setUserGroup(Class_UserGroup::getLoader()
-							->newInstanceWithId(3)
-							->setLibelle('Patrons'))));
+class AbonneControllerMultimediaAuthenticateValidationTest extends AbonneControllerMultimediaAuthenticateTestCase {
+	public function setUp() {
+		parent::setUp();
+		$this->_expectUserToLoad(AbonneControllerMultimediaUsersFixtures::getLaurent());
 	}
 
-
+		
 	/** @test */
 	public function responseShouldNotBeARedirect() {
-		$json = $this->getJson('/abonne/authenticate/login/laurent/password/afi');
+		$json = $this->getJson('/abonne/authenticate/login/any/password/any');
 		$this->assertNotRedirect();
 	}
 
 
 	/** @test */
 	public function withoutPosteShouldReturnErrorMissingParameter() {
-		$json = $this->getJson('/abonne/authenticate/login/laurent/password');
+		$json = $this->getJson('/abonne/authenticate/login/any');
 		$this->assertEquals('MissingParameter', $json->error);
 	}
 
 
 	/** @test */
 	public function withoutSiteShouldReturnErrorMissingParameter() {
-		$json = $this->getJson('/abonne/authenticate/login/laurent/password/poste/1');
+		$json = $this->getJson('/abonne/authenticate/login/any/password/any/poste/1');
 		$this->assertEquals('MissingParameter', $json->error);
 	}
 
 
 	/** @test */
 	public function getAbonneZorkShouldReturnErrorUserNotFound() {
-		$json= $this->getJson('/abonne/authenticate/login/zork/password/toto/poste/1/site/1');
+		$json = $this->getJson('/abonne/authenticate/login/any/password/toto/poste/1/site/1');
 		$this->assertEquals("UserNotFound", $json->error);
-		
 	}
-	
+
 
 	/** @test */
 	public function authenticateAbonneLaurentPasswordXXXShouldReturnWrongPassword() {
-		$json=$this->getJson('/abonne/authenticate/login/laurent/password/xxx/poste/1/site/1');
+		$json = $this->getJson('/abonne/authenticate/login/laurent/password/xxx/poste/1/site/1');
 		$this->assertEquals("PasswordIsWrong", $json->error);	
 	}
+}
+
+
+class AbonneControllerMultimediaAuthenticateMireilleTest extends AbonneControllerMultimediaAuthenticateTestCase {
+	public function setUp() {
+		parent::setUp();
+		$user = AbonneControllerMultimediaUsersFixtures::getMireille();
+		$this->_expectUserToLoad($user);
+
+		$this->_json = $this->getJson('/abonne/authenticate/login/mireille/password/afi/poste/1/site/1');
+	}
+
 
-	
 	/** @test */
-	public function rightAuthenticationShouldNotReturnError() {
+	public function shouldReturnSubscriptionExpired() {
+		$this->assertEquals('SubscriptionExpired', $this->_json->error);	
+	}
+}
+
+
+abstract class AbonneControllerMultimediaAuthenticateValidTestCase extends AbonneControllerMultimediaAuthenticateTestCase {
+	protected $_user;
+	protected $_group;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->_initUser();
+		$this->_expectUserToLoad($this->_user);
+		$this->_expectGroupForUser($this->_user, $this->_group);
+		$this->_launch();
+	}
+
+
+	protected function _launch() {
+		$this->_json = $this->getJson(sprintf('/abonne/authenticate/login/%s/password/%s/poste/1/site/1',
+				                                  $this->_user->getLogin(),
+				                                  $this->_user->getPassword()));
+	}
+
+
+	protected function _initUser() {}
+}
+
+
+class AbonneControllerMultimediaAuthenticateLaurentTest extends AbonneControllerMultimediaAuthenticateValidTestCase {
+	protected function _initUser() {
+		$this->_user = AbonneControllerMultimediaUsersFixtures::getLaurent();
+		$this->_group= 'Devs agiles';
+	}
+
+		
+	protected function _launch() {
 		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_Location')
-				->whenCalled('findByIdOrigine')
-				->answers(Class_Multimedia_Location::getLoader()->newInstanceWithId(1));
+			->whenCalled('findByIdOrigine')
+			->answers(Class_Multimedia_Location::getLoader()->newInstanceWithId(1));
 				
 		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_Device')
-				->whenCalled('findByIdOrigineAndLocation')
-				->answers(Class_Multimedia_Device::getLoader()->newInstanceWithId(1));
+			->whenCalled('findByIdOrigineAndLocation')
+			->answers(Class_Multimedia_Device::getLoader()->newInstanceWithId(1));
 				
 		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_DeviceHold')
-				->whenCalled('getCurrentHoldOfUserOnDevice')
-				->answers(Class_Multimedia_DeviceHold::getLoader()->newInstanceWithId(333)
-					->setEnd(strtotime('2012-09-09 16:40:00')));
-		$json = $this->getJson('/abonne/authenticate/login/laurent/password/afi/poste/1/site/1');
-		$this->assertFalse(property_exists($json, 'error'));
-		return $json;
+			->whenCalled('getHoldOnDeviceAtTime')
+			->answers(Class_Multimedia_DeviceHold::getLoader()->newInstanceWithId(333)
+				->setIdUser($this->_user->getId())
+				->setEnd(strtotime('2012-09-09 16:40:00')));
+				
+		parent::_launch();
 	}
+
 	
-	
-	/**
-	 * @test 
-	 * @depends rightAuthenticationShouldNotReturnError
-	 */
-	public function laurentIdShoudBe8($json) {
-		$this->assertEquals('8', $json->id);
+	/** @test */
+	public function shouldNotReturnError() {
+		$this->assertFalse(property_exists($this->_json, 'error'));
 	}
 	
 	
-	/**
-	 * @test 
-	 * @depends rightAuthenticationShouldNotReturnError
-	 */
-	public function laurentLoginShoudBelaurent($json) {
-		$this->assertEquals('laurent', $json->login);
+	/** @test */
+	public function idShoudBe8() {
+		$this->assertEquals('8', $this->_json->id);
 	}
 	
 	
-	/**
-	 * @test 
-	 * @depends rightAuthenticationShouldNotReturnError
-	 */
-	public function laurentPasswordShoudBeAfi($json) {
-		$this->assertEquals('afi',$json->password);
-	}
-	
-	/**
-	 * @test 
-	 * @depends rightAuthenticationShouldNotReturnError
-	 */
-	public function laurentNomShoudBelaffont($json) {
-		$this->assertEquals('laffont', $json->nom);
+	/** @test */
+	public function loginShoudBelaurent() {
+		$this->assertEquals('laurent', $this->_json->login);
 	}
 	
 	
-	/**
-	 * @test 
-	 * @depends rightAuthenticationShouldNotReturnError
-	 */
-	public function laurentPrenomShoudBelaurent($json) {
-		$this->assertEquals('laurent', $json->prenom);
+	/** @test */
+	public function passwordShoudBeAfi() {
+		$this->assertEquals('afi', $this->_json->password);
+	}
+
+
+	/** @test */
+	public function nomShoudBelaffont() {
+		$this->assertEquals('laffont', $this->_json->nom);
 	}
 	
 	
-	/**
-	 * @test 
-	 * @depends rightAuthenticationShouldNotReturnError
-	 */
-	public function laurentDateNaissanceShoudBe1978_02_17($json) {
-		$this->assertEquals('1978/02/17', $json->date_naissance);
+	/** @test */
+	public function prenomShoudBelaurent() {
+		$this->assertEquals('laurent', $this->_json->prenom);
 	}
 	
 	
-	/**
-	 * @test 
-	 * @depends rightAuthenticationShouldNotReturnError
-	 */
-	public function laurentGroupeShoudBeAdulteAbonneAdminAndAgile($json) {
-		$this->assertEquals(array('adulte','abonne','admin_bib', 'Devs agiles'), $json->groupes);
+	/** @test */
+	public function dateNaissanceShoudBe1978_02_17() {
+		$this->assertEquals('1978/02/17', $this->_json->date_naissance);
 	}
 
 
-	/**
-	 * @test 
-	 * @depends rightAuthenticationShouldNotReturnError
-	 */
-	public function laurentShouldHaveHold($json) {
-		$this->assertEquals(1, $json->auth);
+	/** @test */
+	public function groupShoudBeAdulteAbonneAdminAndAgile() {
+		$this->assertEquals(array('adulte','abonne','admin_bib', 'Devs agiles'),
+			                  $this->_json->groupes);
 	}
 
 
-	/**
-	 * @test 
-	 * @depends rightAuthenticationShouldNotReturnError
-	 */
-	public function laurentHoldShouldLastUntil16h40($json) {
-		$this->assertEquals('2012-09-09T16:40:00+02:00', $json->until);
+	/** @test */
+	public function shouldHaveHold() {
+		$this->assertEquals(1, $this->_json->auth);
 	}
 
-		
+
 	/** @test */
-	public function baptisteGroupesShouldBeMineurAbonneAndOldSchool(){
-		$json = $this->getJson('/abonne/authenticate/login/baptiste/password/afi/poste/1/site/1');
-		$this->assertEquals(array('mineur','abonne_sigb', 'Devs Oldschool'), $json->groupes);	
+	public function holdShouldLastUntil16h40() {
+		$this->assertEquals('2012-09-09T16:40:00+02:00', $this->_json->until);
 	}
-	
-	
-	/** @test */
-		public function mireilleAuthenticateShouldReturnSubscriptionExpired(){
-		$json=$this->getJson('/abonne/authenticate/login/mireille/password/afi/poste/1/site/1');
-		$this->assertEquals('SubscriptionExpired',$json->error);	
+}
+
+
+class AbonneControllerMultimediaAuthenticateArnaudTest extends AbonneControllerMultimediaAuthenticateValidTestCase {
+	protected function _initUser() {
+		$this->_user = AbonneControllerMultimediaUsersFixtures::getArnaud();
+		$this->_group= 'Patrons';
 	}
-	
 
 	/** @test */
-	public function arnaudGroupesShouldBeInviteAndPatrons() {
-		$json=$this->getJson('/abonne/authenticate/login/arnaud/password/lelache/poste/1/site/1');
-		$this->assertEquals(array('invite', 'Patrons'), $json->groupes);	
+	public function groupsShouldBeInviteAndPatrons() {
+		$this->assertEquals(array('invite', 'Patrons'), $this->_json->groupes);	
 	}
+}
 
 
-	protected function getJson($url) {
-		$this->dispatch($url);
-		return json_decode($this->_response->getBody());
+class AbonneControllerMultimediaAuthenticateBaptisteTest extends AbonneControllerMultimediaAuthenticateValidTestCase {
+	protected function _initUser() {
+		$this->_user = AbonneControllerMultimediaUsersFixtures::getBaptiste();
+		$this->_group= 'Devs Oldschool';
+	}
+
+		
+	/** @test */
+	public function groupsShouldBeMineurAbonneAndOldSchool() {
+		$this->assertEquals(array('mineur','abonne_sigb', 'Devs Oldschool'), $this->_json->groupes);	
 	}
 }
 
@@ -790,4 +800,43 @@ class AbonneControllerMultimediaHoldFicheAbonneTest extends AbstractControllerTe
 	public function viewHoldLinkShouldBePresent() {
 		$this->assertXPath('//a[contains(@href, "multimedia-hold-view/id/12")]');
 	}
+}
+
+
+class AbonneControllerMultimediaUsersFixtures {
+	public static function getLaurent() {
+		return Class_Users::getLoader()->newInstanceWithId(8)
+				->setLogin("laurent")
+				->setPassword("afi")
+				->setNom('laffont')
+				->setPrenom('laurent')
+				->setRoleLevel(4)
+				->setIdabon('bca2')
+				->setNaissance('1978-02-17');
+	}
+
+
+	public static function getBaptiste() {
+		return Class_Users::getLoader()->newInstanceWithId(9)
+				->setLogin("baptiste")
+				->setPassword("afi")
+				->setRoleLevel(2)
+				->setNaissance('2005-02-17')
+				->setDateFin('3000-01-01');
+	}
+
+
+	public static function getMireille() {
+		return Class_Users::getLoader()->newInstanceWithId(10)
+				->setLogin("mireille")
+				->setPassword("afi")
+				->setDateFin('1999-01-01');
+	}
+
+
+	public static function getArnaud() {
+		return Class_Users::getLoader()->newInstanceWithId(11)
+				->setLogin("arnaud")
+				->setPassword("lelache");
+	}
 }
\ No newline at end of file
diff --git a/tests/library/Class/Multimedia/DeviceTest.php b/tests/library/Class/Multimedia/DeviceTest.php
new file mode 100644
index 00000000000..cb01a037bf5
--- /dev/null
+++ b/tests/library/Class/Multimedia/DeviceTest.php
@@ -0,0 +1,217 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * AFI-OPAC 2.0 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.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * AFI-OPAC 2.0 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 AFI-OPAC 2.0; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ */
+require_once 'ModelTestCase.php';
+
+abstract class Multimedia_DeviceCurrentHoldTestCase extends ModelTestCase {
+	/** @var Class_Multimedia_Device */
+	protected $_device;
+	/** @var Class_Multimedia_DeviceGroup */
+	protected $_group;
+	/** @var Class_Multimedia_Location */
+	protected $_location;
+	/** @var Class_Multimedia_DeviceHold */
+	protected $_hold;
+	/** @var int */
+	protected $_time;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->_location = Class_Multimedia_Location::getLoader()->newInstanceWithId(2);
+		$this->_group = Class_Multimedia_DeviceGroup::getLoader()->newInstanceWithId(2)
+				->setLocation($this->_location);
+		$this->_device = Class_Multimedia_Device::getLoader()->newInstanceWithId(2)
+				->setGroup($this->_group);
+
+		$this->_time = strtotime('today');
+		$time_source = Storm_Test_ObjectWrapper::mock()
+				->whenCalled('time')
+				->answers($this->_time);
+				
+		Class_Multimedia_Device::setTimeSource($time_source);
+		Class_Multimedia_Location::setTimeSource($time_source);
+	}
+}
+
+
+class Multimedia_DeviceCurrentHoldForUserHavingHoldTest extends Multimedia_DeviceCurrentHoldTestCase {
+	public function setUp() {
+		parent::setUp();
+		$this->_location->setAutohold(0);
+		
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_DeviceHold')
+				->whenCalled('getHoldOnDeviceAtTime')
+				->with($this->_device, $this->_time)
+				->answers(Class_Multimedia_DeviceHold::getLoader()->newInstanceWithId(123)
+					->setIdUser(7));
+
+		$this->_hold = $this->_device->getCurrentHoldForUser(Class_Users::getLoader()->newInstanceWithId(7));
+	}
+
+
+	/** @test */
+	public function shouldHaveHold() {
+		$this->assertNotNull($this->_hold);
+	}
+}
+
+
+class Multimedia_DeviceCurrentHoldForUserWithoutHoldAndNoAutoholdTest extends Multimedia_DeviceCurrentHoldTestCase {
+	public function setUp() {
+		parent::setUp();
+		$this->_location->setAutohold(0);
+		
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_DeviceHold')
+				->whenCalled('getHoldOnDeviceAtTime')
+				->with($this->_device, $this->_time)
+				->answers(null);
+
+		$this->_hold = $this->_device->getCurrentHoldForUser(Class_Users::getLoader()->newInstanceWithId(7));
+	}
+
+
+	/** @test */
+	public function shouldNotHaveHold() {
+		$this->assertNull($this->_hold);
+	}
+}
+
+
+class Multimedia_DeviceCurrentHoldForUserWithoutHoldAndAnotherValidHoldTest extends Multimedia_DeviceCurrentHoldTestCase {
+	public function setUp() {
+		parent::setUp();
+		$this->_location
+				->setAuthDelay(10)
+				->setAutohold(1)
+				->setSlotSize(15);
+		
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_DeviceHold')
+				->whenCalled('getHoldOnDeviceAtTime')
+				->with($this->_device, $this->_time)
+				->answers(Class_Multimedia_DeviceHold::getLoader()->newInstanceWithId(123)
+					->setIdUser(5)
+					->setStart($this->_time - 60));
+
+		$this->_hold = $this->_device->getCurrentHoldForUser(Class_Users::getLoader()->newInstanceWithId(7));
+	}
+
+
+	/** @test */
+	public function shouldNotHaveHold() {
+		$this->assertNull($this->_hold);
+	}
+}
+
+
+class Multimedia_DeviceCurrentHoldForUserWithoutHoldAndNoStartTimeTest extends Multimedia_DeviceCurrentHoldTestCase {
+	public function setUp() {
+		parent::setUp();
+		$this->_location
+				->setAuthDelay(10)
+				->setAutohold(1)
+				->setSlotSize(15);
+		
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_DeviceHold')
+				->whenCalled('getHoldOnDeviceAtTime')
+				->with($this->_device, $this->_time)
+				->answers(null);
+
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_Location')
+				->whenCalled('getPossibleTimes')
+				->answers(array());
+
+		$this->_hold = $this->_device->getCurrentHoldForUser(Class_Users::getLoader()->newInstanceWithId(7));
+	}
+
+
+	/** @test */
+	public function shouldNotHaveHold() {
+		$this->assertNull($this->_hold);
+	}
+}
+
+
+class Multimedia_DeviceCurrentHoldForUserWithoutHoldAndMaxSlotsAfterCloseHoursTest extends Multimedia_DeviceCurrentHoldTestCase {
+	public function setUp() {
+		parent::setUp();
+		$this->_location
+				->setAuthDelay(10)
+				->setAutohold(1)
+				->setSlotSize(15)
+				->setAutoholdSlotsMax(600)
+				->setCloseHour('10');
+		
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_DeviceHold')
+				->whenCalled('getHoldOnDeviceAtTime')
+				->with($this->_device, $this->_time)
+				->answers(null);
+
+		$this->_hold = $this->_device->getCurrentHoldForUser(Class_Users::getLoader()->newInstanceWithId(7));
+	}
+
+
+	/** @test */
+	public function shouldHaveHold() {
+		$this->assertNotNull($this->_hold);
+	}
+
+
+	/** @test */
+	public function holdEndShouldBeCloseHour() {
+		$this->assertEquals($this->_location->getMaxTimeForToday(), $this->_hold->getEnd());
+	}
+}
+
+
+class Multimedia_DeviceCurrentHoldForUserWithoutHoldAndMaxSlotsAfterNextHoldStartTest extends Multimedia_DeviceCurrentHoldTestCase {
+	/** @var int */
+	protected $_nextStartTime;
+	
+	public function setUp() {
+		parent::setUp();
+		$this->_location
+				->setAuthDelay(10)
+				->setAutohold(1)
+				->setSlotSize(15)
+				->setAutoholdSlotsMax(600)
+				->setCloseHour('23');
+
+		$this->_nextStartTime = $this->_time + (60 * 60);
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_DeviceHold')
+				->whenCalled('getFirstHoldOnDeviceBetweenTimes')
+				->answers(Class_Multimedia_DeviceHold::getLoader()->newInstanceWithId(333)
+					->setStart($this->_nextStartTime));
+
+		$this->_hold = $this->_device->getCurrentHoldForUser(Class_Users::getLoader()->newInstanceWithId(7));
+	}
+
+
+	/** @test */
+	public function shouldHaveHold() {
+		$this->assertNotNull($this->_hold);
+	}
+
+
+	/** @test */
+	public function holdEndShouldBeNextStartTime() {
+		$this->assertEquals($this->_nextStartTime, $this->_hold->getEnd());
+	}
+}
\ No newline at end of file
-- 
GitLab