diff --git a/includes/project_src_github.archive.inc b/includes/project_src_github.archive.inc
index 72a5db9..4f7df3f 100644
--- a/includes/project_src_github.archive.inc
+++ b/includes/project_src_github.archive.inc
@@ -102,7 +102,7 @@ function project_src_github_get_archive($namespace, $project_name, $version) {
  * @todo We should really be storing/managing tarballs after repackaging.
  */
 function _project_src_github_repackage_tarball($namespace, $project, $version, $managed_destination) {
-  $github = 'https://github.com';
+  $github = variable_get('project_src_github_domain', 'https://github.com');
   $extension = $namespace . '/' . $project;
   $get = $github . '/' . $extension . '/archive/' . $version . '.tar.gz';
   $tarball = _project_src_github_file_as_string($get);
diff --git a/includes/project_src_github.private.inc b/includes/project_src_github.private.inc
index 7c26a50..8ac4d78 100644
--- a/includes/project_src_github.private.inc
+++ b/includes/project_src_github.private.inc
@@ -21,8 +21,9 @@
  *   A release array suitable for use in the primary release theme function.
  */
 function _project_src_github_format_release_info($commitref, $info) {
-  // Root of the project on GitLab.
-  $root = 'https://github.com/' . $info['api']['path_with_namespace'];
+  // Root of the project on GitHub.
+  $github = variable_get('project_src_github_domain', 'https://github.com');
+  $root = $github . '/' . $info['api']['path_with_namespace'];
 
   // Filter $commitref['name'] here, since it's used very often below.
   $commitref['name'] = check_plain($commitref['name']);
diff --git a/project_src_github.info b/project_src_github.info
index 82deef7..f42b73a 100644
--- a/project_src_github.info
+++ b/project_src_github.info
@@ -5,3 +5,5 @@ package = Development
 configure = admin/config/development/github-src
 dependencies[] = project_src
 dependencies[] = github_api
+
+files[] = project_src_github.test
diff --git a/project_src_github.install b/project_src_github.install
index 13680fb..dae139f 100644
--- a/project_src_github.install
+++ b/project_src_github.install
@@ -12,6 +12,7 @@
 function project_src_github_uninstall() {
   $variables = array(
     'project_src_github_org',
+    'project_src_github_domain',
   );
 
   foreach ($variables as $variable) {
diff --git a/project_src_github.test b/project_src_github.test
new file mode 100644
index 0000000..5354bb6
--- /dev/null
+++ b/project_src_github.test
@@ -0,0 +1,364 @@
+<?php
+
+/**
+ * @file
+ * SimpleTest tests for the Project Source: GitHub module.
+ */
+
+
+/**
+ * Helper class with some added functions and properties for testing.
+ */
+class ProjectSrcGithubBaseCase extends DrupalWebTestCase {
+
+  /**
+   * The GitHub organization used for testing.
+   */
+  protected $org = 'drewpaul_inc';
+
+  /**
+   * @var GithubApiMock
+   */
+  protected $github;
+
+  function setUp(array $modules = array()) {
+    $modules[] = 'project_src';
+    $modules[] = 'project_src_github';
+    parent::setUp($modules);
+
+    // Ensure the test environment has the proper public files directory.
+    variable_del('file_public_path');
+
+    // Order matters!
+    $fake_api = 'project_src_github_test_github_api';
+    $this->fakeModuleUninstall('composer_manager');
+    $this->fakeModuleUninstall('github_api');
+    drupal_flush_all_caches();
+    $this->fakeModuleInstall($fake_api);
+
+    // Set relevant Project Source: GitHub variables.
+    variable_set('project_src_github_org', $this->org);
+    $module = drupal_get_path('module', $fake_api);
+    $github = $GLOBALS['base_url'] . '/' . $module . '/archives';
+    variable_set('project_src_github_domain', $github);
+
+    // Keep track of our fake GitHub API object for test comparisons.
+    module_load_include('module', $fake_api, $fake_api);
+    $this->github = new GithubApiMock();
+  }
+
+  /**
+   * Performs an unofficial module install (in that no hooks are run and no
+   * dependencies are loaded or installed).
+   *
+   * @param string $module
+   *   The module to "install."
+   */
+  protected function fakeModuleInstall($module) {
+    db_update('system')
+      ->fields(array('status' => 1))
+      ->condition('type', 'module')
+      ->condition('name', $module)
+      ->execute();
+
+    system_list_reset();
+    module_list(TRUE);
+    module_implements('', FALSE, TRUE);
+    _system_update_bootstrap_status();
+    registry_update();
+    drupal_get_schema(NULL, TRUE);
+    drupal_theme_rebuild();
+    entity_info_cache_clear();
+  }
+
+  /**
+   * Performs an unofficial module uninstall (in that no hooks are run and no
+   * dependencies are loaded or installed).
+   *
+   * @param string $module
+   *   The module to "uninstall."
+   */
+  protected function fakeModuleUninstall($module) {
+    db_update('system')
+      ->fields(array('status' => 0))
+      ->condition('type', 'module')
+      ->condition('name', $module)
+      ->execute();
+
+    system_list_reset();
+    module_list(TRUE);
+    module_implements('', FALSE, TRUE);
+    entity_info_cache_clear();
+    registry_update();
+    _system_update_bootstrap_status();
+    drupal_theme_rebuild();
+  }
+}
+
+
+/**
+ * Tests Project Source: GitHub XML generation.
+ */
+class ProjectSrcGithubXMLTests extends ProjectSrcGithubBaseCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Project Source GitHub - XML',
+      'description' => 'Tests the Project Source GitHub XML generation.',
+      'group' => 'Project Source GitHub',
+    );
+  }
+
+  /**
+   * Compares the provided project XML values with those provided by our mock
+   * GitHub API object.
+   *
+   * @param SimpleXMLElement|array $xml
+   *   The SimpleXMLElement as returned by the xpath query (or an array that
+   *   contains at a SimpleXMLElement as its first item).
+   * @param string $api
+   *   The API version of the requested project XML.
+   */
+  protected function validateProjectXml($xml, $api) {
+    $repos = $this->github->repositories($this->org);
+    $repo = $repos[$this->org . '_module'];
+
+    // We may be handed an array.
+    if (is_array($xml)) {
+      $xml = $xml[0];
+    }
+
+    $major_version = substr($repo['default_branch'], 4, 1);
+
+    $this->assertEqual($xml->title, $repo['name'], format_string('Returned the expected project title %arg.', array('%arg' => $xml->title)));
+    $this->assertEqual($xml->short_name, $repo['name'], format_string('Returned the expected project short name %arg.', array('%arg' => $xml->short_name)));
+    $this->assertEqual($xml->creator, $repo['owner']['login'], format_string('Returned the expected project creator %arg.', array('%arg' => $xml->creator)));
+    $this->assertEqual($xml->api_version, $api, format_string('Returned the expected API version %arg.', array('%arg' => $xml->api_version)));
+    $this->assertEqual($xml->default_major, $major_version, format_string('Returned the expected default major version %arg.', array('%arg' => $xml->default_major)));
+    $this->assertEqual($xml->project_status, 'published', 'Returned the expected status.');
+    // @todo Recommended/supported major?
+    // @todo Link?
+  }
+
+  /**
+   * Compares the provided release XML values with those provided by our mock
+   * GitHub API object.
+   *
+   * @param SimpleXMLElement $xml
+   *   The SimpleXMLElement as returned by the xpath query.
+   */
+  protected function validateReleaseXml($xml) {
+    $repos = $this->github->repositories($this->org);
+    $repo = $repos[$this->org . '_module'];
+    $branches = $this->github->branches($this->org, $this->org . '_module');
+    $commit = $this->github->show($this->org, $this->org . '_module', '');
+
+    // Tests if we're dealing with a dev release.
+    if (substr($xml->version, -4) == '-dev') {
+      $expected_extra = 'dev';
+      $real_tag = substr($xml->version, 0, -4);
+    }
+    // Test if we're dealing with an unstable release.
+    elseif (preg_match_all('/.*?((unstable|alpha|beta|rc)\\d+)/is', $xml->version, $matches)) {
+      $expected_extra = $matches[1][0];
+      $real_tag = $xml->version;
+    }
+    else {
+      $expected_extra = NULL;
+      $real_tag = $xml->version;
+    }
+
+    $name = $this->org . '_module ' . $xml->version;
+    $major_version = substr($real_tag, 4, 1);
+    $release_link = 'https://github.com/' . $repo['full_name'];
+    $file_name = 'project-src-github/' . $repo['full_name'] . '/' . $real_tag . '/' . 'download';
+
+    $this->assertEqual($xml->name, $name, format_string('Returned the expected release name %arg.', array('%arg' => $xml->name)));
+    $this->assertEqual($xml->version, $xml->tag, format_string('Returned the expected release tag %arg.', array('%arg' => $xml->tag)));
+    $this->assertEqual($xml->version_major, $major_version, format_string('Returned the expected major version %arg.', array('%arg' => $xml->version_major)));
+    $this->assertTrue(strpos($xml->download_link, $file_name) !== FALSE, format_string('Returned the expected release download link %arg.', array('%arg' => $xml->download_link)));
+    $this->assertEqual($xml->date, strtotime($commit['commit']['author']['date']), format_string('Returned the expected release date %arg.', array('%arg' => $xml->date)));
+    $this->assertEqual($xml->release_link, $release_link, format_string('Returned the expected release link %arg.', array('%arg' => $xml->release_link)));
+    $this->assertEqual($xml->status, 'published', 'Returned valid status.');
+
+    // If this release corresponds to a dev/alpha/beta/etc. release, test it.
+    if (!empty($expected_extra)) {
+      $this->assertEqual($xml->version_extra, $expected_extra, format_string('Returned expected extra version metadata %arg.', array('%arg' => $xml->version_extra)));
+
+      // Ensure that dev tags correspond to actual branches from the API.
+      if ($expected_extra == 'dev') {
+        $this->assertTrue(isset($branches[$real_tag]), format_string('Release %arg matches a valid branch.', array('%arg' => $real_tag)));
+      }
+    }
+  }
+
+  /**
+   * Compares the provided file XML values with those provided by our mock
+   * GitHub API object.
+   *
+   * @param SimpleXMLElement $xml
+   *   The SimpleXMLElement as returned by the xpath query.
+   */
+  protected function validateFileXml($xml) {
+    $repos = $this->github->repositories($this->org);
+    $repo = $repos[$this->org . '_module'];
+    $commit = $this->github->show($this->org, $this->org . '_module', '');
+
+    $version = '';
+    if (preg_match_all('/.*?(\\d+\\.x-\\d+\\..(-.*\\d+)?).*?/is', $xml->url, $matches)) {
+      $version = $matches[1][0];
+      $this->verbose('<pre>' . print_r($matches, TRUE) . '</pre>');
+    }
+    elseif (preg_match_all('', $xml->url, $matches)) {
+
+    }
+    $file_name = 'project-src-github/' . $repo['full_name'] . '/' . $version . '/' . 'download';
+
+    // Note File size and md5 are validated separately.
+    $this->assertTrue(strpos($xml->url, $file_name) !== FALSE, format_string('Returned the expected release download link %arg.', array('%arg' => $xml->url)));
+    $this->assertEqual($xml->archive_type, 'tar.gz', 'Returned the expected archive type.');
+    $this->assertEqual($xml->filedate, strtotime($commit['commit']['author']['date']), format_string('Returned the expected file date.', array('%arg' => $xml->filedate)));
+  }
+
+  /**
+   * Main XML generation test method.
+   */
+  public function testProjectSourceProjectXml() {
+    // Check for valid XML at 6.x.
+    $this->drupalGet('drupal/release-history/' . $this->org . '_module/6.x');
+    $headers = $this->drupalGetHeaders();
+    $this->assertEqual($headers['content-type'], 'text/xml; charset=utf-8', 'Found valid XML for 6.x.');
+    $this->validateProjectXml($this->xpath('//project'), '6.x');
+    $this->assertNoFieldByXpath('//version', 'master', 'No release found for "master."');
+
+    // Check 6.x releases.
+    $releases = $this->xpath('//release');
+    $this->assertEqual(count($releases), 2, 'Found 2 release for 6.x.');
+    foreach ($releases as $release) {
+      $this->validateReleaseXml($release);
+    }
+
+    // Check 6.x files.
+    $files = $this->xpath('//file');
+    $this->assertEqual(count($files), 2, 'Found 2 file for 6.x');
+    foreach ($files as $file) {
+      $this->validateFileXml($file);
+    }
+
+    // Check for valid XML at 7.x.
+    $this->drupalGet('drupal/release-history/' . $this->org . '_module/7.x');
+    $headers = $this->drupalGetHeaders();
+    $this->assertEqual($headers['content-type'], 'text/xml; charset=utf-8', 'Found valid XML for 7.x.');
+    $this->validateProjectXml($this->xpath('//project'), '7.x');
+    $this->assertNoFieldByXpath('//version', 'master', 'No release found for "master."');
+
+    // Check 7.x releases.
+    $releases = $this->xpath('//release');
+    $this->assertEqual(count($releases), 5, 'Found 5 releases for 7.x');
+    foreach ($releases as $release) {
+      $this->validateReleaseXml($release);
+    }
+
+    // Check 7.x files.
+    $files = $this->xpath('//file');
+    $this->assertEqual(count($files), 5, 'Found 5 files for 7.x');
+    foreach ($files as $file) {
+      $this->validateFileXml($file);
+    }
+
+    // Check that there are no results for 8.x.
+    $this->drupalGet('drupal/release-history/' . $this->org . '_module/8.x');
+    $headers = $this->drupalGetHeaders();
+    $this->assertEqual($headers['content-type'], 'text/xml; charset=utf-8', 'Found valid XML for 8.x.');
+    $this->assertNoFieldByXpath('//release', NULL, 'No releases found for 8.x.');
+    $text = 'No release history was found for the requested project (' . $this->org . '_module).';
+    $this->assertFieldByXPath('//error', $text, 'Found "no release history" messaging for 8.x');
+  }
+}
+
+
+/**
+ * Tests Project Source: GitHub file generation.
+ */
+class ProjectSrcGithubFileTests extends ProjectSrcGithubBaseCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Project Source GitHub - File',
+      'description' => 'Tests the Project Source GitHub file generation.',
+      'group' => 'Project Source GitHub',
+    );
+  }
+
+  /**
+   * Validates file metadata and validity.
+   *
+   * @param array $commitrefs
+   *   An array of commit refs, keyed by ref (e.g. tag name or branch name).
+   * @param bool $file_exists
+   *   Checks whether or not the file exists and is managed after requesting it.
+   */
+  protected function validateFileMetadata(array $commitrefs, $file_exists) {
+    $root = 'public://project-src-github/drewpaul_inc/drewpaul_inc_module';
+    $module = $this->org . '_module';
+
+    foreach ($commitrefs as $ref => $info) {
+      $file = $root . '/' . $ref . '/download.tar.gz';
+
+      // Check that the file does not exist locally.
+      $this->assertFalse(file_exists($file), format_string('No local file for !ref yet.', array(
+        '!ref' => $ref,
+      )));
+
+      // Then get the file and check the response, content type, and filename.
+      $this->drupalGet(file_create_url($file));
+      $headers = $this->drupalGetHeaders();
+      $this->assertEqual($headers[':status'], 'HTTP/1.1 200 OK', 'File returned a 200.');
+      $this->assertEqual($headers['content-type'], 'application/octet-stream', format_string('Found a valid tar archive for !ref.', array(
+        '!ref' => $ref,
+      )));
+      $filename = $module . '-' . $ref . '.tar.gz';
+      $this->assertEqual($headers['content-disposition'], 'attachment; filename="' . $filename . '"', format_string('File name header set to !filename', array(
+        '!filename' => $filename,
+      )));
+
+      // If desired, check that the file now exists.
+      if ($file_exists) {
+        // Then check that the file does exist locally.
+        $this->assertTrue(file_exists($file), format_string('File successfully saved for !ref.', array(
+          '!ref' => $ref,
+        )));
+
+        // Check that the file is being managed.
+        $managed_file = _project_src_github_load_file_by_uri($file);
+        $this->assertEqual($managed_file->filename, 'download.tar.gz', 'Found a valid entry in the managed file system.');
+
+        // Clean up the file.
+        file_delete($managed_file);
+      }
+      else {
+        // Ensure a file was not created.
+        $this->assertTrue(!file_exists($file), format_string('File was not saved for !ref.', array(
+          '!ref' => $ref,
+        )));
+
+        // Ensure no managed file entry was created.
+        $this->assertFalse(_project_src_github_load_file_by_uri($file), 'Found no entry in the managed file system.');
+      }
+    }
+  }
+
+  /**
+   * Tests tagged and branch-based release tar archive metadata.
+   */
+  public function testProjectSourceProjectFiles() {
+    // Ensure that all tagged releases have valid archives.
+    $tags = $this->github->tags($this->org, $this->org . '_module');
+    $this->validateFileMetadata($tags, TRUE);
+
+    // Ensure that all branches have valid archives.
+    $branches = $this->github->branches($this->org, $this->org . '_module');
+    unset($branches['master']);
+    $this->validateFileMetadata($branches, FALSE);
+  }
+}
diff --git a/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/6.x-1.0.tar.gz b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/6.x-1.0.tar.gz
new file mode 100644
index 0000000..d77c795
Binary files /dev/null and b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/6.x-1.0.tar.gz differ
diff --git a/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/6.x-1.x.tar.gz b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/6.x-1.x.tar.gz
new file mode 100644
index 0000000..eff2e22
Binary files /dev/null and b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/6.x-1.x.tar.gz differ
diff --git a/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-1.0.tar.gz b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-1.0.tar.gz
new file mode 100644
index 0000000..0853786
Binary files /dev/null and b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-1.0.tar.gz differ
diff --git a/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-1.x.tar.gz b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-1.x.tar.gz
new file mode 100644
index 0000000..8c617be
Binary files /dev/null and b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-1.x.tar.gz differ
diff --git a/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-2.0-alpha1.tar.gz b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-2.0-alpha1.tar.gz
new file mode 100644
index 0000000..7362ca0
Binary files /dev/null and b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-2.0-alpha1.tar.gz differ
diff --git a/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-2.0.tar.gz b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-2.0.tar.gz
new file mode 100644
index 0000000..485ee7f
Binary files /dev/null and b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-2.0.tar.gz differ
diff --git a/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-2.x.tar.gz b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-2.x.tar.gz
new file mode 100644
index 0000000..fd3a09a
Binary files /dev/null and b/test/project_src_github_test_github_api/archives/drewpaul_inc/drewpaul_inc_module/archive/7.x-2.x.tar.gz differ
diff --git a/test/project_src_github_test_github_api/project_src_github_test_github_api.info b/test/project_src_github_test_github_api/project_src_github_test_github_api.info
new file mode 100644
index 0000000..08068b2
--- /dev/null
+++ b/test/project_src_github_test_github_api/project_src_github_test_github_api.info
@@ -0,0 +1,5 @@
+name = Project Source: GitHub - Mock GitHub API
+description = Mock GitHub API module for Project Source: GitHub. Do not enable.
+core = 7.x
+package = Testing
+hidden = TRUE
diff --git a/test/project_src_github_test_github_api/project_src_github_test_github_api.module b/test/project_src_github_test_github_api/project_src_github_test_github_api.module
new file mode 100644
index 0000000..39cee74
--- /dev/null
+++ b/test/project_src_github_test_github_api/project_src_github_test_github_api.module
@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * @file
+ * Classes and functions used to help test the Project Source: GitHub module.
+ */
+
+
+if (!function_exists('github_api_client')) {
+  /**
+   * Returns a mock GitHub API object, similar to what is returned by the GitHub
+   * API module.
+   *
+   * @return GithubApiMock
+   *   A custom mock GitHub API object we use for testing.
+   */
+  function github_api_client() {
+    return new GithubApiMock();
+  }
+}
+
+
+class GithubApiMock {
+
+  /**
+   * Returns an instance of the GithubApiMock object.
+   *
+   * @param string $type
+   *
+   * @return GithubApiMock
+   *   Rather than dealing with multiple mock objects, we set this object up to
+   *   return an instance of itself, which contains all methods we actually use.
+   */
+  public function api($type) {
+    return $this;
+  }
+
+  /**
+   * Returns an array that mimics a response from GitHub's API given a request
+   * for repositories for a given organization.
+   *
+   * @param string $organization
+   * @return array
+   *   An array representing all GitHub repos for a given organization.
+   */
+  public function repositories($organization) {
+    $module = $organization . '_module';
+    $repos[$module] = array(
+      'id' => 1234567,
+      'name' => $module,
+      'full_name' => $organization . '/' . $module,
+      'owner' => array(
+        'login' => $organization . '_user',
+      ),
+      'html_url' => 'http://example.com/' . $organization . '/' . $module,
+      'default_branch' => '7.x-2.x',
+    );
+
+    return $repos;
+  }
+
+  /**
+   * Returns an array that mimics a response from GitHub's API given a request
+   * for branches for a given author and repository.
+   *
+   * @param string $username
+   * @param string $repository
+   * @param string $branch
+   *
+   * @return array
+   *   An array representing all branches for a given repository.
+   */
+  public function branches($username, $repository, $branch = NULL) {
+    // Note we're making the branch name the key of the returned array. Although
+    // this does not match up with GitHub's response, we use it for testing.
+    $branches['6.x-1.x'] = array(
+      'name' => '6.x-1.x',
+      'commit' => array(
+        'sha' => '231ed864c57ce2c890230b52f958dd33c65e54f6',
+        'url' => 'http://api.example.com/repos/commits/etc/61',
+      ),
+    );
+    $branches['7.x-1.x'] = array(
+      'name' => '7.x-1.x',
+      'commit' => array(
+        'sha' => '231ed864c57ce2c890230b52f958dd33c65e54f7',
+        'url' => 'http://api.example.com/repos/commits/etc/71',
+      ),
+    );
+    $branches['7.x-2.x'] = array(
+      'name' => '7.x-2.x',
+      'commit' => array(
+        'sha' => '65812be004d5b2701be8099c5886071a4909e775',
+        'url' => 'http://api.example.com/repos/commits/etc/72',
+      ),
+    );
+    $branches['master'] = array(
+      'name' => 'master',
+      'commit' => array(
+        'sha' => '65812be004d5b2701be8099c5886071a4909e774',
+        'url' => 'http://api.example.com/repos/commits/etc/master',
+      ),
+    );
+
+    return $branches;
+  }
+
+  /**
+   * Returns an instance of the GithubApiMock object.
+   *
+   * @return GithubApiMock
+   *   Rather than dealing with multiple mock objects, we set this method up to
+   *   return an instance of itself. Commit methods are also located on this
+   *   object.
+   */
+  public function commits() {
+    return $this;
+  }
+
+  /**
+   * Returns an array that mimics a response from GitHub's API given a request
+   * for commit details for a given commit on a given repository.
+   *
+   * @param $username
+   * @param $repository
+   * @param $sha
+   *
+   * @return array
+   *   An array representing commit details for a given commitref for a given
+   *   repository.
+   */
+  public function show($username, $repository, $sha) {
+    return array(
+      'commit' => array(
+        'author' => array(
+          'date' => 'November 19, 1978',
+        ),
+      ),
+    );
+  }
+
+  /**
+   * Returns an array that mimics a response from GitHub's API given a request
+   * for release tags for a given repository.
+   *
+   * @param $username
+   * @param $repository
+   *
+   * @return array
+   *   An array representing release tag details for a given repository.
+   */
+  public function tags($username, $repository) {
+    // Note we're making the tag name the key of the returned array. Although
+    // this does not match up with GitHub's response, we use it for testing.
+    $tags['6.x-1.0'] = array(
+      'name' => '6.x-1.0',
+      'zipball_url' => 'https://api.example.com/repos/zipball/6x10',
+      'tarball_url' => 'https://api.example.com/repos/tarball/6x10',
+      'commit' => array(
+        'sha' => '231ed864c57ce2c890230b52f958dd33c65e54f6',
+        'url' => 'http://api.example.com/repos/commits/etc/6x10',
+      ),
+    );
+    $tags['7.x-1.0'] = array(
+      'name' => '7.x-1.0',
+      'zipball_url' => 'https://api.example.com/repos/zipball/7x10',
+      'tarball_url' => 'https://api.example.com/repos/tarball/7x10',
+      'commit' => array(
+        'sha' => '231ed864c57ce2c890230b52f958dd33c65e54f7',
+        'url' => 'http://api.example.com/repos/commits/etc/7x10',
+      ),
+    );
+    $tags['7.x-2.0-alpha1'] = array(
+      'name' => '7.x-2.0-alpha1',
+      'zipball_url' => 'https://api.example.com/repos/zipball/7x20a1',
+      'tarball_url' => 'https://api.example.com/repos/tarball/7x20a1',
+      'commit' => array(
+        'sha' => '65812be004d5b2701be8099c5886071a4909e774',
+        'url' => 'http://api.example.com/repos/commits/etc/7x20',
+      ),
+    );
+    $tags['7.x-2.0'] = array(
+      'name' => '7.x-2.0',
+      'zipball_url' => 'https://api.example.com/repos/zipball/7x20',
+      'tarball_url' => 'https://api.example.com/repos/tarball/7x20',
+      'commit' => array(
+        'sha' => '65812be004d5b2701be8099c5886071a4909e775',
+        'url' => 'http://api.example.com/repos/commits/etc/7x20',
+      ),
+    );
+
+    return $tags;
+  }
+}
