Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Now with universal filtering, sorting, pagination, and field selection!
- Loading branch information
Showing
18 changed files
with
1,414 additions
and
547 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# This file is part of Buildbot. Buildbot is free software: you can | ||
# redistribute it and/or modify it under the terms of the GNU General Public | ||
# License as published by the Free Software Foundation, version 2. | ||
# | ||
# This program 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 General Public License for more | ||
# details. | ||
# | ||
# You should have received a copy of the GNU General Public License along with | ||
# this program; if not, write to the Free Software Foundation, Inc., 51 | ||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
# | ||
# Copyright Buildbot Team Members | ||
|
||
# This is a static resource type and set of endpoints uesd as common data by | ||
# tests. | ||
|
||
from twisted.internet import defer | ||
from buildbot.data import base, types | ||
|
||
testData = { | ||
13: {'id': 13, 'info': 'ok', 'success': True}, | ||
14: {'id': 14, 'info': 'failed', 'success': False}, | ||
15: {'id': 15, 'info': 'warned', 'success': True}, | ||
16: {'id': 16, 'info': 'skipped', 'success': True}, | ||
17: {'id': 17, 'info': 'ignored', 'success': True}, | ||
18: {'id': 18, 'info': 'unexp', 'success': False}, | ||
19: {'id': 19, 'info': 'todo', 'success': True}, | ||
20: {'id': 20, 'info': 'error', 'success': False}, | ||
} | ||
|
||
class TestsEndpoint(base.Endpoint): | ||
isCollection = True | ||
pathPatterns = "/test" | ||
|
||
def get(self, resultSpec, kwargs): | ||
# results are sorted by ID for test stability | ||
return defer.succeed(sorted(testData.values(), key=lambda v : v['id'])) | ||
|
||
|
||
class FailEndpoint(base.Endpoint): | ||
isCollection = False | ||
pathPatterns = "/test/fail" | ||
|
||
def get(self, resultSpec, kwargs): | ||
return defer.fail(RuntimeError('oh noes')) | ||
|
||
|
||
class TestEndpoint(base.Endpoint): | ||
isCollection = False | ||
pathPatterns = "/test/n:testid" | ||
|
||
def get(self, resultSpec, kwargs): | ||
if kwargs['testid'] == 0: | ||
return None | ||
return defer.succeed(testData[kwargs['testid']]) | ||
|
||
def control(self, action, args, kwargs): | ||
if action == "fail": | ||
return defer.fail(RuntimeError("oh noes")) | ||
return defer.succeed({'action': action, 'args': args, 'kwargs': kwargs}) | ||
|
||
|
||
class Test(base.ResourceType): | ||
name = "test" | ||
plural = "tests" | ||
endpoints = [ TestsEndpoint, TestEndpoint, FailEndpoint ] | ||
|
||
class EntityType(types.Entity): | ||
id = types.Integer() | ||
info = types.String() | ||
success = types.Boolean() | ||
entityType = EntityType(name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
# This file is part of Buildbot. Buildbot is free software: you can | ||
# redistribute it and/or modify it under the terms of the GNU General Public | ||
# License as published by the Free Software Foundation, version 2. | ||
# | ||
# This program 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 General Public License for more | ||
# details. | ||
# | ||
# You should have received a copy of the GNU General Public License along with | ||
# this program; if not, write to the Free Software Foundation, Inc., 51 | ||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
# | ||
# Copyright Buildbot Team Members | ||
|
||
from twisted.internet import defer, reactor, protocol | ||
from twisted.trial import unittest | ||
from twisted.web import client | ||
from buildbot.util import json | ||
from buildbot.test.util import db | ||
from buildbot.test.fake import fakemaster, fakedb | ||
from buildbot.db import connector as dbconnector | ||
from buildbot.mq import connector as mqconnector | ||
from buildbot.data import connector as dataconnector | ||
from buildbot.www import service as wwwservice | ||
|
||
SOMETIME = 1348971992 | ||
OTHERTIME = 1008971992 | ||
|
||
|
||
class BodyReader(protocol.Protocol): | ||
# an IProtocol that reads the entire HTTP body and then calls back | ||
# with it | ||
|
||
def __init__(self, finishedDeferred): | ||
self.body = [] | ||
self.finishedDeferred = finishedDeferred | ||
|
||
def dataReceived(self, bytes): | ||
self.body.append(bytes) | ||
|
||
def connectionLost(self, reason): | ||
if reason.check(client.ResponseDone): | ||
self.finishedDeferred.callback(''.join(self.body)) | ||
else: | ||
self.finishedDeferred.errback(reason) | ||
|
||
|
||
class Www(db.RealDatabaseMixin, unittest.TestCase): | ||
|
||
master = None | ||
|
||
@defer.inlineCallbacks | ||
def setUp(self): | ||
# set up a full master serving HTTP | ||
yield self.setUpRealDatabase(table_names=['masters'], | ||
sqlite_memory=False) | ||
|
||
master = fakemaster.FakeMaster() | ||
|
||
master.config.db = dict(db_url=self.db_url) | ||
master.db = dbconnector.DBConnector(master, 'basedir') | ||
yield master.db.setup(check_version=False) | ||
|
||
master.config.mq = dict(type='simple') | ||
master.mq = mqconnector.MQConnector(master) | ||
master.mq.setup() | ||
|
||
master.data = dataconnector.DataConnector(master) | ||
|
||
master.config.www = dict( | ||
port='tcp:0:interface=127.0.0.1', | ||
debug=True) | ||
master.www = wwwservice.WWWService(master) | ||
yield master.www.startService() | ||
yield master.www.reconfigService(master.config) | ||
|
||
# now that we have a port, construct the real URL and insert it into | ||
# the config. The second reconfig isn't really required, but doesn't | ||
# hurt. | ||
self.url = 'http://127.0.0.1:%d/' % master.www.getPortnum() | ||
master.config.www['url'] = self.url | ||
yield master.www.reconfigService(master.config) | ||
|
||
self.master = master | ||
|
||
# build an HTTP agent | ||
self.pool = client.HTTPConnectionPool(reactor) | ||
self.agent = client.Agent(reactor, pool=self.pool) | ||
|
||
@defer.inlineCallbacks | ||
def tearDown(self): | ||
yield self.pool.closeCachedConnections() | ||
if self.master: | ||
yield self.master.www.stopService() | ||
|
||
@defer.inlineCallbacks | ||
def apiGet(self, url, expect200=True): | ||
pg = yield self.agent.request('GET', url) | ||
|
||
# this is kind of obscene, but protocols are like that | ||
d = defer.Deferred() | ||
bodyReader = BodyReader(d) | ||
pg.deliverBody(bodyReader) | ||
body = yield d | ||
|
||
# check this *after* reading the body, otherwise Trial will | ||
# complain tha the response is half-read | ||
if expect200 and pg.code != 200: | ||
self.fail("did not get 200 response for '%s'" % (url,)) | ||
|
||
defer.returnValue(json.loads(body)) | ||
|
||
def link(self, suffix): | ||
return self.url + 'api/v2/' + suffix | ||
|
||
# tests | ||
|
||
# There's no need to be exhaustive here. The intent is to test that data | ||
# can get all the way from the DB to a real HTTP client, and a few | ||
# resources will be sufficient to demonstrate that. | ||
|
||
@defer.inlineCallbacks | ||
def test_masters(self): | ||
yield self.insertTestData([ | ||
fakedb.Master(id=7, name='some:master', | ||
active=0, last_active=SOMETIME), | ||
fakedb.Master(id=8, name='other:master', | ||
active=1, last_active=OTHERTIME), | ||
]) | ||
|
||
res = yield self.apiGet(self.link('master')) | ||
self.assertEqual(res, { | ||
'masters': [ | ||
{'active': False, 'masterid': 7, 'name': 'some:master', | ||
'last_active': SOMETIME, 'link': self.link('master/7')}, | ||
{'active': True, 'masterid': 8, 'name': 'other:master', | ||
'last_active': OTHERTIME, 'link': self.link('master/8')}, | ||
], | ||
'meta': { | ||
'total': 2, | ||
'links': [ | ||
{'href': self.link('master'), 'rel': 'self'} | ||
], | ||
}}) | ||
|
||
res = yield self.apiGet(self.link('master/7')) | ||
self.assertEqual(res, { | ||
'masters': [ | ||
{'active': False, 'masterid': 7, 'name': 'some:master', | ||
'last_active': SOMETIME, 'link': self.link('master/7')}, | ||
], | ||
'meta': { | ||
'links': [ | ||
{'href': self.link('master/7'), 'rel': 'self'} | ||
], | ||
}}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.