summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Poignant <oliver@poignant.se>2016-05-08 01:19:03 +0200
committerOliver Poignant <oliver@poignant.se>2016-05-08 01:19:03 +0200
commit6e6fd2e4f13cfb827ae1f5a0c3e83f14cc1fa15a (patch)
tree5a8d88d329413a0e9b8f786529c4ee2ce118fbe7
parentae3c50f997427bc128ff303b4a3dbaea19c18fde (diff)
downloadGit-Auto-Deploy-6e6fd2e4f13cfb827ae1f5a0c3e83f14cc1fa15a.zip
Git-Auto-Deploy-6e6fd2e4f13cfb827ae1f5a0c3e83f14cc1fa15a.tar.gz
Git-Auto-Deploy-6e6fd2e4f13cfb827ae1f5a0c3e83f14cc1fa15a.tar.bz2
Automated testing of weebhook request handling
-rw-r--r--test/samples/github-push.test-case.json182
-rw-r--r--test/samples/gitlab-push.test-case.json110
-rw-r--r--test/stubs/__init__.py0
-rw-r--r--test/stubs/git.py16
-rw-r--r--test/stubs/process.py6
-rw-r--r--test/test_parsers.py85
-rw-r--r--test/utils.py108
7 files changed, 507 insertions, 0 deletions
diff --git a/test/samples/github-push.test-case.json b/test/samples/github-push.test-case.json
new file mode 100644
index 0000000..dbc9ccf
--- /dev/null
+++ b/test/samples/github-push.test-case.json
@@ -0,0 +1,182 @@
+{
+ "config": {
+ "branch": "master",
+ "deploy": "echo test!",
+ "remote": "origin",
+ "url": "https://github.com/olipo186/Git-Auto-Deploy.git"
+ },
+ "expected": {
+ "status": 200,
+ "data": [
+ {
+ "deploy": 0
+ }
+ ]
+ },
+ "headers": {
+ "accept": "*/*",
+ "content-length": "6602",
+ "content-type": "application/json",
+ "host": "narpau.se:8001",
+ "user-agent": "GitHub-Hookshot/e4028f5",
+ "x-github-delivery": "xxx",
+ "x-github-event": "push",
+ "x-hub-signature": "xxx"
+ },
+ "payload": {
+ "after": "b60ff44438d884b200d70de9c45fec5a15f2c0fa",
+ "base_ref": null,
+ "before": "6aa6dd514d5b80c9f36fec6c56d4094fd182de61",
+ "commits": [
+ {
+ "added": [],
+ "author": {
+ "email": "oliver@poignant.se",
+ "name": "Oliver Poignant",
+ "username": "olipo186"
+ },
+ "committer": {
+ "email": "oliver@poignant.se",
+ "name": "Oliver Poignant",
+ "username": "olipo186"
+ },
+ "distinct": true,
+ "id": "b60ff44438d884b200d70de9c45fec5a15f2c0fa",
+ "message": "Updated docs",
+ "modified": [
+ "gitautodeploy/httpserver.py"
+ ],
+ "removed": [],
+ "timestamp": "2016-05-08T00:08:09+02:00",
+ "tree_id": "af9230d6223d6f6849b0f86ff7b5bef8b777a85b",
+ "url": "https://github.com/olipo186/Git-Auto-Deploy/commit/b60ff44438d884b200d70de9c45fec5a15f2c0fa"
+ }
+ ],
+ "compare": "https://github.com/olipo186/Git-Auto-Deploy/compare/6aa6dd514d5b...b60ff44438d8",
+ "created": false,
+ "deleted": false,
+ "forced": false,
+ "head_commit": {
+ "added": [],
+ "author": {
+ "email": "oliver@poignant.se",
+ "name": "Oliver Poignant",
+ "username": "olipo186"
+ },
+ "committer": {
+ "email": "oliver@poignant.se",
+ "name": "Oliver Poignant",
+ "username": "olipo186"
+ },
+ "distinct": true,
+ "id": "b60ff44438d884b200d70de9c45fec5a15f2c0fa",
+ "message": "Updated docs",
+ "modified": [
+ "gitautodeploy/httpserver.py"
+ ],
+ "removed": [],
+ "timestamp": "2016-05-08T00:08:09+02:00",
+ "tree_id": "af9230d6223d6f6849b0f86ff7b5bef8b777a85b",
+ "url": "https://github.com/olipo186/Git-Auto-Deploy/commit/b60ff44438d884b200d70de9c45fec5a15f2c0fa"
+ },
+ "pusher": {
+ "email": "oliver@poignant.se",
+ "name": "olipo186"
+ },
+ "ref": "refs/heads/master",
+ "repository": {
+ "archive_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/{archive_format}{/ref}",
+ "assignees_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/assignees{/user}",
+ "blobs_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/blobs{/sha}",
+ "branches_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/branches{/branch}",
+ "clone_url": "https://github.com/olipo186/Git-Auto-Deploy.git",
+ "collaborators_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/collaborators{/collaborator}",
+ "comments_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/comments{/number}",
+ "commits_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/commits{/sha}",
+ "compare_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/compare/{base}...{head}",
+ "contents_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/contents/{+path}",
+ "contributors_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/contributors",
+ "created_at": 1370546738,
+ "default_branch": "master",
+ "deployments_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/deployments",
+ "description": "Deploy your GitHub, GitLab or Bitbucket projects automatically on Git push events or webhooks using this small HTTP server written in Python. Continuous deployment in it's most simple form.",
+ "downloads_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/downloads",
+ "events_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/events",
+ "fork": false,
+ "forks": 71,
+ "forks_count": 71,
+ "forks_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/forks",
+ "full_name": "olipo186/Git-Auto-Deploy",
+ "git_commits_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/commits{/sha}",
+ "git_refs_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/refs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/tags{/sha}",
+ "git_url": "git://github.com/olipo186/Git-Auto-Deploy.git",
+ "has_downloads": true,
+ "has_issues": true,
+ "has_pages": true,
+ "has_wiki": true,
+ "homepage": "http://olipo186.github.io/Git-Auto-Deploy/",
+ "hooks_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/hooks",
+ "html_url": "https://github.com/olipo186/Git-Auto-Deploy",
+ "id": 10534595,
+ "issue_comment_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/comments{/number}",
+ "issue_events_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues/events{/number}",
+ "issues_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/issues{/number}",
+ "keys_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/keys{/key_id}",
+ "labels_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/labels{/name}",
+ "language": "Python",
+ "languages_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/languages",
+ "master_branch": "master",
+ "merges_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/merges",
+ "milestones_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/milestones{/number}",
+ "mirror_url": null,
+ "name": "Git-Auto-Deploy",
+ "notifications_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/notifications{?since,all,participating}",
+ "open_issues": 9,
+ "open_issues_count": 9,
+ "owner": {
+ "email": "oliver@poignant.se",
+ "name": "olipo186"
+ },
+ "private": false,
+ "pulls_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/pulls{/number}",
+ "pushed_at": 1462658898,
+ "releases_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/releases{/id}",
+ "size": 502,
+ "ssh_url": "git@github.com:olipo186/Git-Auto-Deploy.git",
+ "stargazers": 259,
+ "stargazers_count": 259,
+ "stargazers_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/stargazers",
+ "statuses_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/statuses/{sha}",
+ "subscribers_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/subscribers",
+ "subscription_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/subscription",
+ "svn_url": "https://github.com/olipo186/Git-Auto-Deploy",
+ "tags_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/tags",
+ "teams_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/teams",
+ "trees_url": "https://api.github.com/repos/olipo186/Git-Auto-Deploy/git/trees{/sha}",
+ "updated_at": "2016-05-07T11:10:07Z",
+ "url": "https://github.com/olipo186/Git-Auto-Deploy",
+ "watchers": 259,
+ "watchers_count": 259
+ },
+ "sender": {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1056476?v=3",
+ "events_url": "https://api.github.com/users/olipo186/events{/privacy}",
+ "followers_url": "https://api.github.com/users/olipo186/followers",
+ "following_url": "https://api.github.com/users/olipo186/following{/other_user}",
+ "gists_url": "https://api.github.com/users/olipo186/gists{/gist_id}",
+ "gravatar_id": "",
+ "html_url": "https://github.com/olipo186",
+ "id": 1056476,
+ "login": "olipo186",
+ "organizations_url": "https://api.github.com/users/olipo186/orgs",
+ "received_events_url": "https://api.github.com/users/olipo186/received_events",
+ "repos_url": "https://api.github.com/users/olipo186/repos",
+ "site_admin": false,
+ "starred_url": "https://api.github.com/users/olipo186/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/olipo186/subscriptions",
+ "type": "User",
+ "url": "https://api.github.com/users/olipo186"
+ }
+ }
+} \ No newline at end of file
diff --git a/test/samples/gitlab-push.test-case.json b/test/samples/gitlab-push.test-case.json
new file mode 100644
index 0000000..3c8b0d9
--- /dev/null
+++ b/test/samples/gitlab-push.test-case.json
@@ -0,0 +1,110 @@
+{
+ "config": {
+ "branch": "master",
+ "deploy": "echo test!",
+ "remote": "origin",
+ "url": "http://gitlab.example.com/oliver/poignant-se.git"
+ },
+ "expected": {
+ "status": 200,
+ "data": [
+ {
+ "deploy": 0
+ }
+ ]
+ },
+ "headers": {
+ "connection": "close",
+ "content-length": "2336",
+ "content-type": "application/json",
+ "host": "example.com:8001",
+ "x-gitlab-event": "Push Hook"
+ },
+ "payload": {
+ "after": "bb26e21adb5a97d3f7b45d0c29506d52793c8f3b",
+ "before": "fdd525130a91991648783608f9d55c359ed41abf",
+ "checkout_sha": "bb26e21adb5a97d3f7b45d0c29506d52793c8f3b",
+ "commits": [
+ {
+ "added": [],
+ "author": {
+ "email": "oliver@poignant.se",
+ "name": "Oliver Poignant"
+ },
+ "id": "bb26e21adb5a97d3f7b45d0c29506d52793c8f3b",
+ "message": "aa",
+ "modified": [
+ "README.md"
+ ],
+ "removed": [],
+ "timestamp": "2016-04-14T00:19:23+02:00",
+ "url": "http://gitlab.example.com/oliver/poignant-se/commit/bb26e21adb5a97d3f7b45d0c29506d52793c8f3b"
+ },
+ {
+ "added": [],
+ "author": {
+ "email": "oliver@poignant.se",
+ "name": "Oliver Poignant"
+ },
+ "id": "dcfccd6cb19ef50c6b7e27a657f9e37bd041a913",
+ "message": "ss",
+ "modified": [
+ "README.md"
+ ],
+ "removed": [],
+ "timestamp": "2016-04-14T00:18:53+02:00",
+ "url": "http://gitlab.example.com/oliver/poignant-se/commit/dcfccd6cb19ef50c6b7e27a657f9e37bd041a913"
+ },
+ {
+ "added": [],
+ "author": {
+ "email": "oliver@poignant.se",
+ "name": "Oliver Poignant"
+ },
+ "id": "fdd525130a91991648783608f9d55c359ed41abf",
+ "message": "aa",
+ "modified": [
+ "README.md"
+ ],
+ "removed": [],
+ "timestamp": "2016-04-14T00:08:43+02:00",
+ "url": "http://gitlab.example.com/oliver/poignant-se/commit/fdd525130a91991648783608f9d55c359ed41abf"
+ }
+ ],
+ "event_name": "push",
+ "message": null,
+ "object_kind": "push",
+ "project": {
+ "avatar_url": null,
+ "default_branch": "master",
+ "description": "",
+ "git_http_url": "http://gitlab.example.com/oliver/poignant-se.git",
+ "git_ssh_url": "ssh://git@example.com/oliver/poignant-se.git",
+ "homepage": "http://gitlab.example.com/oliver/poignant-se",
+ "http_url": "http://gitlab.example.com/oliver/poignant-se.git",
+ "name": "poignant-se",
+ "namespace": "oliver",
+ "path_with_namespace": "oliver/poignant-se",
+ "ssh_url": "ssh://git@example.com/oliver/poignant-se.git",
+ "url": "ssh://git@example.com/oliver/poignant-se.git",
+ "visibility_level": 0,
+ "web_url": "http://gitlab.example.com/oliver/poignant-se"
+ },
+ "project_id": 28,
+ "ref": "refs/heads/master",
+ "repository": {
+ "description": "",
+ "git_http_url": "http://gitlab.example.com/oliver/poignant-se.git",
+ "git_ssh_url": "ssh://git@example.com/oliver/poignant-se.git",
+ "homepage": "http://gitlab.example.com/oliver/poignant-se",
+ "name": "poignant-se",
+ "url": "ssh://git@example.com/oliver/poignant-se.git",
+ "visibility_level": 0
+ },
+ "total_commits_count": 3,
+ "user_avatar": "http://www.gravatar.com/avatar/bdf723d6a6f847ec075a6809aa4808fb?s=80&d=identicon",
+ "user_email": "oliver@poignant.se",
+ "user_id": 7,
+ "user_name": "Oliver Poignant"
+ }
+} \ No newline at end of file
diff --git a/test/stubs/__init__.py b/test/stubs/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/stubs/__init__.py
diff --git a/test/stubs/git.py b/test/stubs/git.py
new file mode 100644
index 0000000..d023a51
--- /dev/null
+++ b/test/stubs/git.py
@@ -0,0 +1,16 @@
+class GitWrapper(object):
+
+ @staticmethod
+ def pull(*args, **kwargs):
+ """Fake git pull"""
+ return 0
+
+ @staticmethod
+ def clone(*args, **kwargs):
+ """Fake git clone"""
+ return 0
+
+ @staticmethod
+ def deploy(*args, **kwargs):
+ """Fake deploy"""
+ return 0
diff --git a/test/stubs/process.py b/test/stubs/process.py
new file mode 100644
index 0000000..8695c96
--- /dev/null
+++ b/test/stubs/process.py
@@ -0,0 +1,6 @@
+class ProcessWrapper():
+
+ @staticmethod
+ def call(*args, **kwargs):
+ """Fake process call"""
+ return 0
diff --git a/test/test_parsers.py b/test/test_parsers.py
new file mode 100644
index 0000000..a5ba65f
--- /dev/null
+++ b/test/test_parsers.py
@@ -0,0 +1,85 @@
+import unittest
+from utils import WebhookTestCaseBase
+
+class WebhookTestCase(WebhookTestCaseBase):
+
+ test_case = None
+ test_name = None
+
+ def set_test_case(self, test_case, test_name):
+ self.test_case = test_case
+ self.test_name = test_name
+
+ def shortDescription(self):
+ return self.test_name
+
+ def runTest(self):
+ import requests
+ import json
+
+ # GAD configuration for this test case
+ config = {
+ 'port': 0,
+ 'intercept-stdout': False,
+ 'detailed-response': True,
+ 'log-level': 'WARNING',
+ 'log-test-case': True,
+ 'repositories': []
+ }
+
+ if 'url' in self.test_case['config']:
+ config['repositories'].append(self.test_case['config'])
+
+ # Start GAD instance
+ self.start_gad(config)
+
+ # Send webhook request
+ session = requests.session()
+ response = requests.post('http://localhost:%s/' % self.gad_port(), data=json.dumps(self.test_case['payload']), headers=self.test_case['headers'])
+
+ # Verify response status
+ self.assertEqual(response.status_code, self.test_case['expected']['status'])
+
+ # Compare results
+ if 'data' in self.test_case['expected'] and self.test_case['expected']['data']:
+ actual = json.dumps(response.json())
+ expected = json.dumps(self.test_case['expected']['data'])
+ self.assertEqual(actual, expected)
+
+ # Wait for GAD to handle the request
+ self.await_gad()
+
+def suite():
+ import os
+ import json
+
+ suite = unittest.TestSuite()
+
+ # Look for test cases in samples dir
+ samples_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'samples')
+ for item in os.listdir(samples_dir):
+
+ if not item[-15:] == '.test-case.json':
+ continue
+
+ file = open(os.path.join(samples_dir, item), 'r')
+ test_case = json.loads(file.read())
+
+ t = WebhookTestCase()
+ t.set_test_case(test_case, item)
+
+ suite.addTest(t)
+
+ return suite
+
+if __name__ == '__main__':
+ from unittest import TestResult
+ #result = TestResult()
+
+ #suite().run(result)
+ #unittest.main()
+ #print result
+
+ suite = suite()
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
diff --git a/test/utils.py b/test/utils.py
new file mode 100644
index 0000000..a686485
--- /dev/null
+++ b/test/utils.py
@@ -0,0 +1,108 @@
+import unittest
+
+class WebhookTestCaseBase(unittest.TestCase):
+
+ thread = None
+
+ def setUp(self):
+ import sys
+
+ # Use our custom importer to replace certain modules with stub modules to
+ # enable testing of other parts of GAD
+ sys.meta_path.append(StubImporter())
+
+ def start_gad(self, test_config):
+ import sys
+ import time
+ sys.path.insert(1, '..')
+
+ from gitautodeploy.cli.config import get_config_defaults, init_config
+
+ config = get_config_defaults()
+ config.update(test_config)
+
+ init_config(config)
+
+ # Create a GAD instance
+ self.thread = GADRunnerThread(config)
+
+ # Run GAD in a thread
+ self.thread.start()
+
+ def gad_port(self):
+ return self.thread.port
+
+ def await_gad(self):
+ """Waits for GAD and all it's threads to complete."""
+ import threading
+
+ # Wait for GAD runner thread to finish
+ self.thread.join()
+
+ # Wait for all request handler threads to finish
+ main_thread = threading.currentThread()
+ for current_thread in threading.enumerate():
+ if current_thread == main_thread:
+ continue
+ #print "Waiting for thread %s to finish" % current_thread
+ current_thread.join()
+
+ def tearDown(self):
+ pass
+
+
+class StubImporter(object):
+
+ overload_modules = ['gitautodeploy.wrappers.git', 'gitautodeploy.wrappers.process']
+
+ def find_module(self, full_name, package_path):
+
+ # Intervene when any wrapper module is imported
+ if full_name in self.overload_modules:
+
+ # Return a loader
+ return self
+
+ return None
+
+ def load_module(self, full_name):
+ """Load matching module from stubs package (test stub) instead of main gitautodeploy package."""
+ import imp
+ import sys
+
+ module_name = full_name.split('.')[-1]
+ module_info = imp.find_module(module_name, ['stubs'])
+ module = imp.load_module(module_name, *module_info)
+ sys.modules[module_name] = module
+
+# print "returning module %s" % module_name
+
+# module.history.append('WAH')
+
+ # Return a module
+ return module
+
+
+import threading
+
+class GADRunnerThread(threading.Thread):
+ def __init__(self, config):
+ super(GADRunnerThread, self).__init__()
+ import sys
+ import time
+
+ from gitautodeploy import GitAutoDeploy
+
+ self._app = GitAutoDeploy()
+ self._app.setup(config)
+
+ # Store PID and port in thread instance
+ self.pid = self._app._pid
+ self.port = self._app._port
+
+ def run(self):
+ self._app.handle_request()
+
+ def exit(self):
+ self._app.stop()
+ self._app.close()