1
2
3
4
5
6
7
8
9
10
11
12
13
14 import os
15 import tempfile
16 import unittest
17 import zipfile
18 from datetime import datetime
19 from StringIO import StringIO
20
21 from trac.attachment import Attachment, AttachmentModule
22 from trac.core import Component, implements, TracError
23 from trac.perm import IPermissionPolicy, PermissionCache
24 from trac.resource import IResourceManager, Resource, resource_exists
25 from trac.test import EnvironmentStub, MockRequest
26 from trac.util.datefmt import utc, to_utimestamp
27 from trac.web.api import HTTPBadRequest, RequestDone
28
29
30 hashes = {
31 '42': '92cfceb39d57d914ed8b14d0e37643de0797ae56',
32 'Foo.Mp3': '95797b6eb253337ff2c54e0881e2b747ec394f51',
33 'SomePage': 'd7e80bae461ca8568e794792f5520b603f540e06',
34 'Teh bar.jpg': 'ed9102c4aa099e92baf1073f824d21c5e4be5944',
35 'Teh foo.txt': 'ab97ba98d98fcf72b92e33a66b07077010171f70',
36 'bar.7z': '6c9600ad4d59ac864e6f0d2030c1fc76b4b406cb',
37 'bar.jpg': 'ae0faa593abf2b6f8871f6f32fe5b28d1c6572be',
38 'foo.$$$': 'eefc6aa745dbe129e8067a4a57637883edd83a8a',
39 'foo.2.txt': 'a8fcfcc2ef4e400ee09ae53c1aabd7f5a5fda0c7',
40 'foo.txt': '9206ac42b532ef8e983470c251f4e1a365fd636c',
41 u'bar.aäc': '70d0e3b813fdc756602d82748719a3ceb85cbf29',
42 u'ÜberSicht': 'a16c6837f6d3d2cc3addd68976db1c55deb694c8',
43 }
44
45
54
55
57
59 self.env = EnvironmentStub()
60 self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-')
61 self.attachments_dir = os.path.join(self.env.path, 'files',
62 'attachments')
63 self.env.enable_component(TicketOnlyViewsTicket)
64 self.env.config.set('trac', 'permission_policies',
65 'TicketOnlyViewsTicket, LegacyAttachmentPolicy')
66 self.env.config.set('attachment', 'max_size', 512)
67
68 self.perm = PermissionCache(self.env)
69 self.datetime = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
70 with self.env.db_transaction as db:
71 db("INSERT INTO wiki (name,version) VALUES ('WikiStart',1)")
72 db("INSERT INTO wiki (name,version) VALUES ('SomePage',1)")
73 db("INSERT INTO ticket (id) VALUES (42)")
74 db("INSERT INTO ticket (id) VALUES (43)")
75 db("INSERT INTO attachment VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
76 ('ticket', '43', 'foo.txt', 8, to_utimestamp(self.datetime),
77 'A comment', 'joe', '::1'))
78
81
91
93 attachment = Attachment(self.env, 'ticket', 43, 'foo.txt')
94 self.assertEqual('foo.txt', attachment.filename)
95 self.assertEqual('A comment', attachment.description)
96 self.assertEqual(8, attachment.size)
97 self.assertEqual(self.datetime, attachment.date)
98 self.assertEqual('joe', attachment.author)
99 self.assertEqual('::1', attachment.ipnr)
100 self.assertEqual("<Attachment u'foo.txt'>", repr(attachment))
101
103 resource = Resource('ticket', 43).child('attachment', 'foo.txt')
104 attachment = Attachment(self.env, resource)
105 self.assertEqual('foo.txt', attachment.filename)
106 self.assertEqual('A comment', attachment.description)
107 self.assertEqual(8, attachment.size)
108 self.assertEqual(self.datetime, attachment.date)
109 self.assertEqual('joe', attachment.author)
110 self.assertEqual('::1', attachment.ipnr)
111 self.assertEqual("<Attachment u'foo.txt'>", repr(attachment))
112
114 attachment = Attachment(self.env, 'ticket', 42)
115 attachment.filename = 'foo.txt'
116 self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
117 hashes['42'][0:3], hashes['42'],
118 hashes['foo.txt'] + '.txt'),
119 attachment.path)
120 attachment = Attachment(self.env, 'wiki', 'SomePage')
121 attachment.filename = 'bar.jpg'
122 self.assertEqual(os.path.join(self.attachments_dir, 'wiki',
123 hashes['SomePage'][0:3],
124 hashes['SomePage'],
125 hashes['bar.jpg'] + '.jpg'),
126 attachment.path)
127
129 attachment = Attachment(self.env, 'ticket', 42)
130 attachment.filename = 'Foo.Mp3'
131 self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
132 hashes['42'][0:3], hashes['42'],
133 hashes['Foo.Mp3'] + '.Mp3'),
134 attachment.path)
135 attachment = Attachment(self.env, 'wiki', 'SomePage')
136 attachment.filename = 'bar.7z'
137 self.assertEqual(os.path.join(self.attachments_dir, 'wiki',
138 hashes['SomePage'][0:3],
139 hashes['SomePage'],
140 hashes['bar.7z'] + '.7z'),
141 attachment.path)
142 attachment = Attachment(self.env, 'ticket', 42)
143 attachment.filename = 'foo.$$$'
144 self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
145 hashes['42'][0:3], hashes['42'],
146 hashes['foo.$$$']),
147 attachment.path)
148 attachment = Attachment(self.env, 'wiki', 'SomePage')
149 attachment.filename = u'bar.aäc'
150 self.assertEqual(os.path.join(self.attachments_dir, 'wiki',
151 hashes['SomePage'][0:3],
152 hashes['SomePage'],
153 hashes[u'bar.aäc']),
154 attachment.path)
155
157 attachment = Attachment(self.env, 'ticket', 42)
158 attachment.filename = 'Teh foo.txt'
159 self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
160 hashes['42'][0:3], hashes['42'],
161 hashes['Teh foo.txt'] + '.txt'),
162 attachment.path)
163 attachment = Attachment(self.env, 'wiki', u'ÜberSicht')
164 attachment.filename = 'Teh bar.jpg'
165 self.assertEqual(os.path.join(self.attachments_dir, 'wiki',
166 hashes[u'ÜberSicht'][0:3],
167 hashes[u'ÜberSicht'],
168 hashes['Teh bar.jpg'] + '.jpg'),
169 attachment.path)
170
176
178 attachment = Attachment(self.env, 'ticket', 42)
179 attachment.insert('foo.txt', StringIO(''), 0, 1)
180 attachment = Attachment(self.env, 'ticket', 42)
181 attachment.insert('bar.jpg', StringIO(''), 0, 2)
182
183 attachments = Attachment.select(self.env, 'ticket', 42)
184 self.assertEqual('foo.txt', attachments.next().filename)
185 self.assertEqual('bar.jpg', attachments.next().filename)
186 self.assertRaises(StopIteration, attachments.next)
187
189 attachment = Attachment(self.env, 'ticket', 42)
190 attachment.insert('foo.txt', StringIO(''), 0)
191 self.assertEqual('foo.txt', attachment.filename)
192 attachment = Attachment(self.env, 'ticket', 42)
193 attachment.insert('foo.txt', StringIO(''), 0)
194 self.assertEqual('foo.2.txt', attachment.filename)
195 self.assertEqual(os.path.join(self.attachments_dir, 'ticket',
196 hashes['42'][0:3], hashes['42'],
197 hashes['foo.2.txt'] + '.txt'),
198 attachment.path)
199 self.assertTrue(os.path.exists(attachment.path))
200
205
207 attachment1 = Attachment(self.env, 'wiki', 'SomePage')
208 attachment1.insert('foo.txt', StringIO(''), 0)
209 attachment2 = Attachment(self.env, 'wiki', 'SomePage')
210 attachment2.insert('bar.jpg', StringIO(''), 0)
211
212 attachments = Attachment.select(self.env, 'wiki', 'SomePage')
213 self.assertEqual(2, len(list(attachments)))
214
215 attachment1.delete()
216 attachment2.delete()
217
218 self.assertFalse(os.path.exists(attachment1.path))
219 self.assertFalse(os.path.exists(attachment2.path))
220
221 attachments = Attachment.select(self.env, 'wiki', 'SomePage')
222 self.assertEqual(0, len(list(attachments)))
223
234
236 attachment1 = Attachment(self.env, 'wiki', 'SomePage')
237 attachment1.insert('foo.txt', StringIO(''), 0)
238 path1 = attachment1.path
239 attachment2 = Attachment(self.env, 'wiki', 'SomePage')
240 attachment2.insert('bar.jpg', StringIO(''), 0)
241
242 attachments = Attachment.select(self.env, 'wiki', 'SomePage')
243 self.assertEqual(2, len(list(attachments)))
244 attachments = Attachment.select(self.env, 'ticket', 123)
245 self.assertEqual(0, len(list(attachments)))
246 self.assertTrue(os.path.exists(path1) and os.path.exists(attachment2.path))
247
248 attachment1.reparent('ticket', 123)
249 self.assertEqual('ticket', attachment1.parent_realm)
250 self.assertEqual('ticket', attachment1.resource.parent.realm)
251 self.assertEqual('123', attachment1.parent_id)
252 self.assertEqual('123', attachment1.resource.parent.id)
253
254 attachments = Attachment.select(self.env, 'wiki', 'SomePage')
255 self.assertEqual(1, len(list(attachments)))
256 attachments = Attachment.select(self.env, 'ticket', 123)
257 self.assertEqual(1, len(list(attachments)))
258 self.assertFalse(os.path.exists(path1) and os.path.exists(attachment1.path))
259 self.assertTrue(os.path.exists(attachment2.path))
260
262 """Ensure that legacy action tests are done on parent. As
263 `ATTACHMENT_VIEW` maps to `TICKET_VIEW`, the `TICKET_VIEW` is tested
264 against the ticket's resource."""
265 attachment = Attachment(self.env, 'ticket', 42)
266 self.assertTrue('ATTACHMENT_VIEW' in self.perm(attachment.resource))
267
272
273
275
277 class GenericResourceManager(Component):
278
279 implements(IResourceManager)
280
281 def get_resource_realms(self):
282 yield 'parent_realm'
283
284 def get_resource_url(self, resource, href, **kwargs):
285 pass
286
287 def get_resource_description(self, resource, format='default',
288 context=None, **kwargs):
289 pass
290
291 def resource_exists(self, resource):
292 return resource.id == 'parent_id'
293
294 self.env = EnvironmentStub(enable=(GenericResourceManager,))
295 self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-')
296
299
301
302 path_info = '/attachment/parent_realm/parent_id/attachment_id'
303 attachment = Attachment(self.env, 'parent_realm', 'parent_id')
304 attachment.insert('attachment_id', StringIO(''), 0, 1)
305 req = MockRequest(self.env, method='POST', action=None,
306 path_info=path_info)
307 module = AttachmentModule(self.env)
308
309 self.assertTrue(module.match_request(req))
310 self.assertRaises(HTTPBadRequest, module.process_request, req)
311
313 """TracError is raised when a POST request is submitted
314 without an attachment.
315 """
316 path_info = '/attachment/parent_realm/parent_id'
317 req = MockRequest(self.env, path_info=path_info, method='POST',
318 args={'action': 'new'})
319 module = AttachmentModule(self.env)
320
321 self.assertTrue(module.match_request(req))
322 self.assertRaises(TracError, module.process_request, req)
323
334
341
343 att = Attachment(self.env, 'parent_realm', 'parent_id')
344 att.description = 'Blah blah'
345 att.insert('foo.txt', StringIO('foo'), 3,
346 datetime(2016, 9, 23, 12, 34, 56, tzinfo=utc))
347 att = Attachment(self.env, 'parent_realm', 'parent_id')
348 att.insert('bar.jpg', StringIO('bar'), 3,
349 datetime(2016, 12, 14, 23, 56, 30, tzinfo=utc))
350 module = AttachmentModule(self.env)
351 req = MockRequest(self.env, args={'format': 'zip'},
352 path_info='/attachment/parent_realm/parent_id/')
353
354 self.assertTrue(module.match_request(req))
355 self.assertRaises(RequestDone, module.process_request, req)
356 z = zipfile.ZipFile(req.response_sent, 'r')
357 self.assertEqual(['bar.jpg', 'foo.txt'],
358 sorted(i.filename for i in z.infolist()))
359
360 zinfo = z.getinfo('foo.txt')
361 self.assertEqual('foo', z.read('foo.txt'))
362 self.assertEqual(3, zinfo.file_size)
363 self.assertEqual((2016, 9, 23, 12, 34, 56), zinfo.date_time)
364 self.assertEqual('Blah blah', zinfo.comment)
365
366 zinfo = z.getinfo('bar.jpg')
367 self.assertEqual('bar', z.read('bar.jpg'))
368 self.assertEqual(3, zinfo.file_size)
369 self.assertEqual((2016, 12, 14, 23, 56, 30), zinfo.date_time)
370 self.assertEqual('', zinfo.comment)
371
372
378
379 if __name__ == '__main__':
380 unittest.main(defaultTest='test_suite')
381