|
@@ -21,8 +21,20 @@
|
|
from cStringIO import StringIO
|
|
from cStringIO import StringIO
|
|
import re
|
|
import re
|
|
|
|
|
|
|
|
+from dulwich.object_store import (
|
|
|
|
+ MemoryObjectStore,
|
|
|
|
+ )
|
|
from dulwich.objects import (
|
|
from dulwich.objects import (
|
|
Blob,
|
|
Blob,
|
|
|
|
+ Tag,
|
|
|
|
+ )
|
|
|
|
+from dulwich.repo import (
|
|
|
|
+ BaseRepo,
|
|
|
|
+ DictRefsContainer,
|
|
|
|
+ MemoryRepo,
|
|
|
|
+ )
|
|
|
|
+from dulwich.server import (
|
|
|
|
+ DictBackend,
|
|
)
|
|
)
|
|
from dulwich.tests import (
|
|
from dulwich.tests import (
|
|
TestCase,
|
|
TestCase,
|
|
@@ -31,33 +43,73 @@ from dulwich.web import (
|
|
HTTP_OK,
|
|
HTTP_OK,
|
|
HTTP_NOT_FOUND,
|
|
HTTP_NOT_FOUND,
|
|
HTTP_FORBIDDEN,
|
|
HTTP_FORBIDDEN,
|
|
|
|
+ HTTP_ERROR,
|
|
send_file,
|
|
send_file,
|
|
|
|
+ get_text_file,
|
|
|
|
+ get_loose_object,
|
|
|
|
+ get_pack_file,
|
|
|
|
+ get_idx_file,
|
|
get_info_refs,
|
|
get_info_refs,
|
|
|
|
+ get_info_packs,
|
|
handle_service_request,
|
|
handle_service_request,
|
|
_LengthLimitedFile,
|
|
_LengthLimitedFile,
|
|
HTTPGitRequest,
|
|
HTTPGitRequest,
|
|
HTTPGitApplication,
|
|
HTTPGitApplication,
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+from utils import make_object
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class TestHTTPGitRequest(HTTPGitRequest):
|
|
|
|
+ """HTTPGitRequest with overridden methods to help test caching."""
|
|
|
|
+
|
|
|
|
+ def __init__(self, *args, **kwargs):
|
|
|
|
+ HTTPGitRequest.__init__(self, *args, **kwargs)
|
|
|
|
+ self.cached = None
|
|
|
|
+
|
|
|
|
+ def nocache(self):
|
|
|
|
+ self.cached = False
|
|
|
|
+
|
|
|
|
+ def cache_forever(self):
|
|
|
|
+ self.cached = True
|
|
|
|
+
|
|
|
|
|
|
class WebTestCase(TestCase):
|
|
class WebTestCase(TestCase):
|
|
- """Base TestCase that sets up some useful instance vars."""
|
|
|
|
|
|
+ """Base TestCase with useful instance vars and utility functions."""
|
|
|
|
+
|
|
|
|
+ _req_class = TestHTTPGitRequest
|
|
|
|
|
|
def setUp(self):
|
|
def setUp(self):
|
|
super(WebTestCase, self).setUp()
|
|
super(WebTestCase, self).setUp()
|
|
self._environ = {}
|
|
self._environ = {}
|
|
- self._req = HTTPGitRequest(self._environ, self._start_response,
|
|
|
|
- handlers=self._handlers())
|
|
|
|
|
|
+ self._req = self._req_class(self._environ, self._start_response,
|
|
|
|
+ handlers=self._handlers())
|
|
self._status = None
|
|
self._status = None
|
|
self._headers = []
|
|
self._headers = []
|
|
|
|
+ self._output = StringIO()
|
|
|
|
|
|
def _start_response(self, status, headers):
|
|
def _start_response(self, status, headers):
|
|
self._status = status
|
|
self._status = status
|
|
self._headers = list(headers)
|
|
self._headers = list(headers)
|
|
|
|
+ return self._output.write
|
|
|
|
|
|
def _handlers(self):
|
|
def _handlers(self):
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
+ def assertContentTypeEquals(self, expected):
|
|
|
|
+ self.assertTrue(('Content-Type', expected) in self._headers)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def _test_backend(objects, refs=None, named_files=None):
|
|
|
|
+ if not refs:
|
|
|
|
+ refs = {}
|
|
|
|
+ if not named_files:
|
|
|
|
+ named_files = {}
|
|
|
|
+ repo = MemoryRepo.init_bare(objects, refs)
|
|
|
|
+ for path, contents in named_files.iteritems():
|
|
|
|
+ repo._put_named_file(path, contents)
|
|
|
|
+ return DictBackend({'/': repo})
|
|
|
|
+
|
|
|
|
|
|
class DumbHandlersTestCase(WebTestCase):
|
|
class DumbHandlersTestCase(WebTestCase):
|
|
|
|
|
|
@@ -67,10 +119,10 @@ class DumbHandlersTestCase(WebTestCase):
|
|
|
|
|
|
def test_send_file(self):
|
|
def test_send_file(self):
|
|
f = StringIO('foobar')
|
|
f = StringIO('foobar')
|
|
- output = ''.join(send_file(self._req, f, 'text/plain'))
|
|
|
|
|
|
+ output = ''.join(send_file(self._req, f, 'some/thing'))
|
|
self.assertEquals('foobar', output)
|
|
self.assertEquals('foobar', output)
|
|
self.assertEquals(HTTP_OK, self._status)
|
|
self.assertEquals(HTTP_OK, self._status)
|
|
- self.assertTrue(('Content-Type', 'text/plain') in self._headers)
|
|
|
|
|
|
+ self.assertContentTypeEquals('some/thing')
|
|
self.assertTrue(f.closed)
|
|
self.assertTrue(f.closed)
|
|
|
|
|
|
def test_send_file_buffered(self):
|
|
def test_send_file_buffered(self):
|
|
@@ -78,93 +130,152 @@ class DumbHandlersTestCase(WebTestCase):
|
|
xs = 'x' * bufsize
|
|
xs = 'x' * bufsize
|
|
f = StringIO(2 * xs)
|
|
f = StringIO(2 * xs)
|
|
self.assertEquals([xs, xs],
|
|
self.assertEquals([xs, xs],
|
|
- list(send_file(self._req, f, 'text/plain')))
|
|
|
|
|
|
+ list(send_file(self._req, f, 'some/thing')))
|
|
self.assertEquals(HTTP_OK, self._status)
|
|
self.assertEquals(HTTP_OK, self._status)
|
|
- self.assertTrue(('Content-Type', 'text/plain') in self._headers)
|
|
|
|
|
|
+ self.assertContentTypeEquals('some/thing')
|
|
self.assertTrue(f.closed)
|
|
self.assertTrue(f.closed)
|
|
|
|
|
|
def test_send_file_error(self):
|
|
def test_send_file_error(self):
|
|
class TestFile(object):
|
|
class TestFile(object):
|
|
- def __init__(self):
|
|
|
|
|
|
+ def __init__(self, exc_class):
|
|
self.closed = False
|
|
self.closed = False
|
|
|
|
+ self._exc_class = exc_class
|
|
|
|
|
|
def read(self, size=-1):
|
|
def read(self, size=-1):
|
|
- raise IOError
|
|
|
|
|
|
+ raise self._exc_class()
|
|
|
|
|
|
def close(self):
|
|
def close(self):
|
|
self.closed = True
|
|
self.closed = True
|
|
|
|
|
|
- f = TestFile()
|
|
|
|
- list(send_file(self._req, f, 'text/plain'))
|
|
|
|
- self.assertEquals(HTTP_NOT_FOUND, self._status)
|
|
|
|
|
|
+ f = TestFile(IOError)
|
|
|
|
+ list(send_file(self._req, f, 'some/thing'))
|
|
|
|
+ self.assertEquals(HTTP_ERROR, self._status)
|
|
|
|
+ self.assertTrue(f.closed)
|
|
|
|
+ self.assertFalse(self._req.cached)
|
|
|
|
+
|
|
|
|
+ # non-IOErrors are reraised
|
|
|
|
+ f = TestFile(AttributeError)
|
|
|
|
+ self.assertRaises(AttributeError, list,
|
|
|
|
+ send_file(self._req, f, 'some/thing'))
|
|
self.assertTrue(f.closed)
|
|
self.assertTrue(f.closed)
|
|
|
|
+ self.assertFalse(self._req.cached)
|
|
|
|
+
|
|
|
|
+ def test_get_text_file(self):
|
|
|
|
+ backend = _test_backend([], named_files={'description': 'foo'})
|
|
|
|
+ mat = re.search('.*', 'description')
|
|
|
|
+ output = ''.join(get_text_file(self._req, backend, mat))
|
|
|
|
+ self.assertEquals('foo', output)
|
|
|
|
+ self.assertEquals(HTTP_OK, self._status)
|
|
|
|
+ self.assertContentTypeEquals('text/plain')
|
|
|
|
+ self.assertFalse(self._req.cached)
|
|
|
|
+
|
|
|
|
+ def test_get_loose_object(self):
|
|
|
|
+ blob = make_object(Blob, data='foo')
|
|
|
|
+ backend = _test_backend([blob])
|
|
|
|
+ mat = re.search('^(..)(.{38})$', blob.id)
|
|
|
|
+ output = ''.join(get_loose_object(self._req, backend, mat))
|
|
|
|
+ self.assertEquals(blob.as_legacy_object(), output)
|
|
|
|
+ self.assertEquals(HTTP_OK, self._status)
|
|
|
|
+ self.assertContentTypeEquals('application/x-git-loose-object')
|
|
|
|
+ self.assertTrue(self._req.cached)
|
|
|
|
+
|
|
|
|
+ def test_get_loose_object_missing(self):
|
|
|
|
+ mat = re.search('^(..)(.{38})$', '1' * 40)
|
|
|
|
+ list(get_loose_object(self._req, _test_backend([]), mat))
|
|
|
|
+ self.assertEquals(HTTP_NOT_FOUND, self._status)
|
|
|
|
+
|
|
|
|
+ def test_get_loose_object_error(self):
|
|
|
|
+ blob = make_object(Blob, data='foo')
|
|
|
|
+ backend = _test_backend([blob])
|
|
|
|
+ mat = re.search('^(..)(.{38})$', blob.id)
|
|
|
|
+
|
|
|
|
+ def as_legacy_object_error():
|
|
|
|
+ raise IOError
|
|
|
|
+
|
|
|
|
+ blob.as_legacy_object = as_legacy_object_error
|
|
|
|
+ list(get_loose_object(self._req, backend, mat))
|
|
|
|
+ self.assertEquals(HTTP_ERROR, self._status)
|
|
|
|
+
|
|
|
|
+ def test_get_pack_file(self):
|
|
|
|
+ pack_name = 'objects/pack/pack-%s.pack' % ('1' * 40)
|
|
|
|
+ backend = _test_backend([], named_files={pack_name: 'pack contents'})
|
|
|
|
+ mat = re.search('.*', pack_name)
|
|
|
|
+ output = ''.join(get_pack_file(self._req, backend, mat))
|
|
|
|
+ self.assertEquals('pack contents', output)
|
|
|
|
+ self.assertEquals(HTTP_OK, self._status)
|
|
|
|
+ self.assertContentTypeEquals('application/x-git-packed-objects')
|
|
|
|
+ self.assertTrue(self._req.cached)
|
|
|
|
+
|
|
|
|
+ def test_get_idx_file(self):
|
|
|
|
+ idx_name = 'objects/pack/pack-%s.idx' % ('1' * 40)
|
|
|
|
+ backend = _test_backend([], named_files={idx_name: 'idx contents'})
|
|
|
|
+ mat = re.search('.*', idx_name)
|
|
|
|
+ output = ''.join(get_idx_file(self._req, backend, mat))
|
|
|
|
+ self.assertEquals('idx contents', output)
|
|
|
|
+ self.assertEquals(HTTP_OK, self._status)
|
|
|
|
+ self.assertContentTypeEquals('application/x-git-packed-objects-toc')
|
|
|
|
+ self.assertTrue(self._req.cached)
|
|
|
|
|
|
def test_get_info_refs(self):
|
|
def test_get_info_refs(self):
|
|
self._environ['QUERY_STRING'] = ''
|
|
self._environ['QUERY_STRING'] = ''
|
|
|
|
|
|
- class TestTag(object):
|
|
|
|
- def __init__(self, sha, obj_class, obj_sha):
|
|
|
|
- self.sha = lambda: sha
|
|
|
|
- self.object = (obj_class, obj_sha)
|
|
|
|
-
|
|
|
|
- class TestBlob(object):
|
|
|
|
- def __init__(self, sha):
|
|
|
|
- self.sha = lambda: sha
|
|
|
|
-
|
|
|
|
- blob1 = TestBlob('111')
|
|
|
|
- blob2 = TestBlob('222')
|
|
|
|
- blob3 = TestBlob('333')
|
|
|
|
-
|
|
|
|
- tag1 = TestTag('aaa', Blob, '222')
|
|
|
|
-
|
|
|
|
- class TestRepo(object):
|
|
|
|
-
|
|
|
|
- def __init__(self, objects, peeled):
|
|
|
|
- self._objects = dict((o.sha(), o) for o in objects)
|
|
|
|
- self._peeled = peeled
|
|
|
|
-
|
|
|
|
- def get_peeled(self, sha):
|
|
|
|
- return self._peeled[sha]
|
|
|
|
-
|
|
|
|
- def __getitem__(self, sha):
|
|
|
|
- return self._objects[sha]
|
|
|
|
-
|
|
|
|
- def get_refs(self):
|
|
|
|
- return {
|
|
|
|
- 'HEAD': '000',
|
|
|
|
- 'refs/heads/master': blob1.sha(),
|
|
|
|
- 'refs/tags/tag-tag': tag1.sha(),
|
|
|
|
- 'refs/tags/blob-tag': blob3.sha(),
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- class TestBackend(object):
|
|
|
|
- def __init__(self):
|
|
|
|
- objects = [blob1, blob2, blob3, tag1]
|
|
|
|
- self.repo = TestRepo(objects, {
|
|
|
|
- 'HEAD': '000',
|
|
|
|
- 'refs/heads/master': blob1.sha(),
|
|
|
|
- 'refs/tags/tag-tag': blob2.sha(),
|
|
|
|
- 'refs/tags/blob-tag': blob3.sha(),
|
|
|
|
- })
|
|
|
|
-
|
|
|
|
- def open_repository(self, path):
|
|
|
|
- assert path == '/'
|
|
|
|
- return self.repo
|
|
|
|
-
|
|
|
|
- def get_refs(self):
|
|
|
|
- return {
|
|
|
|
- 'HEAD': '000',
|
|
|
|
- 'refs/heads/master': blob1.sha(),
|
|
|
|
- 'refs/tags/tag-tag': tag1.sha(),
|
|
|
|
- 'refs/tags/blob-tag': blob3.sha(),
|
|
|
|
- }
|
|
|
|
|
|
+ blob1 = make_object(Blob, data='1')
|
|
|
|
+ blob2 = make_object(Blob, data='2')
|
|
|
|
+ blob3 = make_object(Blob, data='3')
|
|
|
|
+
|
|
|
|
+ tag1 = make_object(Tag, name='tag-tag',
|
|
|
|
+ tagger='Test <test@example.com>',
|
|
|
|
+ tag_time=12345,
|
|
|
|
+ tag_timezone=0,
|
|
|
|
+ message='message',
|
|
|
|
+ object=(Blob, blob2.id))
|
|
|
|
+
|
|
|
|
+ objects = [blob1, blob2, blob3, tag1]
|
|
|
|
+ refs = {
|
|
|
|
+ 'HEAD': '000',
|
|
|
|
+ 'refs/heads/master': blob1.id,
|
|
|
|
+ 'refs/tags/tag-tag': tag1.id,
|
|
|
|
+ 'refs/tags/blob-tag': blob3.id,
|
|
|
|
+ }
|
|
|
|
+ backend = _test_backend(objects, refs=refs)
|
|
|
|
|
|
mat = re.search('.*', '//info/refs')
|
|
mat = re.search('.*', '//info/refs')
|
|
- self.assertEquals(['111\trefs/heads/master\n',
|
|
|
|
- '333\trefs/tags/blob-tag\n',
|
|
|
|
- 'aaa\trefs/tags/tag-tag\n',
|
|
|
|
- '222\trefs/tags/tag-tag^{}\n'],
|
|
|
|
- list(get_info_refs(self._req, TestBackend(), mat)))
|
|
|
|
|
|
+ self.assertEquals(['%s\trefs/heads/master\n' % blob1.id,
|
|
|
|
+ '%s\trefs/tags/blob-tag\n' % blob3.id,
|
|
|
|
+ '%s\trefs/tags/tag-tag\n' % tag1.id,
|
|
|
|
+ '%s\trefs/tags/tag-tag^{}\n' % blob2.id],
|
|
|
|
+ list(get_info_refs(self._req, backend, mat)))
|
|
|
|
+ self.assertEquals(HTTP_OK, self._status)
|
|
|
|
+ self.assertContentTypeEquals('text/plain')
|
|
|
|
+ self.assertFalse(self._req.cached)
|
|
|
|
+
|
|
|
|
+ def test_get_info_packs(self):
|
|
|
|
+ class TestPack(object):
|
|
|
|
+ def __init__(self, sha):
|
|
|
|
+ self._sha = sha
|
|
|
|
+
|
|
|
|
+ def name(self):
|
|
|
|
+ return self._sha
|
|
|
|
+
|
|
|
|
+ packs = [TestPack(str(i) * 40) for i in xrange(1, 4)]
|
|
|
|
+
|
|
|
|
+ class TestObjectStore(MemoryObjectStore):
|
|
|
|
+ # property must be overridden, can't be assigned
|
|
|
|
+ @property
|
|
|
|
+ def packs(self):
|
|
|
|
+ return packs
|
|
|
|
+
|
|
|
|
+ store = TestObjectStore()
|
|
|
|
+ repo = BaseRepo(store, None)
|
|
|
|
+ backend = DictBackend({'/': repo})
|
|
|
|
+ mat = re.search('.*', '//info/packs')
|
|
|
|
+ output = ''.join(get_info_packs(self._req, backend, mat))
|
|
|
|
+ expected = 'P pack-%s.pack\n' * 3
|
|
|
|
+ expected %= ('1' * 40, '2' * 40, '3' * 40)
|
|
|
|
+ self.assertEquals(expected, output)
|
|
|
|
+ self.assertEquals(HTTP_OK, self._status)
|
|
|
|
+ self.assertContentTypeEquals('text/plain')
|
|
|
|
+ self.assertFalse(self._req.cached)
|
|
|
|
|
|
|
|
|
|
class SmartHandlersTestCase(WebTestCase):
|
|
class SmartHandlersTestCase(WebTestCase):
|
|
@@ -191,43 +302,55 @@ class SmartHandlersTestCase(WebTestCase):
|
|
mat = re.search('.*', '/git-evil-handler')
|
|
mat = re.search('.*', '/git-evil-handler')
|
|
list(handle_service_request(self._req, 'backend', mat))
|
|
list(handle_service_request(self._req, 'backend', mat))
|
|
self.assertEquals(HTTP_FORBIDDEN, self._status)
|
|
self.assertEquals(HTTP_FORBIDDEN, self._status)
|
|
|
|
+ self.assertFalse(self._req.cached)
|
|
|
|
|
|
- def test_handle_service_request(self):
|
|
|
|
|
|
+ def _run_handle_service_request(self, content_length=None):
|
|
self._environ['wsgi.input'] = StringIO('foo')
|
|
self._environ['wsgi.input'] = StringIO('foo')
|
|
|
|
+ if content_length is not None:
|
|
|
|
+ self._environ['CONTENT_LENGTH'] = content_length
|
|
mat = re.search('.*', '/git-upload-pack')
|
|
mat = re.search('.*', '/git-upload-pack')
|
|
- output = ''.join(handle_service_request(self._req, 'backend', mat))
|
|
|
|
- self.assertEqual('handled input: foo', output)
|
|
|
|
- response_type = 'application/x-git-upload-pack-response'
|
|
|
|
- self.assertTrue(('Content-Type', response_type) in self._headers)
|
|
|
|
|
|
+ handler_output = ''.join(
|
|
|
|
+ handle_service_request(self._req, 'backend', mat))
|
|
|
|
+ write_output = self._output.getvalue()
|
|
|
|
+ # Ensure all output was written via the write callback.
|
|
|
|
+ self.assertEqual('', handler_output)
|
|
|
|
+ self.assertEqual('handled input: foo', write_output)
|
|
|
|
+ self.assertContentTypeEquals('application/x-git-upload-pack-response')
|
|
self.assertFalse(self._handler.advertise_refs)
|
|
self.assertFalse(self._handler.advertise_refs)
|
|
self.assertTrue(self._handler.stateless_rpc)
|
|
self.assertTrue(self._handler.stateless_rpc)
|
|
|
|
+ self.assertFalse(self._req.cached)
|
|
|
|
+
|
|
|
|
+ def test_handle_service_request(self):
|
|
|
|
+ self._run_handle_service_request()
|
|
|
|
|
|
def test_handle_service_request_with_length(self):
|
|
def test_handle_service_request_with_length(self):
|
|
- self._environ['wsgi.input'] = StringIO('foobar')
|
|
|
|
- self._environ['CONTENT_LENGTH'] = 3
|
|
|
|
- mat = re.search('.*', '/git-upload-pack')
|
|
|
|
- output = ''.join(handle_service_request(self._req, 'backend', mat))
|
|
|
|
- self.assertEqual('handled input: foo', output)
|
|
|
|
- response_type = 'application/x-git-upload-pack-response'
|
|
|
|
- self.assertTrue(('Content-Type', response_type) in self._headers)
|
|
|
|
|
|
+ self._run_handle_service_request(content_length='3')
|
|
|
|
+
|
|
|
|
+ def test_handle_service_request_empty_length(self):
|
|
|
|
+ self._run_handle_service_request(content_length='')
|
|
|
|
|
|
def test_get_info_refs_unknown(self):
|
|
def test_get_info_refs_unknown(self):
|
|
self._environ['QUERY_STRING'] = 'service=git-evil-handler'
|
|
self._environ['QUERY_STRING'] = 'service=git-evil-handler'
|
|
list(get_info_refs(self._req, 'backend', None))
|
|
list(get_info_refs(self._req, 'backend', None))
|
|
self.assertEquals(HTTP_FORBIDDEN, self._status)
|
|
self.assertEquals(HTTP_FORBIDDEN, self._status)
|
|
|
|
+ self.assertFalse(self._req.cached)
|
|
|
|
|
|
def test_get_info_refs(self):
|
|
def test_get_info_refs(self):
|
|
self._environ['wsgi.input'] = StringIO('foo')
|
|
self._environ['wsgi.input'] = StringIO('foo')
|
|
self._environ['QUERY_STRING'] = 'service=git-upload-pack'
|
|
self._environ['QUERY_STRING'] = 'service=git-upload-pack'
|
|
|
|
|
|
mat = re.search('.*', '/git-upload-pack')
|
|
mat = re.search('.*', '/git-upload-pack')
|
|
- output = ''.join(get_info_refs(self._req, 'backend', mat))
|
|
|
|
|
|
+ handler_output = ''.join(get_info_refs(self._req, 'backend', mat))
|
|
|
|
+ write_output = self._output.getvalue()
|
|
self.assertEquals(('001e# service=git-upload-pack\n'
|
|
self.assertEquals(('001e# service=git-upload-pack\n'
|
|
'0000'
|
|
'0000'
|
|
# input is ignored by the handler
|
|
# input is ignored by the handler
|
|
- 'handled input: '), output)
|
|
|
|
|
|
+ 'handled input: '), write_output)
|
|
|
|
+ # Ensure all output was written via the write callback.
|
|
|
|
+ self.assertEquals('', handler_output)
|
|
self.assertTrue(self._handler.advertise_refs)
|
|
self.assertTrue(self._handler.advertise_refs)
|
|
self.assertTrue(self._handler.stateless_rpc)
|
|
self.assertTrue(self._handler.stateless_rpc)
|
|
|
|
+ self.assertFalse(self._req.cached)
|
|
|
|
|
|
|
|
|
|
class LengthLimitedFileTestCase(TestCase):
|
|
class LengthLimitedFileTestCase(TestCase):
|
|
@@ -248,6 +371,10 @@ class LengthLimitedFileTestCase(TestCase):
|
|
|
|
|
|
|
|
|
|
class HTTPGitRequestTestCase(WebTestCase):
|
|
class HTTPGitRequestTestCase(WebTestCase):
|
|
|
|
+
|
|
|
|
+ # This class tests the contents of the actual cache headers
|
|
|
|
+ _req_class = HTTPGitRequest
|
|
|
|
+
|
|
def test_not_found(self):
|
|
def test_not_found(self):
|
|
self._req.cache_forever() # cache headers should be discarded
|
|
self._req.cache_forever() # cache headers should be discarded
|
|
message = 'Something not found'
|
|
message = 'Something not found'
|