Page MenuHomeDevCentral

No OneTemporary

diff --git a/app/Phabricator/ProjectsMap.php b/app/Phabricator/ProjectsMap.php
index 62ba716..876053f 100644
--- a/app/Phabricator/ProjectsMap.php
+++ b/app/Phabricator/ProjectsMap.php
@@ -1,273 +1,275 @@
<?php
namespace Nasqueron\Notifications\Phabricator;
use Nasqueron\Notifications\Phabricator\PhabricatorAPIClient as ApiClient;
use App;
use Cache;
class ProjectsMap implements \IteratorAggregate, \ArrayAccess {
///
/// Private properties and constants
///
/**
* The maximum number of projects to fetch
*/
const LIMIT = 1000;
/**
* The projects as an array with phid as keys, project names as $value
*
* @var string[]
*/
private $map = [];
/**
* The Phabricator instance for this projects map
*
* @var string
*/
private $instance;
/**
*
* @var Nasqueron\Notifications\Contracts\APIClient
*/
private $apiClient;
/**
* The source of the map
*
* @var string
*/
private $source = 'unloaded';
///
/// Constructor
///
/**
* Initializes a new instance of ProjectsMap
*
* @param string $instance The Phabricator root URL without trailing slash
*/
public function __construct ($instance) {
$this->instance = $instance;
}
///
/// IteratorAggregate interface implementation
///
/**
* Gets iterator
*
* @return Traversable
*/
public function getIterator () {
return new \ArrayIterator($this->map);
}
///
/// ArrayAccess interface implementation
///
/**
* Determines whether an offset exists
*
* @param mixed $offset The offset
*/
public function offsetExists ($offset) {
return array_key_exists($offset, $this->map);
}
/**
* Gets an offset
*
* @param mixed $offset The offset
* @return mixed The value
*/
public function offsetGet ($offset) {
return $this->map[$offset];
}
/**
* Assigns a value to the specified offset
*
* @param mixed $offset The offset
* @param mixed $value The value to assign
*/
public function offsetSet ($offset, $value) {
$this->map[$offset] = $value;
}
/**
* Unset an offset
*
* @param mixed $offset The offset
*/
public function offsetUnset ($offset) {
unset($this->map[$offset]);
}
///
/// Static constructors
///
/**
* Gets a new ProjectsMap instance from cache or API when not cached
*
* @param string $phabricatorURL The Phabricator URL (e.g. http://secure.phabricator.com)
* @return ProjectsMap
*/
public static function load ($phabricatorURL) {
$instance = new self($phabricatorURL);
if ($instance->isCached()) {
$instance->loadFromCache();
} else {
$instance->fetchFromAPI();
}
return $instance;
}
/**
* Gets a new ProjectsMap instance and queries Phabricator API to fill it
*
* @param string $phabricatorURL The Phabricator URL (e.g. http://secure.phabricator.com)
+ * @param Nasqueron\Notifications\Contracts\APIClient $apiClient The Phabricator API client
* @return ProjectsMap
*/
- public static function fetch ($phabricatorURL) {
+ public static function fetch ($phabricatorURL, APIClient $apiClient = null) {
$instance = new self($phabricatorURL);
+ $instance->setAPIClient($apiClient);
$instance->fetchFromAPI();
return $instance;
}
///
/// API
///
/**
* @return Nasqueron\Notifications\Contracts\APIClient
*/
public function getAPIClient () {
if ($this->apiClient === null) {
$factory = App::make('phabricator-api');
$this->apiClient = $factory->get($this->instance);
}
return $this->apiClient;
}
/**
* @param Nasqueron\Notifications\Contracts\APIClient $apiClient
*/
- public function setAPIClient (APIClient $apiClient) {
+ public function setAPIClient (APIClient $apiClient = null) {
$this->apiClient = $apiClient;
}
/**
* Fetches the projects' map from the Phabricator API
*/
private function fetchFromAPI () {
$reply = $this->getAPIClient()->call(
'project.query',
[ 'limit' => self::LIMIT ]
);
if (!$reply) {
throw new \Exception("Empty reply calling project.query at $this->instance API.");
}
if (!property_exists($reply, 'data')) {
throw new \Exception("Invalid reply calling project.query at $this->instance API.");
}
foreach ($reply->data as $phid => $projectInfo) {
$this->offsetSet($phid, $projectInfo->name);
}
$this->source = 'api';
}
///
/// Cache
///
/**
* Gets cache key
*
* @return string The cache key for the current projects map
*/
private function getCacheKey () {
return class_basename(get_class($this)) . '-' . md5($this->instance);
}
/**
* Determines if the instance is cached
*
* @return bool true if cached; otherwise, false.
*/
public function isCached () {
return Cache::has($this->getCacheKey());
}
/**
* Saves data to cache
*/
public function saveToCache () {
Cache::forever($this->getCacheKey(), $this->map);
}
/**
* Loads data from cache
*
* Populates 'map' and 'source' properties
*/
public function loadFromCache () {
$cachedMap = Cache::get($this->getCacheKey());
if ($cachedMap !== null) {
$this->map = $cachedMap;
$this->source = 'cache';
}
}
///
/// Output
///
/**
* Gets project name, refreshing the cache if needed
*
* @param string $projectPHID the PHID of the project to query the name
* @return string
*/
public function getProjectName ($projectPHID) {
if ($this->offsetExists($projectPHID)) {
return $this->offsetGet($projectPHID);
}
if ($this->source !== 'api') {
$this->fetchFromAPI();
return $this->getProjectName($projectPHID);
}
return "";
}
/**
* Returns the projects map as an array, each row ['PHID', 'project name']
*
* @return array
*/
public function toArray () {
$array = [];
foreach ($this->map as $phid => $projectName) {
$array[] = [$phid, $projectName];
}
return $array;
}
}
diff --git a/tests/Phabricator/ProjectsMapTest.php b/tests/Phabricator/ProjectsMapTest.php
index 7bbe281..dd2e5b2 100644
--- a/tests/Phabricator/ProjectsMapTest.php
+++ b/tests/Phabricator/ProjectsMapTest.php
@@ -1,164 +1,192 @@
<?php
namespace Nasqueron\Notifications\Tests\Phabricator;
use Nasqueron\Notifications\Phabricator\ProjectsMap;
use Nasqueron\Notifications\Tests\TestCase;
+use Mockery;
+
class ProjectsMapTest extends TestCase {
/**
* @var Nasqueron\Notifications\Phabricator\ProjectsMap
*/
private $map;
public function setUp () {
parent::setUp();
//
// We mock the API, so an imaginary instance of Phabricator
// will return 3 results: Accounts, Agora & architecture.
//
// Agora has the key "PHID-PROJ-cztcgpvqr6smnnekotq7".
//
$this->mockPhabricatorAPIForProjectsMap();
$this->map = ProjectsMap::fetch("http://phabricator.acme.tld");
}
public function testIteratorIsTraversable () {
$this->assertInstanceOf(
"Traversable",
$this->map->getIterator()
);
}
///
/// Tests for ArrayAccess
///
public function testOffsetExistsWhenItDoes () {
$this->assertTrue(
$this->map->offsetExists("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
public function testOffsetExistsWhenItDoesNot () {
$this->assertFalse(
$this->map->offsetExists("non-existing-key")
);
}
public function testOffsetGetWhenItDoesExist () {
$this->assertSame(
"Agora",
$this->map->offsetGet("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
/**
* @expectedException ErrorException
*/
public function testOffsetGetWhenItDoesNotExist () {
$this->map->offsetGet("non-existing-key");
}
/**
* @covers Nasqueron\Notifications\Phabricator\ProjectsMap::offsetSet
*/
public function testOffsetSet () {
$this->map->offsetSet("newkey", "quux");
$this->assertSame("quux", $this->map->offsetGet("newkey"));
}
/**
* @covers Nasqueron\Notifications\Phabricator\ProjectsMap::offsetUnset
*/
public function testOffsetUnset () {
unset($this->map["PHID-PROJ-cztcgpvqr6smnnekotq7"]);
$this->assertFalse(
$this->map->offsetExists("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
///
/// Tests for cache
///
public function testCache () {
$this->assertFalse($this->map->isCached());
$this->map->saveToCache();
$this->assertTrue($this->map->isCached());
}
public function testLoadFromCache () {
$this->map->saveToCache();
$map = new ProjectsMap("http://phabricator.acme.tld");
$map->loadFromCache();
$this->assertTrue(
$map->offsetExists("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
public function testLoadWhenInCache () {
$this->map->saveToCache();
$map = ProjectsMap::load("http://phabricator.acme.tld");
$this->assertTrue(
$map->offsetExists("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
///
/// Tests for helper methods
///
public function testGetProjectName () {
$this->assertSame(
"Agora",
$this->map->getProjectName("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
public function testGetProjectNameForNewInstance () {
$map = new ProjectsMap("http://phabricator.acme.tld");
$this->assertSame(
"Agora",
$map->getProjectName("PHID-PROJ-cztcgpvqr6smnnekotq7")
);
}
public function testGetProjectNameWhenItDoesNotExist () {
$this->assertSame(
"",
$this->map->getProjectName("non-existing-key")
);
}
public function testToArrayProducesArray () {
$array = $this->map->toArray();
$this->assertTrue(
is_array($array),
"Test if toArray return an array"
);
}
public function testThatArrayCount () {
$array = $this->map->toArray();
$this->assertSame(3, count($array));
}
public function testThatArrayContainsExpectedData () {
$this->assertSame(
[
["PHID-PROJ-6dg6ogx5pjmk24ur4tp4", "Accounts"],
["PHID-PROJ-cztcgpvqr6smnnekotq7", "Agora"],
["PHID-PROJ-3iew3cqf3htpazfyzb5a", "architecture"]
],
$this->map->toArray()
);
}
+ ///
+ /// Tests API
+ ///
+
+ private function mockPhabricatorAPIWithReply ($reply) {
+ return Mockery::mock('Nasqueron\Notifications\Contract\APIClient')
+ ->shouldReceive('call')
+ ->andReturn($reply);
+ }
+
+ /**
+ * @expectedException Exception
+ */
+ public function testFetchFromAPIWithoutReply () {
+ $mock = $this->mockPhabricatorAPIWithReply(false);
+ ProjectsMap::fetch("http://phabricator.acme.tld", $mock);
+ }
+
+ /**
+ * @expectedException Exception
+ */
+ public function testFetchFromAPIInvalidReply () {
+ $mock = $this->mockPhabricatorAPIWithReply(new \stdClass);
+ ProjectsMap::fetch("http://phabricator.acme.tld", $mock);
+ }
+
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, May 5, 11:49 (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3684461
Default Alt Text
(12 KB)

Event Timeline