Merge branch 'master' into nomenclature
Conflicts: test/data/test.db
This commit is contained in:
commit
f4681f2c18
60 changed files with 1716 additions and 496 deletions
Binary file not shown.
60
test/fulldbtest.sh
Executable file
60
test/fulldbtest.sh
Executable file
|
@ -0,0 +1,60 @@
|
|||
set -e
|
||||
|
||||
up_mysql() {
|
||||
# Run a SQL database on port 3306 inside of Docker.
|
||||
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password -d mysql
|
||||
|
||||
# Sleep for 5s to get MySQL get started.
|
||||
echo 'Sleeping for 10...'
|
||||
sleep 10
|
||||
|
||||
# Add the database to mysql.
|
||||
docker run --rm --link mysql:mysql mysql sh -c 'echo "create database genschema" | mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -ppassword'
|
||||
}
|
||||
|
||||
down_mysql() {
|
||||
docker kill mysql
|
||||
docker rm mysql
|
||||
}
|
||||
|
||||
up_postgres() {
|
||||
# Run a SQL database on port 5432 inside of Docker.
|
||||
docker run --name postgres -p 5432:5432 -d postgres
|
||||
|
||||
# Sleep for 5s to get SQL get started.
|
||||
echo 'Sleeping for 5...'
|
||||
sleep 5
|
||||
|
||||
# Add the database to postgres.
|
||||
docker run --rm --link postgres:postgres postgres sh -c 'echo "create database genschema" | psql -h "$POSTGRES_PORT_5432_TCP_ADDR" -p "$POSTGRES_PORT_5432_TCP_PORT" -U postgres'
|
||||
}
|
||||
|
||||
down_postgres() {
|
||||
docker kill postgres
|
||||
docker rm postgres
|
||||
}
|
||||
|
||||
run_tests() {
|
||||
TEST_DATABASE_URI=$1 TEST=true python -m unittest discover
|
||||
}
|
||||
|
||||
# Test (and generate, if requested) via MySQL.
|
||||
echo '> Starting MySQL'
|
||||
up_mysql
|
||||
|
||||
echo '> Running Full Test Suite (mysql)'
|
||||
set +e
|
||||
run_tests "mysql+pymysql://root:password@192.168.59.103/genschema"
|
||||
set -e
|
||||
down_mysql
|
||||
|
||||
# Test via Postgres.
|
||||
echo '> Starting Postgres'
|
||||
up_postgres
|
||||
|
||||
echo '> Running Full Test Suite (postgres)'
|
||||
set +e
|
||||
run_tests "postgresql://postgres@192.168.59.103/genschema"
|
||||
set -e
|
||||
down_postgres
|
||||
|
|
@ -43,7 +43,7 @@ from endpoints.api.permission import (RepositoryUserPermission, RepositoryTeamPe
|
|||
RepositoryTeamPermissionList, RepositoryUserPermissionList)
|
||||
|
||||
from endpoints.api.superuser import (SuperUserLogs, SuperUserList, SuperUserManagement,
|
||||
SuperUserSendRecoveryEmail)
|
||||
SuperUserSendRecoveryEmail, UsageInformation)
|
||||
|
||||
|
||||
try:
|
||||
|
@ -3636,6 +3636,24 @@ class TestTeamMemberInvite(ApiTestCase):
|
|||
self._run_test('DELETE', 400, 'devtable', None)
|
||||
|
||||
|
||||
class TestUsageInformation(ApiTestCase):
|
||||
def setUp(self):
|
||||
ApiTestCase.setUp(self)
|
||||
self._set_url(UsageInformation)
|
||||
|
||||
def test_get_anonymous(self):
|
||||
self._run_test('GET', 401, None, None)
|
||||
|
||||
def test_get_freshuser(self):
|
||||
self._run_test('GET', 403, 'freshuser', None)
|
||||
|
||||
def test_get_reader(self):
|
||||
self._run_test('GET', 403, 'reader', None)
|
||||
|
||||
def test_get_devtable(self):
|
||||
self._run_test('GET', 200, 'devtable', None)
|
||||
|
||||
|
||||
class TestSuperUserList(ApiTestCase):
|
||||
def setUp(self):
|
||||
ApiTestCase.setUp(self)
|
||||
|
|
|
@ -43,7 +43,8 @@ from endpoints.api.organization import (OrganizationList, OrganizationMember,
|
|||
from endpoints.api.repository import RepositoryList, RepositoryVisibility, Repository
|
||||
from endpoints.api.permission import (RepositoryUserPermission, RepositoryTeamPermission,
|
||||
RepositoryTeamPermissionList, RepositoryUserPermissionList)
|
||||
from endpoints.api.superuser import SuperUserLogs, SuperUserList, SuperUserManagement
|
||||
from endpoints.api.superuser import (SuperUserLogs, SuperUserList, SuperUserManagement,
|
||||
UsageInformation)
|
||||
|
||||
try:
|
||||
app.register_blueprint(api_bp, url_prefix='/api')
|
||||
|
@ -1139,6 +1140,8 @@ class TestChangeRepoVisibility(ApiTestCase):
|
|||
|
||||
class TestDeleteRepository(ApiTestCase):
|
||||
SIMPLE_REPO = ADMIN_ACCESS_USER + '/simple'
|
||||
COMPLEX_REPO = ADMIN_ACCESS_USER + '/complex'
|
||||
|
||||
def test_deleterepo(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
|
@ -1150,6 +1153,64 @@ class TestDeleteRepository(ApiTestCase):
|
|||
params=dict(repository=self.SIMPLE_REPO),
|
||||
expected_code=404)
|
||||
|
||||
def test_deleterepo2(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
self.deleteResponse(Repository,
|
||||
params=dict(repository=self.COMPLEX_REPO))
|
||||
|
||||
# Verify the repo was deleted.
|
||||
self.getResponse(Repository,
|
||||
params=dict(repository=self.COMPLEX_REPO),
|
||||
expected_code=404)
|
||||
|
||||
def test_populate_and_delete_repo(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
# Make sure the repository has come images and tags.
|
||||
self.assertTrue(len(list(model.get_repository_images(ADMIN_ACCESS_USER, 'complex'))) > 0)
|
||||
self.assertTrue(len(list(model.list_repository_tags(ADMIN_ACCESS_USER, 'complex'))) > 0)
|
||||
|
||||
# Add some data for the repository, in addition to is already existing images and tags.
|
||||
repository = model.get_repository(ADMIN_ACCESS_USER, 'complex')
|
||||
|
||||
# Create some access tokens.
|
||||
access_token = model.create_access_token(repository, 'read')
|
||||
model.create_access_token(repository, 'write')
|
||||
|
||||
delegate_token = model.create_delegate_token(ADMIN_ACCESS_USER, 'complex', 'sometoken', 'read')
|
||||
model.create_delegate_token(ADMIN_ACCESS_USER, 'complex', 'sometoken', 'write')
|
||||
|
||||
# Create some repository builds.
|
||||
model.create_repository_build(repository, access_token, {}, 'someid', 'foobar')
|
||||
model.create_repository_build(repository, delegate_token, {}, 'someid2', 'foobar2')
|
||||
|
||||
# Create some notifications.
|
||||
model.create_repo_notification(repository, 'repo_push', 'hipchat', {})
|
||||
model.create_repo_notification(repository, 'build_queued', 'slack', {})
|
||||
|
||||
# Create some logs.
|
||||
model.log_action('push_repo', ADMIN_ACCESS_USER, repository=repository)
|
||||
model.log_action('push_repo', ADMIN_ACCESS_USER, repository=repository)
|
||||
|
||||
# Create some build triggers.
|
||||
user = model.get_user(ADMIN_ACCESS_USER)
|
||||
model.create_build_trigger(repository, 'github', 'sometoken', user)
|
||||
model.create_build_trigger(repository, 'github', 'anothertoken', user)
|
||||
|
||||
# Create some email authorizations.
|
||||
model.create_email_authorization_for_repo(ADMIN_ACCESS_USER, 'complex', 'a@b.com')
|
||||
model.create_email_authorization_for_repo(ADMIN_ACCESS_USER, 'complex', 'b@c.com')
|
||||
|
||||
# Delete the repository.
|
||||
self.deleteResponse(Repository,
|
||||
params=dict(repository=self.COMPLEX_REPO))
|
||||
|
||||
# Verify the repo was deleted.
|
||||
self.getResponse(Repository,
|
||||
params=dict(repository=self.COMPLEX_REPO),
|
||||
expected_code=404)
|
||||
|
||||
|
||||
class TestGetRepository(ApiTestCase):
|
||||
PUBLIC_REPO = PUBLIC_USER + '/publicrepo'
|
||||
|
@ -1267,7 +1328,9 @@ class TestRepoBuilds(ApiTestCase):
|
|||
status_json = self.getJsonResponse(RepositoryBuildStatus,
|
||||
params=dict(repository=ADMIN_ACCESS_USER + '/building', build_uuid=build['id']))
|
||||
|
||||
self.assertEquals(status_json, build)
|
||||
self.assertEquals(status_json['id'], build['id'])
|
||||
self.assertEquals(status_json['resource_key'], build['resource_key'])
|
||||
self.assertEquals(status_json['trigger'], build['trigger'])
|
||||
|
||||
class TestRequestRepoBuild(ApiTestCase):
|
||||
def test_requestrepobuild(self):
|
||||
|
@ -1779,7 +1842,7 @@ class TestOrgSubscription(ApiTestCase):
|
|||
|
||||
class TestUserRobots(ApiTestCase):
|
||||
def getRobotNames(self):
|
||||
return [r['name'] for r in self.getJsonResponse(UserRobotList)['robots']]
|
||||
return [r['name'] for r in self.getJsonResponse(UserRobotList)['robots']]
|
||||
|
||||
def test_robots(self):
|
||||
self.login(NO_ACCESS_USER)
|
||||
|
@ -1833,6 +1896,65 @@ class TestOrgRobots(ApiTestCase):
|
|||
return [r['name'] for r in self.getJsonResponse(OrgRobotList,
|
||||
params=dict(orgname=ORGANIZATION))['robots']]
|
||||
|
||||
def test_delete_robot_after_use(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
# Create the robot.
|
||||
self.putJsonResponse(OrgRobot,
|
||||
params=dict(orgname=ORGANIZATION, robot_shortname='bender'),
|
||||
expected_code=201)
|
||||
|
||||
# Add the robot to a team.
|
||||
membername = ORGANIZATION + '+bender'
|
||||
self.putJsonResponse(TeamMember,
|
||||
params=dict(orgname=ORGANIZATION, teamname='readers',
|
||||
membername=membername))
|
||||
|
||||
# Add a repository permission.
|
||||
self.putJsonResponse(RepositoryUserPermission,
|
||||
params=dict(repository=ORGANIZATION + '/' + ORG_REPO, username=membername),
|
||||
data=dict(role='read'))
|
||||
|
||||
# Add a permission prototype with the robot as the activating user.
|
||||
self.postJsonResponse(PermissionPrototypeList,
|
||||
params=dict(orgname=ORGANIZATION),
|
||||
data=dict(role='read',
|
||||
activating_user={'name': membername},
|
||||
delegate={'kind': 'user',
|
||||
'name': membername}))
|
||||
|
||||
# Add a permission prototype with the robot as the delegating user.
|
||||
self.postJsonResponse(PermissionPrototypeList,
|
||||
params=dict(orgname=ORGANIZATION),
|
||||
data=dict(role='read',
|
||||
delegate={'kind': 'user',
|
||||
'name': membername}))
|
||||
|
||||
# Add a build trigger with the robot as the pull robot.
|
||||
database.BuildTriggerService.create(name='fakeservice')
|
||||
|
||||
# Add a new fake trigger.
|
||||
repo = model.get_repository(ORGANIZATION, ORG_REPO)
|
||||
user = model.get_user(ADMIN_ACCESS_USER)
|
||||
pull_robot = model.get_user(membername)
|
||||
model.create_build_trigger(repo, 'fakeservice', 'sometoken', user, pull_robot=pull_robot)
|
||||
|
||||
# Delete the robot and verify it works.
|
||||
self.deleteResponse(OrgRobot,
|
||||
params=dict(orgname=ORGANIZATION, robot_shortname='bender'))
|
||||
|
||||
# All the above records should now be deleted, along with the robot. We verify a few of the
|
||||
# critical ones below.
|
||||
|
||||
# Check the team.
|
||||
team = model.get_organization_team(ORGANIZATION, 'readers')
|
||||
members = [member.username for member in model.get_organization_team_members(team.id)]
|
||||
self.assertFalse(membername in members)
|
||||
|
||||
# Check the robot itself.
|
||||
self.assertIsNone(model.get_user(membername))
|
||||
|
||||
|
||||
def test_robots(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
|
@ -1931,7 +2053,14 @@ class TestOrganizationApplications(ApiTestCase):
|
|||
json = self.getJsonResponse(OrganizationApplications, params=dict(orgname=ORGANIZATION))
|
||||
|
||||
self.assertEquals(2, len(json['applications']))
|
||||
self.assertEquals(FAKE_APPLICATION_CLIENT_ID, json['applications'][0]['client_id'])
|
||||
|
||||
found = False
|
||||
for application in json['applications']:
|
||||
if application['client_id'] == FAKE_APPLICATION_CLIENT_ID:
|
||||
found = True
|
||||
break
|
||||
|
||||
self.assertTrue(found)
|
||||
|
||||
# Add a new application.
|
||||
json = self.postJsonResponse(OrganizationApplications, params=dict(orgname=ORGANIZATION),
|
||||
|
@ -1943,7 +2072,6 @@ class TestOrganizationApplications(ApiTestCase):
|
|||
# Retrieve the apps list again
|
||||
list_json = self.getJsonResponse(OrganizationApplications, params=dict(orgname=ORGANIZATION))
|
||||
self.assertEquals(3, len(list_json['applications']))
|
||||
self.assertEquals(json, list_json['applications'][2])
|
||||
|
||||
|
||||
class TestOrganizationApplicationResource(ApiTestCase):
|
||||
|
@ -2358,6 +2486,15 @@ class TestSuperUserList(ApiTestCase):
|
|||
assert len(json['users']) > 0
|
||||
|
||||
|
||||
class TestUsageInformation(ApiTestCase):
|
||||
def test_get_usage(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
json = self.getJsonResponse(UsageInformation)
|
||||
|
||||
assert 'usage' in json
|
||||
assert 'allowed' in json
|
||||
|
||||
|
||||
class TestSuperUserManagement(ApiTestCase):
|
||||
def test_get_user(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
|
|
@ -34,6 +34,17 @@ class TestGarbageColection(unittest.TestCase):
|
|||
for i in range(0, 2):
|
||||
model.find_or_create_derived_storage(image.storage, 'squash', preferred)
|
||||
|
||||
# Add some additional placements to the image.
|
||||
for location_name in ['local_eu']:
|
||||
location = database.ImageStorageLocation.get(name=location_name)
|
||||
|
||||
try:
|
||||
database.ImageStoragePlacement.get(location=location, storage=image.storage)
|
||||
except:
|
||||
continue
|
||||
|
||||
database.ImageStoragePlacement.create(location=location, storage=image.storage)
|
||||
|
||||
return image.storage
|
||||
|
||||
def createRepository(self, namespace=ADMIN_ACCESS_USER, name=REPO, **kwargs):
|
||||
|
|
|
@ -3,6 +3,7 @@ import tarfile
|
|||
|
||||
from StringIO import StringIO
|
||||
from util.streamlayerformat import StreamLayerMerger, AUFS_WHITEOUT
|
||||
from util.tarlayerformat import TarLayerReadException
|
||||
|
||||
class TestStreamLayerMerger(unittest.TestCase):
|
||||
def create_layer(self, **kwargs):
|
||||
|
@ -30,6 +31,9 @@ class TestStreamLayerMerger(unittest.TestCase):
|
|||
|
||||
return output.getvalue()
|
||||
|
||||
def create_empty_layer(self):
|
||||
return ''
|
||||
|
||||
def squash_layers(self, layers):
|
||||
def get_layers():
|
||||
return [StringIO(layer) for layer in layers]
|
||||
|
@ -337,5 +341,59 @@ class TestStreamLayerMerger(unittest.TestCase):
|
|||
self.assertHasFile(squashed, 'foobar/baz/some_file', 'foo')
|
||||
self.assertDoesNotHaveFile(squashed, 'foo/another_file')
|
||||
|
||||
|
||||
def test_delete_root_directory(self):
|
||||
third_layer = self.create_layer(
|
||||
foo = 'build/first_file',
|
||||
bar = 'build/second_file')
|
||||
|
||||
second_layer = self.create_layer(
|
||||
_ = 'build')
|
||||
|
||||
squashed = self.squash_layers([second_layer, third_layer])
|
||||
|
||||
self.assertDoesNotHaveFile(squashed, 'build/first_file')
|
||||
self.assertDoesNotHaveFile(squashed, 'build/second_file')
|
||||
|
||||
|
||||
def test_tar_empty_layer(self):
|
||||
third_layer = self.create_layer(
|
||||
foo = 'build/first_file',
|
||||
bar = 'build/second_file')
|
||||
|
||||
empty_layer = self.create_layer()
|
||||
|
||||
squashed = self.squash_layers([empty_layer, third_layer])
|
||||
|
||||
self.assertHasFile(squashed, 'build/first_file', 'foo')
|
||||
self.assertHasFile(squashed, 'build/second_file', 'bar')
|
||||
|
||||
|
||||
def test_data_empty_layer(self):
|
||||
third_layer = self.create_layer(
|
||||
foo = 'build/first_file',
|
||||
bar = 'build/second_file')
|
||||
|
||||
empty_layer = self.create_empty_layer()
|
||||
|
||||
squashed = self.squash_layers([empty_layer, third_layer])
|
||||
|
||||
self.assertHasFile(squashed, 'build/first_file', 'foo')
|
||||
self.assertHasFile(squashed, 'build/second_file', 'bar')
|
||||
|
||||
|
||||
def test_broken_layer(self):
|
||||
third_layer = self.create_layer(
|
||||
foo = 'build/first_file',
|
||||
bar = 'build/second_file')
|
||||
|
||||
broken_layer = 'not valid data'
|
||||
|
||||
try:
|
||||
self.squash_layers([broken_layer, third_layer])
|
||||
self.fail('Expected exception')
|
||||
except TarLayerReadException as ex:
|
||||
self.assertEquals('Could not read layer', ex.message)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import os
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from config import DefaultConfig
|
||||
|
@ -14,8 +16,11 @@ class FakeTransaction(object):
|
|||
class TestConfig(DefaultConfig):
|
||||
TESTING = True
|
||||
|
||||
DB_URI = 'sqlite:///:memory:'
|
||||
DB_CONNECTION_ARGS = {}
|
||||
DB_URI = os.environ.get('TEST_DATABASE_URI', 'sqlite:///:memory:')
|
||||
DB_CONNECTION_ARGS = {
|
||||
'threadlocals': True,
|
||||
'autorollback': True
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def create_transaction(db):
|
||||
|
@ -23,7 +28,7 @@ class TestConfig(DefaultConfig):
|
|||
|
||||
DB_TRANSACTION_FACTORY = create_transaction
|
||||
|
||||
DISTRIBUTED_STORAGE_CONFIG = {'local_us': ['FakeStorage', {}]}
|
||||
DISTRIBUTED_STORAGE_CONFIG = {'local_us': ['FakeStorage', {}], 'local_eu': ['FakeStorage', {}]}
|
||||
DISTRIBUTED_STORAGE_PREFERENCE = ['local_us']
|
||||
|
||||
BUILDLOGS_MODULE_AND_CLASS = ('test.testlogs', 'testlogs.TestBuildLogs')
|
||||
|
|
Reference in a new issue