##// END OF EJS Templates
git-lfs: report chunked encoding support properly to the client....
marcink -
r199:39019410 default
parent child Browse files
Show More
@@ -1,171 +1,172 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2017 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import os
18 import os
19 import shutil
19 import shutil
20 import logging
20 import logging
21 from collections import OrderedDict
21 from collections import OrderedDict
22
22
23 log = logging.getLogger(__name__)
23 log = logging.getLogger(__name__)
24
24
25
25
26 class OidHandler(object):
26 class OidHandler(object):
27
27
28 def __init__(self, store, repo_name, auth, oid, obj_size, obj_data, obj_href,
28 def __init__(self, store, repo_name, auth, oid, obj_size, obj_data, obj_href,
29 obj_verify_href=None):
29 obj_verify_href=None):
30 self.current_store = store
30 self.current_store = store
31 self.repo_name = repo_name
31 self.repo_name = repo_name
32 self.auth = auth
32 self.auth = auth
33 self.oid = oid
33 self.oid = oid
34 self.obj_size = obj_size
34 self.obj_size = obj_size
35 self.obj_data = obj_data
35 self.obj_data = obj_data
36 self.obj_href = obj_href
36 self.obj_href = obj_href
37 self.obj_verify_href = obj_verify_href
37 self.obj_verify_href = obj_verify_href
38
38
39 def get_store(self, mode=None):
39 def get_store(self, mode=None):
40 return self.current_store
40 return self.current_store
41
41
42 def get_auth(self):
42 def get_auth(self):
43 """returns auth header for re-use in upload/download"""
43 """returns auth header for re-use in upload/download"""
44 return " ".join(self.auth)
44 return " ".join(self.auth)
45
45
46 def download(self):
46 def download(self):
47
47
48 store = self.get_store()
48 store = self.get_store()
49 response = None
49 response = None
50 has_errors = None
50 has_errors = None
51
51
52 if not store.has_oid():
52 if not store.has_oid():
53 # error reply back to client that something is wrong with dl
53 # error reply back to client that something is wrong with dl
54 err_msg = 'object: {} does not exist in store'.format(store.oid)
54 err_msg = 'object: {} does not exist in store'.format(store.oid)
55 has_errors = OrderedDict(
55 has_errors = OrderedDict(
56 error=OrderedDict(
56 error=OrderedDict(
57 code=404,
57 code=404,
58 message=err_msg
58 message=err_msg
59 )
59 )
60 )
60 )
61
61
62 download_action = OrderedDict(
62 download_action = OrderedDict(
63 href=self.obj_href,
63 href=self.obj_href,
64 header=OrderedDict([("Authorization", self.get_auth())])
64 header=OrderedDict([("Authorization", self.get_auth())])
65 )
65 )
66 if not has_errors:
66 if not has_errors:
67 response = OrderedDict(download=download_action)
67 response = OrderedDict(download=download_action)
68 return response, has_errors
68 return response, has_errors
69
69
70 def upload(self, skip_existing=True):
70 def upload(self, skip_existing=True):
71 """
71 """
72 Write upload action for git-lfs server
72 Write upload action for git-lfs server
73 """
73 """
74
74
75 store = self.get_store()
75 store = self.get_store()
76 response = None
76 response = None
77 has_errors = None
77 has_errors = None
78
78
79 # verify if we have the OID before, if we do, reply with empty
79 # verify if we have the OID before, if we do, reply with empty
80 if store.has_oid():
80 if store.has_oid():
81 log.debug('LFS: store already has oid %s', store.oid)
81 log.debug('LFS: store already has oid %s', store.oid)
82
82
83 # validate size
83 # validate size
84 size_match = store.size_oid() == self.obj_size
84 size_match = store.size_oid() == self.obj_size
85 if not size_match:
85 if not size_match:
86 log.warning('LFS: size mismatch for oid:%s', self.oid)
86 log.warning('LFS: size mismatch for oid:%s', self.oid)
87 elif skip_existing:
87 elif skip_existing:
88 log.debug('LFS: skipping further action as oid is existing')
88 log.debug('LFS: skipping further action as oid is existing')
89 return response, has_errors
89 return response, has_errors
90
90
91 chunked = ("Transfer-Encoding", "chunked")
91 upload_action = OrderedDict(
92 upload_action = OrderedDict(
92 href=self.obj_href,
93 href=self.obj_href,
93 header=OrderedDict([("Authorization", self.get_auth())])
94 header=OrderedDict([("Authorization", self.get_auth()), chunked])
94 )
95 )
95 if not has_errors:
96 if not has_errors:
96 response = OrderedDict(upload=upload_action)
97 response = OrderedDict(upload=upload_action)
97 # if specified in handler, return the verification endpoint
98 # if specified in handler, return the verification endpoint
98 if self.obj_verify_href:
99 if self.obj_verify_href:
99 verify_action = OrderedDict(
100 verify_action = OrderedDict(
100 href=self.obj_verify_href,
101 href=self.obj_verify_href,
101 header=OrderedDict([("Authorization", self.get_auth())])
102 header=OrderedDict([("Authorization", self.get_auth())])
102 )
103 )
103 response['verify'] = verify_action
104 response['verify'] = verify_action
104 return response, has_errors
105 return response, has_errors
105
106
106 def exec_operation(self, operation, *args, **kwargs):
107 def exec_operation(self, operation, *args, **kwargs):
107 handler = getattr(self, operation)
108 handler = getattr(self, operation)
108 log.debug('LFS: handling request using %s handler', handler)
109 log.debug('LFS: handling request using %s handler', handler)
109 return handler(*args, **kwargs)
110 return handler(*args, **kwargs)
110
111
111
112
112 class LFSOidStore(object):
113 class LFSOidStore(object):
113
114
114 def __init__(self, oid, repo, store_location=None):
115 def __init__(self, oid, repo, store_location=None):
115 self.oid = oid
116 self.oid = oid
116 self.repo = repo
117 self.repo = repo
117 self.store_path = store_location or self.get_default_store()
118 self.store_path = store_location or self.get_default_store()
118 self.tmp_oid_path = os.path.join(self.store_path, oid + '.tmp')
119 self.tmp_oid_path = os.path.join(self.store_path, oid + '.tmp')
119 self.oid_path = os.path.join(self.store_path, oid)
120 self.oid_path = os.path.join(self.store_path, oid)
120 self.fd = None
121 self.fd = None
121
122
122 def get_engine(self, mode):
123 def get_engine(self, mode):
123 """
124 """
124 engine = .get_engine(mode='wb')
125 engine = .get_engine(mode='wb')
125 with engine as f:
126 with engine as f:
126 f.write('...')
127 f.write('...')
127 """
128 """
128
129
129 class StoreEngine(object):
130 class StoreEngine(object):
130 def __init__(self, mode, store_path, oid_path, tmp_oid_path):
131 def __init__(self, mode, store_path, oid_path, tmp_oid_path):
131 self.mode = mode
132 self.mode = mode
132 self.store_path = store_path
133 self.store_path = store_path
133 self.oid_path = oid_path
134 self.oid_path = oid_path
134 self.tmp_oid_path = tmp_oid_path
135 self.tmp_oid_path = tmp_oid_path
135
136
136 def __enter__(self):
137 def __enter__(self):
137 if not os.path.isdir(self.store_path):
138 if not os.path.isdir(self.store_path):
138 os.makedirs(self.store_path)
139 os.makedirs(self.store_path)
139
140
140 # TODO(marcink): maybe write metadata here with size/oid ?
141 # TODO(marcink): maybe write metadata here with size/oid ?
141 fd = open(self.tmp_oid_path, self.mode)
142 fd = open(self.tmp_oid_path, self.mode)
142 self.fd = fd
143 self.fd = fd
143 return fd
144 return fd
144
145
145 def __exit__(self, exc_type, exc_value, traceback):
146 def __exit__(self, exc_type, exc_value, traceback):
146 # close tmp file, and rename to final destination
147 # close tmp file, and rename to final destination
147 self.fd.close()
148 self.fd.close()
148 shutil.move(self.tmp_oid_path, self.oid_path)
149 shutil.move(self.tmp_oid_path, self.oid_path)
149
150
150 return StoreEngine(
151 return StoreEngine(
151 mode, self.store_path, self.oid_path, self.tmp_oid_path)
152 mode, self.store_path, self.oid_path, self.tmp_oid_path)
152
153
153 def get_default_store(self):
154 def get_default_store(self):
154 """
155 """
155 Default store, consistent with defaults of Mercurial large files store
156 Default store, consistent with defaults of Mercurial large files store
156 which is /home/username/.cache/largefiles
157 which is /home/username/.cache/largefiles
157 """
158 """
158 user_home = os.path.expanduser("~")
159 user_home = os.path.expanduser("~")
159 return os.path.join(user_home, '.cache', 'lfs-store')
160 return os.path.join(user_home, '.cache', 'lfs-store')
160
161
161 def has_oid(self):
162 def has_oid(self):
162 return os.path.exists(os.path.join(self.store_path, self.oid))
163 return os.path.exists(os.path.join(self.store_path, self.oid))
163
164
164 def size_oid(self):
165 def size_oid(self):
165 size = -1
166 size = -1
166
167
167 if self.has_oid():
168 if self.has_oid():
168 oid = os.path.join(self.store_path, self.oid)
169 oid = os.path.join(self.store_path, self.oid)
169 size = os.stat(oid).st_size
170 size = os.stat(oid).st_size
170
171
171 return size
172 return size
@@ -1,238 +1,239 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2017 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import os
18 import os
19 import pytest
19 import pytest
20 from webtest.app import TestApp as WebObTestApp
20 from webtest.app import TestApp as WebObTestApp
21 import simplejson as json
21 import simplejson as json
22
22
23 from vcsserver.git_lfs.app import create_app
23 from vcsserver.git_lfs.app import create_app
24
24
25
25
26 @pytest.fixture(scope='function')
26 @pytest.fixture(scope='function')
27 def git_lfs_app(tmpdir):
27 def git_lfs_app(tmpdir):
28 custom_app = WebObTestApp(create_app(
28 custom_app = WebObTestApp(create_app(
29 git_lfs_enabled=True, git_lfs_store_path=str(tmpdir)))
29 git_lfs_enabled=True, git_lfs_store_path=str(tmpdir)))
30 custom_app._store = str(tmpdir)
30 custom_app._store = str(tmpdir)
31 return custom_app
31 return custom_app
32
32
33
33
34 @pytest.fixture()
34 @pytest.fixture()
35 def http_auth():
35 def http_auth():
36 return {'HTTP_AUTHORIZATION': "Basic XXXXX"}
36 return {'HTTP_AUTHORIZATION': "Basic XXXXX"}
37
37
38
38
39 class TestLFSApplication(object):
39 class TestLFSApplication(object):
40
40
41 def test_app_wrong_path(self, git_lfs_app):
41 def test_app_wrong_path(self, git_lfs_app):
42 git_lfs_app.get('/repo/info/lfs/xxx', status=404)
42 git_lfs_app.get('/repo/info/lfs/xxx', status=404)
43
43
44 def test_app_deprecated_endpoint(self, git_lfs_app):
44 def test_app_deprecated_endpoint(self, git_lfs_app):
45 response = git_lfs_app.post('/repo/info/lfs/objects', status=501)
45 response = git_lfs_app.post('/repo/info/lfs/objects', status=501)
46 assert response.status_code == 501
46 assert response.status_code == 501
47 assert json.loads(response.text) == {u'message': u'LFS: v1 api not supported'}
47 assert json.loads(response.text) == {u'message': u'LFS: v1 api not supported'}
48
48
49 def test_app_lock_verify_api_not_available(self, git_lfs_app):
49 def test_app_lock_verify_api_not_available(self, git_lfs_app):
50 response = git_lfs_app.post('/repo/info/lfs/locks/verify', status=501)
50 response = git_lfs_app.post('/repo/info/lfs/locks/verify', status=501)
51 assert response.status_code == 501
51 assert response.status_code == 501
52 assert json.loads(response.text) == {
52 assert json.loads(response.text) == {
53 u'message': u'GIT LFS locking api not supported'}
53 u'message': u'GIT LFS locking api not supported'}
54
54
55 def test_app_lock_api_not_available(self, git_lfs_app):
55 def test_app_lock_api_not_available(self, git_lfs_app):
56 response = git_lfs_app.post('/repo/info/lfs/locks', status=501)
56 response = git_lfs_app.post('/repo/info/lfs/locks', status=501)
57 assert response.status_code == 501
57 assert response.status_code == 501
58 assert json.loads(response.text) == {
58 assert json.loads(response.text) == {
59 u'message': u'GIT LFS locking api not supported'}
59 u'message': u'GIT LFS locking api not supported'}
60
60
61 def test_app_batch_api_missing_auth(self, git_lfs_app,):
61 def test_app_batch_api_missing_auth(self, git_lfs_app,):
62 git_lfs_app.post_json(
62 git_lfs_app.post_json(
63 '/repo/info/lfs/objects/batch', params={}, status=403)
63 '/repo/info/lfs/objects/batch', params={}, status=403)
64
64
65 def test_app_batch_api_unsupported_operation(self, git_lfs_app, http_auth):
65 def test_app_batch_api_unsupported_operation(self, git_lfs_app, http_auth):
66 response = git_lfs_app.post_json(
66 response = git_lfs_app.post_json(
67 '/repo/info/lfs/objects/batch', params={}, status=400,
67 '/repo/info/lfs/objects/batch', params={}, status=400,
68 extra_environ=http_auth)
68 extra_environ=http_auth)
69 assert json.loads(response.text) == {
69 assert json.loads(response.text) == {
70 u'message': u'unsupported operation mode: `None`'}
70 u'message': u'unsupported operation mode: `None`'}
71
71
72 def test_app_batch_api_missing_objects(self, git_lfs_app, http_auth):
72 def test_app_batch_api_missing_objects(self, git_lfs_app, http_auth):
73 response = git_lfs_app.post_json(
73 response = git_lfs_app.post_json(
74 '/repo/info/lfs/objects/batch', params={'operation': 'download'},
74 '/repo/info/lfs/objects/batch', params={'operation': 'download'},
75 status=400, extra_environ=http_auth)
75 status=400, extra_environ=http_auth)
76 assert json.loads(response.text) == {
76 assert json.loads(response.text) == {
77 u'message': u'missing objects data'}
77 u'message': u'missing objects data'}
78
78
79 def test_app_batch_api_unsupported_data_in_objects(
79 def test_app_batch_api_unsupported_data_in_objects(
80 self, git_lfs_app, http_auth):
80 self, git_lfs_app, http_auth):
81 params = {'operation': 'download',
81 params = {'operation': 'download',
82 'objects': [{}]}
82 'objects': [{}]}
83 response = git_lfs_app.post_json(
83 response = git_lfs_app.post_json(
84 '/repo/info/lfs/objects/batch', params=params, status=400,
84 '/repo/info/lfs/objects/batch', params=params, status=400,
85 extra_environ=http_auth)
85 extra_environ=http_auth)
86 assert json.loads(response.text) == {
86 assert json.loads(response.text) == {
87 u'message': u'unsupported data in objects'}
87 u'message': u'unsupported data in objects'}
88
88
89 def test_app_batch_api_download_missing_object(
89 def test_app_batch_api_download_missing_object(
90 self, git_lfs_app, http_auth):
90 self, git_lfs_app, http_auth):
91 params = {'operation': 'download',
91 params = {'operation': 'download',
92 'objects': [{'oid': '123', 'size': '1024'}]}
92 'objects': [{'oid': '123', 'size': '1024'}]}
93 response = git_lfs_app.post_json(
93 response = git_lfs_app.post_json(
94 '/repo/info/lfs/objects/batch', params=params,
94 '/repo/info/lfs/objects/batch', params=params,
95 extra_environ=http_auth)
95 extra_environ=http_auth)
96
96
97 expected_objects = [
97 expected_objects = [
98 {u'authenticated': True,
98 {u'authenticated': True,
99 u'errors': {u'error': {
99 u'errors': {u'error': {
100 u'code': 404,
100 u'code': 404,
101 u'message': u'object: 123 does not exist in store'}},
101 u'message': u'object: 123 does not exist in store'}},
102 u'oid': u'123',
102 u'oid': u'123',
103 u'size': u'1024'}
103 u'size': u'1024'}
104 ]
104 ]
105 assert json.loads(response.text) == {
105 assert json.loads(response.text) == {
106 'objects': expected_objects, 'transfer': 'basic'}
106 'objects': expected_objects, 'transfer': 'basic'}
107
107
108 def test_app_batch_api_download(self, git_lfs_app, http_auth):
108 def test_app_batch_api_download(self, git_lfs_app, http_auth):
109 oid = '456'
109 oid = '456'
110 oid_path = os.path.join(git_lfs_app._store, oid)
110 oid_path = os.path.join(git_lfs_app._store, oid)
111 if not os.path.isdir(os.path.dirname(oid_path)):
111 if not os.path.isdir(os.path.dirname(oid_path)):
112 os.makedirs(os.path.dirname(oid_path))
112 os.makedirs(os.path.dirname(oid_path))
113 with open(oid_path, 'wb') as f:
113 with open(oid_path, 'wb') as f:
114 f.write('OID_CONTENT')
114 f.write('OID_CONTENT')
115
115
116 params = {'operation': 'download',
116 params = {'operation': 'download',
117 'objects': [{'oid': oid, 'size': '1024'}]}
117 'objects': [{'oid': oid, 'size': '1024'}]}
118 response = git_lfs_app.post_json(
118 response = git_lfs_app.post_json(
119 '/repo/info/lfs/objects/batch', params=params,
119 '/repo/info/lfs/objects/batch', params=params,
120 extra_environ=http_auth)
120 extra_environ=http_auth)
121
121
122 expected_objects = [
122 expected_objects = [
123 {u'authenticated': True,
123 {u'authenticated': True,
124 u'actions': {
124 u'actions': {
125 u'download': {
125 u'download': {
126 u'header': {u'Authorization': u'Basic XXXXX'},
126 u'header': {u'Authorization': u'Basic XXXXX'},
127 u'href': u'http://localhost/repo/info/lfs/objects/456'},
127 u'href': u'http://localhost/repo/info/lfs/objects/456'},
128 },
128 },
129 u'oid': u'456',
129 u'oid': u'456',
130 u'size': u'1024'}
130 u'size': u'1024'}
131 ]
131 ]
132 assert json.loads(response.text) == {
132 assert json.loads(response.text) == {
133 'objects': expected_objects, 'transfer': 'basic'}
133 'objects': expected_objects, 'transfer': 'basic'}
134
134
135 def test_app_batch_api_upload(self, git_lfs_app, http_auth):
135 def test_app_batch_api_upload(self, git_lfs_app, http_auth):
136 params = {'operation': 'upload',
136 params = {'operation': 'upload',
137 'objects': [{'oid': '123', 'size': '1024'}]}
137 'objects': [{'oid': '123', 'size': '1024'}]}
138 response = git_lfs_app.post_json(
138 response = git_lfs_app.post_json(
139 '/repo/info/lfs/objects/batch', params=params,
139 '/repo/info/lfs/objects/batch', params=params,
140 extra_environ=http_auth)
140 extra_environ=http_auth)
141 expected_objects = [
141 expected_objects = [
142 {u'authenticated': True,
142 {u'authenticated': True,
143 u'actions': {
143 u'actions': {
144 u'upload': {
144 u'upload': {
145 u'header': {u'Authorization': u'Basic XXXXX'},
145 u'header': {u'Authorization': u'Basic XXXXX',
146 u'Transfer-Encoding': u'chunked'},
146 u'href': u'http://localhost/repo/info/lfs/objects/123'},
147 u'href': u'http://localhost/repo/info/lfs/objects/123'},
147 u'verify': {
148 u'verify': {
148 u'header': {u'Authorization': u'Basic XXXXX'},
149 u'header': {u'Authorization': u'Basic XXXXX'},
149 u'href': u'http://localhost/repo/info/lfs/verify'}
150 u'href': u'http://localhost/repo/info/lfs/verify'}
150 },
151 },
151 u'oid': u'123',
152 u'oid': u'123',
152 u'size': u'1024'}
153 u'size': u'1024'}
153 ]
154 ]
154 assert json.loads(response.text) == {
155 assert json.loads(response.text) == {
155 'objects': expected_objects, 'transfer': 'basic'}
156 'objects': expected_objects, 'transfer': 'basic'}
156
157
157 def test_app_verify_api_missing_data(self, git_lfs_app):
158 def test_app_verify_api_missing_data(self, git_lfs_app):
158 params = {'oid': 'missing',}
159 params = {'oid': 'missing',}
159 response = git_lfs_app.post_json(
160 response = git_lfs_app.post_json(
160 '/repo/info/lfs/verify', params=params,
161 '/repo/info/lfs/verify', params=params,
161 status=400)
162 status=400)
162
163
163 assert json.loads(response.text) == {
164 assert json.loads(response.text) == {
164 u'message': u'missing oid and size in request data'}
165 u'message': u'missing oid and size in request data'}
165
166
166 def test_app_verify_api_missing_obj(self, git_lfs_app):
167 def test_app_verify_api_missing_obj(self, git_lfs_app):
167 params = {'oid': 'missing', 'size': '1024'}
168 params = {'oid': 'missing', 'size': '1024'}
168 response = git_lfs_app.post_json(
169 response = git_lfs_app.post_json(
169 '/repo/info/lfs/verify', params=params,
170 '/repo/info/lfs/verify', params=params,
170 status=404)
171 status=404)
171
172
172 assert json.loads(response.text) == {
173 assert json.loads(response.text) == {
173 u'message': u'oid `missing` does not exists in store'}
174 u'message': u'oid `missing` does not exists in store'}
174
175
175 def test_app_verify_api_size_mismatch(self, git_lfs_app):
176 def test_app_verify_api_size_mismatch(self, git_lfs_app):
176 oid = 'existing'
177 oid = 'existing'
177 oid_path = os.path.join(git_lfs_app._store, oid)
178 oid_path = os.path.join(git_lfs_app._store, oid)
178 if not os.path.isdir(os.path.dirname(oid_path)):
179 if not os.path.isdir(os.path.dirname(oid_path)):
179 os.makedirs(os.path.dirname(oid_path))
180 os.makedirs(os.path.dirname(oid_path))
180 with open(oid_path, 'wb') as f:
181 with open(oid_path, 'wb') as f:
181 f.write('OID_CONTENT')
182 f.write('OID_CONTENT')
182
183
183 params = {'oid': oid, 'size': '1024'}
184 params = {'oid': oid, 'size': '1024'}
184 response = git_lfs_app.post_json(
185 response = git_lfs_app.post_json(
185 '/repo/info/lfs/verify', params=params, status=422)
186 '/repo/info/lfs/verify', params=params, status=422)
186
187
187 assert json.loads(response.text) == {
188 assert json.loads(response.text) == {
188 u'message': u'requested file size mismatch '
189 u'message': u'requested file size mismatch '
189 u'store size:11 requested:1024'}
190 u'store size:11 requested:1024'}
190
191
191 def test_app_verify_api(self, git_lfs_app):
192 def test_app_verify_api(self, git_lfs_app):
192 oid = 'existing'
193 oid = 'existing'
193 oid_path = os.path.join(git_lfs_app._store, oid)
194 oid_path = os.path.join(git_lfs_app._store, oid)
194 if not os.path.isdir(os.path.dirname(oid_path)):
195 if not os.path.isdir(os.path.dirname(oid_path)):
195 os.makedirs(os.path.dirname(oid_path))
196 os.makedirs(os.path.dirname(oid_path))
196 with open(oid_path, 'wb') as f:
197 with open(oid_path, 'wb') as f:
197 f.write('OID_CONTENT')
198 f.write('OID_CONTENT')
198
199
199 params = {'oid': oid, 'size': 11}
200 params = {'oid': oid, 'size': 11}
200 response = git_lfs_app.post_json(
201 response = git_lfs_app.post_json(
201 '/repo/info/lfs/verify', params=params)
202 '/repo/info/lfs/verify', params=params)
202
203
203 assert json.loads(response.text) == {
204 assert json.loads(response.text) == {
204 u'message': {u'size': u'ok', u'in_store': u'ok'}}
205 u'message': {u'size': u'ok', u'in_store': u'ok'}}
205
206
206 def test_app_download_api_oid_not_existing(self, git_lfs_app):
207 def test_app_download_api_oid_not_existing(self, git_lfs_app):
207 oid = 'missing'
208 oid = 'missing'
208
209
209 response = git_lfs_app.get(
210 response = git_lfs_app.get(
210 '/repo/info/lfs/objects/{oid}'.format(oid=oid), status=404)
211 '/repo/info/lfs/objects/{oid}'.format(oid=oid), status=404)
211
212
212 assert json.loads(response.text) == {
213 assert json.loads(response.text) == {
213 u'message': u'requested file with oid `missing` not found in store'}
214 u'message': u'requested file with oid `missing` not found in store'}
214
215
215 def test_app_download_api(self, git_lfs_app):
216 def test_app_download_api(self, git_lfs_app):
216 oid = 'existing'
217 oid = 'existing'
217 oid_path = os.path.join(git_lfs_app._store, oid)
218 oid_path = os.path.join(git_lfs_app._store, oid)
218 if not os.path.isdir(os.path.dirname(oid_path)):
219 if not os.path.isdir(os.path.dirname(oid_path)):
219 os.makedirs(os.path.dirname(oid_path))
220 os.makedirs(os.path.dirname(oid_path))
220 with open(oid_path, 'wb') as f:
221 with open(oid_path, 'wb') as f:
221 f.write('OID_CONTENT')
222 f.write('OID_CONTENT')
222
223
223 response = git_lfs_app.get(
224 response = git_lfs_app.get(
224 '/repo/info/lfs/objects/{oid}'.format(oid=oid))
225 '/repo/info/lfs/objects/{oid}'.format(oid=oid))
225 assert response
226 assert response
226
227
227 def test_app_upload(self, git_lfs_app):
228 def test_app_upload(self, git_lfs_app):
228 oid = 'uploaded'
229 oid = 'uploaded'
229
230
230 response = git_lfs_app.put(
231 response = git_lfs_app.put(
231 '/repo/info/lfs/objects/{oid}'.format(oid=oid), params='CONTENT')
232 '/repo/info/lfs/objects/{oid}'.format(oid=oid), params='CONTENT')
232
233
233 assert json.loads(response.text) == {u'upload': u'ok'}
234 assert json.loads(response.text) == {u'upload': u'ok'}
234
235
235 # verify that we actually wrote that OID
236 # verify that we actually wrote that OID
236 oid_path = os.path.join(git_lfs_app._store, oid)
237 oid_path = os.path.join(git_lfs_app._store, oid)
237 assert os.path.isfile(oid_path)
238 assert os.path.isfile(oid_path)
238 assert 'CONTENT' == open(oid_path).read()
239 assert 'CONTENT' == open(oid_path).read()
General Comments 0
You need to be logged in to leave comments. Login now