Beispiel #1
0
class SqlaImagesTests(unittest.TestCase):
    def setUp(self):
        self.transient_root = tempfile.mkdtemp()
        self.persistent_root = tempfile.mkdtemp()
        self.transient_url = '/transient/'
        self.persistent_url = '/media/'
        self.file_manager = FileManager(self.transient_root,
                                        self.persistent_root,
                                        self.transient_url,
                                        self.persistent_url)
        Session = filesessionmaker(orm.sessionmaker(), self.file_manager)
        engine = create_engine('sqlite://')
        Base.metadata.create_all(engine)
        self.db = Session(bind=engine)

    def tearDown(self):
        shutil.rmtree(self.transient_root)
        shutil.rmtree(self.persistent_root)

    def test_create(self):
        obj = ObjWithImage()
        obj.image = f = self.file_manager.new_transient('.gif')
        _create_image(f.path)

        def return_image(image, filter):
            # asserting that filter was called
            self.assertEqual(filter, ImageFilter.BLUR)
            return image

        with mock.patch('PIL.Image.Image.filter',
                        side_effect=return_image,
                        autospec=True):
            self.db.add(obj)
            self.db.commit()

            self.assertIsInstance(obj.image, PersistentFile)
            self.assertIsInstance(obj.thumb, PersistentFile)
            self.assertIsInstance(obj.thumb_filter, PersistentFile)
            self.assertIsInstance(obj.thumb_optimize, PersistentFile)

            img = Image.open(obj.image.path)
            self.assertEqual(img.size, (200, 200))
            self.assertEqual(obj.image.width, img.width)
            self.assertEqual(obj.image.height, img.height)

            thumb = Image.open(obj.thumb.path)
            self.assertEqual(thumb.size, (100, 100))
            self.assertEqual(obj.thumb.height, thumb.height)
            self.assertEqual(obj.thumb.width, thumb.width)
            pixels = thumb.load()
            self.assertEqual(pixels[50, 50], (186, 186, 186))

            self.assertLessEqual(
                os.stat(obj.thumb_optimize.path).st_size,
                os.stat(obj.thumb.path).st_size)

    def test_no_original_image(self):
        obj = ObjWithImage()
        obj.image = f = self.file_manager.new_transient()
        _create_image(f.path, format='PNG')

        warn = []
        with mock.patch('os.path.isfile', return_value=False):
            with mock.patch(
                    'logging.Logger.warn',
                    side_effect=lambda m, *args: warn.append(m % args)):
                self.db.add(obj)
                self.db.commit()

        self.assertEqual(len(warn), 3)
        self.assertEqual(warn[0], warn[1])
        self.assertEqual(warn[1], warn[2])
        self.assertIn("Original file is absent", warn[0])
        self.assertIn(obj.image.path, warn[0])

    def test_no_ext(self):
        # test for extraction image extension from image instead of file path
        obj = ObjWithImage()
        obj.image = f = self.file_manager.new_transient()
        _create_image(f.path, format='PNG')
        self.db.add(obj)
        self.db.commit()
        self.assertIsInstance(obj.image, PersistentFile)
        self.assertTrue(obj.image.path.endswith('.png'))

    def test_no_size(self):
        obj = ObjWithImage()
        obj.icon = f = self.file_manager.new_transient('.gif')
        _create_image(f.path, 200, 300)
        self.db.add(obj)
        self.db.commit()

        self.assertIsNone(obj.image)
        self.assertIsNone(obj.thumb)
        self.assertIsInstance(obj.icon, PersistentFile)

        img = Image.open(obj.icon.path)
        self.assertEqual(img.size, (200, 300))

    def test_no_img(self):
        obj = ObjWithImage()
        self.db.add(obj)
        self.db.commit()

        self.assertEqual(obj.image, None)
        self.assertEqual(obj.thumb, None)
        self.assertEqual(obj.icon, None)

    def test_update(self):
        obj = ObjWithImage()
        self.db.add(obj)
        self.db.commit()

        obj.image = f = self.file_manager.new_transient('.gif')
        _create_image(f.path)
        self.db.commit()

        self.assertIsInstance(obj.image, PersistentFile)
        self.assertIsInstance(obj.thumb, PersistentFile)

        img = Image.open(obj.image.path)
        self.assertEqual(img.size, (200, 200))

        thumb = Image.open(obj.thumb.path)
        self.assertEqual(thumb.size, (100, 100))

    def test_invalid(self):
        obj = ObjWithImage()
        obj.image = f = self.file_manager.new_transient('.gif')
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.add(obj)
        with self.assertRaises(IOError):
            self.db.commit()

    def test_fill_without_size(self):
        with self.assertRaises(AssertionError):

            class Test(Base):

                id = Column(Integer, primary_key=True)
                image_name = Column(VARBINARY(250))
                image = ImageProperty(image_name,
                                      name_template='image/{random}')

                thumb_name = Column(VARBINARY(250))
                thumb = ImageProperty(thumb_name,
                                      name_template='thumb/{random}',
                                      fill_from='image')
Beispiel #2
0
class SqlaFilesTests(unittest.TestCase):

    Model = ObjWithFile

    def setUp(self):

        self.transient_root = tempfile.mkdtemp()
        self.persistent_root = tempfile.mkdtemp()
        self.transient_url = '/transient/'
        self.persistent_url = '/media/'

        self.file_manager = FileManager(self.transient_root,
                                        self.persistent_root,
                                        self.transient_url,
                                        self.persistent_url)

        self.metadata_transient_root = tempfile.mkdtemp()
        self.metadata_persistent_root = tempfile.mkdtemp()
        self.metadata_transient_url = '/metadata/transient/'
        self.metadata_persistent_url = '/metadata/media/'

        self.metadata_file_manager = FileManager(self.metadata_transient_root,
                                                 self.metadata_persistent_root,
                                                 self.metadata_transient_url,
                                                 self.metadata_persistent_url)

        self.model_transient_root = tempfile.mkdtemp()
        self.model_persistent_root = tempfile.mkdtemp()
        self.model_transient_url = '/model/transient/'
        self.model_persistent_url = '/model/media/'

        self.model_file_manager = FileManager(self.model_transient_root,
                                              self.model_persistent_root,
                                              self.model_transient_url,
                                              self.model_persistent_url)

        Session = filesessionmaker(orm.sessionmaker(),
                                   self.file_manager,
                                   file_managers={
                                       MetadataLevelObj.metadata:
                                       self.metadata_file_manager,
                                       ModelLevelObj: self.model_file_manager,
                                   })

        engine = create_engine('sqlite://')
        Base.metadata.create_all(engine)
        CustomBase.metadata.create_all(engine)
        self.db = Session(bind=engine)

    def tearDown(self):
        shutil.rmtree(self.transient_root)
        shutil.rmtree(self.persistent_root)
        shutil.rmtree(self.metadata_transient_root)
        shutil.rmtree(self.metadata_persistent_root)
        shutil.rmtree(self.model_transient_root)
        shutil.rmtree(self.model_persistent_root)

    def test_session(self):
        self.assertTrue(hasattr(self.db, 'file_manager'))
        self.assertIsInstance(self.db.file_manager, FileManager)

    def test_find_file_manager(self):
        self.assertTrue(hasattr(self.db, 'find_file_manager'))

        self.assertEqual(self.db.find_file_manager(self.Model.file),
                         self.file_manager)

        self.assertEqual(self.db.find_file_manager(ModelLevelObj.file),
                         self.model_file_manager)

        self.assertEqual(self.db.find_file_manager(MetadataLevelObj.file),
                         self.metadata_file_manager)

    def test_create(self):
        obj = self.Model()

        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.assertIsInstance(obj.file, TransientFile)
        self.assertIsNotNone(obj.file_name)
        self.db.add(obj)
        self.db.commit()
        self.assertIsInstance(obj.file, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertTrue(os.path.isfile(obj.file.path))
        self.assertEqual(open(obj.file.path).read(), 'test')

    def test_create_metadata_obj(self):
        metadata_obj = MetadataLevelObj()
        metadata_filemanager = self.db.find_file_manager(metadata_obj)
        metadata_file = metadata_filemanager.new_transient()
        with open(metadata_file.path, 'wb') as fp:
            fp.write(b'test')
        metadata_obj.file = metadata_file

        self.assertTrue(
            metadata_obj.file.path.startswith(self.metadata_transient_root))

        self.db.add(metadata_obj)
        self.db.commit()

        self.assertTrue(
            metadata_obj.file.path.startswith(self.metadata_persistent_root))

    def test_create_model_obj(self):
        model_obj = ModelLevelObj()
        model_filemanager = self.db.find_file_manager(model_obj)
        model_file = model_filemanager.new_transient()
        with open(model_file.path, 'wb') as fp:
            fp.write(b'test')
        model_obj.file = model_file

        self.assertTrue(
            model_obj.file.path.startswith(self.model_transient_root))

        self.db.add(model_obj)
        self.db.commit()

        self.assertTrue(
            model_obj.file.path.startswith(self.model_persistent_root))

    def test_update_none2file(self):
        obj = self.Model()
        self.db.add(obj)
        self.db.commit()

        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.assertIsInstance(obj.file, TransientFile)
        self.assertIsNotNone(obj.file_name)
        self.db.commit()

        # cleanup self.Model.file._states.items() to get the result
        # from scratch, not from cache
        obj_id = obj.id
        obj = None
        gc.collect()
        obj = self.db.query(self.Model).get(obj_id)

        self.assertIsInstance(obj.file, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertTrue(os.path.isfile(obj.file.path))
        self.assertEqual(open(obj.file.path).read(), 'test')
        self.assertEqual(obj.file.size, 4)
        self.assertEqual(obj.file_size, 4)

    def test_update_file2none(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.add(obj)
        self.db.commit()

        pf = obj.file

        obj.file = None
        self.assertIsNone(obj.file_name)
        self.assertTrue(os.path.exists(pf.path))
        self.assertEqual(obj.file_size, 4)
        self.db.commit()

        self.assertFalse(os.path.exists(pf.path))
        self.assertEqual(obj.file_size, None)

    def test_update_file2file(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test1')
        self.db.add(obj)
        self.db.commit()
        pf1 = obj.file

        self.assertEqual(obj.file_size, 5)

        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test22')
        self.assertIsInstance(obj.file, TransientFile)
        self.assertIsNotNone(obj.file_name)
        self.db.commit()

        self.assertIsInstance(obj.file, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertFalse(os.path.exists(pf1.path))
        self.assertTrue(os.path.isfile(obj.file.path))
        self.assertEqual(open(obj.file.path).read(), 'test22')
        self.assertEqual(obj.file_size, 6)

    def test_update_file2self(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test1')
        self.db.add(obj)
        self.db.commit()
        pf1 = obj.file

        obj.file = self.file_manager.get_persistent(obj.file.name)
        self.db.commit()

        self.assertIsInstance(obj.file, PersistentFile)
        self.assertTrue(os.path.exists(obj.file.path))
        self.assertEqual(pf1.path, obj.file.path)

        # XXX for test coverage
        #     have no idea what extra check can be performed
        obj.file = obj.file
        self.assertTrue(os.path.exists(obj.file.path))
        self.assertEqual(pf1.path, obj.file.path)

    @unittest.expectedFailure
    def test_update_file2file_not_random(self):
        obj = self.Model()

        obj.file_by_id = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test1')
        self.db.add(obj)
        self.db.commit()
        self.assertEqual(obj.file_by_id_name,
                         self.Model.file_by_id.name_template.format(item=obj))
        pf1 = obj.file_by_id

        obj.file_by_id = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test2')
        self.assertIsInstance(obj.file_by_id, TransientFile)
        self.assertIsNotNone(obj.file_by_id_name)
        self.db.commit()

        self.assertIsInstance(obj.file_by_id, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertFalse(os.path.exists(pf1.path))
        self.assertTrue(os.path.isfile(obj.file_by_id.path))
        self.assertEqual(open(obj.file_by_id.path).read(), 'test2')

    @unittest.expectedFailure
    def test_update_random_collision(self):
        obj = self.Model()
        self.db.add(obj)
        self.db.commit()

        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')

        persistent = self.file_manager.get_persistent(obj.file_name)
        dirname = os.path.dirname(persistent.path)
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        with open(persistent.path, 'wb') as fp:
            fp.write(b'taken')

        self.assertIsInstance(obj.file, TransientFile)
        self.assertIsNotNone(obj.file_name)
        self.db.commit()
        self.assertIsInstance(obj.file, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertTrue(os.path.isfile(obj.file.path))
        self.assertEqual(open(obj.file.path).read(), 'test')
        self.assertNotEqual(persistent.path, obj.file.path)
        self.assertEqual(open(persistent.path).read(), 'taken')

    def test_update_none2persistent(self):
        f = self.file_manager.get_persistent('persistent.txt')
        with open(f.path, 'wb') as fp:
            fp.write(b'test1')

        obj = self.Model()
        obj.file = f
        self.db.add(obj)
        self.db.commit()

        self.assertIsInstance(obj.file, PersistentFile)
        self.assertTrue(os.path.exists(obj.file.path))
        self.assertEqual(obj.file.name, 'persistent.txt')

    def test_delete(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.add(obj)
        self.db.commit()
        pf = obj.file
        self.db.delete(obj)
        self.db.commit()
        self.assertFalse(os.path.exists(pf.path))

    def test_set_invalid(self):
        obj = self.Model()
        self.assertRaises(ValueError, lambda: setattr(obj, 'file', 'test'))

    def test_cached_lost(self):
        obj = self.Model()
        self.db.add(obj)
        self.db.commit()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.commit()

        os.unlink(obj.file.path)

        self.assertEqual(obj.file.size, 4)
        self.assertEqual(obj.file_size, 4)

        del obj.file.size
        self.assertEqual(obj.file.size, None)

    def test_file2none_lost(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.add(obj)
        self.db.commit()

        os.unlink(obj.file.path)
        obj.file = None
        self.db.commit()

        self.assertEqual(obj.file_size, None)

    def test_file_manager_for_field(self):
        def make():
            file_manager = FileManager(self.transient_root,
                                       self.persistent_root,
                                       self.transient_url, self.persistent_url)
            filesessionmaker(orm.sessionmaker(),
                             self.file_manager,
                             file_managers={
                                 ObjWithFile.file: file_manager,
                             })

        self.assertRaises(NotImplementedError, make)

    def test_detached_object(self):
        obj = self.Model()
        self.db.add(obj)
        self.db.commit()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.commit()
        # cleanup self.Model.file._states.items() to get the result
        # from scratch, not from cache
        obj_id = obj.id
        obj = None
        gc.collect()
        obj = self.db.query(self.Model).get(obj_id)

        with mock.patch.object(iktomi.db.sqla.files,
                               'object_session',
                               return_value=None):
            with self.assertRaises(RuntimeError) as exc:
                obj.file
            self.assertEqual('Object is detached', str(exc.exception))

    def test_absent_file_manager(self):
        obj = self.Model()
        del self.db.file_manager
        self.db.add(obj)
        self.db.commit()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.commit()
        # cleanup self.Model.file._states.items() to get the result
        # from scratch, not from cache
        obj_id = obj.id
        obj = None
        gc.collect()
        obj = self.db.query(self.Model).get(obj_id)

        with self.assertRaises(RuntimeError) as exc:
            obj.file
        self.assertEqual("Session doesn't support file management",
                         str(exc.exception))
Beispiel #3
0
class FormFilesTests(unittest.TestCase):

    def setUp(self):
        self.transient_root = tempfile.mkdtemp()
        self.persistent_root = tempfile.mkdtemp()
        self.transient_url = '/transient/'
        self.persistent_url = '/media/'
        self.file_manager = FileManager(self.transient_root,
                                        self.persistent_root,
                                        self.transient_url,
                                        self.persistent_url)
        self.env = AppEnvironment.create(file_manager=self.file_manager)

    def tearDown(self):
        shutil.rmtree(self.transient_root)
        shutil.rmtree(self.persistent_root)

    def _create_persistent(self):
        f = self.file_manager.get_persistent('test.txt')
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        return f

    def _create_transient(self, content, original_name='test.txt'):
        f = self.file_manager.new_transient(ext=os.path.splitext(original_name)[1])
        with open(f.path, 'wb') as fp:
            fp.write(content)
        return f

    def _create_fs(self, mimetype, content, filename='uploaded.txt', name="file"):
        # http://stackoverflow.com/questions/12032807/how-to-create-cgi-fieldstorage-for-testing-purposes
        content = content.encode('utf-8')
        m = {u'content-disposition': u'form-data; name="{}"; filename="{}"'.format(name, filename),
             u'content-length': len(content),
             u'content-type': mimetype}
        environ = {'REQUEST_METHOD': 'POST'}
        fp = BytesIO(content)
        return cgi.FieldStorage(fp=fp, headers=m, environ=environ)

    def test_none2empty(self):
        form = FormWithFile(self.env)
        form.accept(MultiDict({'file.file': None,
                               'file.original_name': '',
                               'file.transient_name': '',
                               'file.mode': 'empty',}))
        data = form.python_data
        self.assertEqual(data['file'], None)

    def test_transient2empty(self):
        transient = self._create_transient(b'transient1')
        form = FormWithFile(self.env)
        form.accept(MultiDict({'file.file': None,
                               'file.original_name': 'test.txt',
                               'file.transient_name': transient.name,
                               'file.mode': 'transient',}))
        data = form.python_data
        self.assertIsInstance(data['file'], TransientFile)
        self.assertEqual(data['file'].name, transient.name)

    def test_transient_lost(self):
        form = FormWithFile(self.env)
        form.accept(MultiDict({'file.file': None,
                               'file.original_name': 'test.txt',
                               'file.transient_name': 'lost.txt',
                               'file.mode': 'transient',}))
        self.assertIn('file', form.errors)

    def test_transient_invalid(self):
        illegal = ['more/../../../lost.txt',
                   '../pass',
                   '/etc/pass',
                   '..',
                   '~',
                   ]
        for filename in illegal:
            form = FormWithFile(self.env)
            form.accept(MultiDict({'file.file': None,
                                   'file.original_name': 'test.txt',
                                   'file.transient_name': filename,
                                   'file.mode': 'transient',}))
            self.assertIn('file.transient_name', form.errors,
                          'file name is not filtered: %s' % filename)

    def test_file2empty(self):
        form = FormWithFile(self.env)
        fs = self._create_fs('text', 'file-content', 'filename.ttt')
        form.accept(MultiDict({'file.file': fs,
                               'file.mode': 'existing',}))
        data = form.python_data
        self.assertIsInstance(data['file'], TransientFile)
        self.assertEqual(os.path.splitext(data['file'].name)[1],
                         '.ttt')
        with open(data['file'].path) as fp:
            self.assertEqual(fp.read(), 'file-content')

    def test_none2persistent(self):
        persistent = self._create_persistent()
        form = FormWithFile(self.env, initial={'file': persistent})
        form.accept(MultiDict({'file.file': None,
                               'file.original_name': '',
                               'file.transient_name': '',
                               'file.mode': 'existing',}))
        data = form.python_data
        self.assertEqual(data['file'], persistent)

    def test_empty2persistent(self):
        persistent = self._create_persistent()
        form = FormWithFile(self.env, initial={'file': persistent})
        form.accept(MultiDict({'file.file': None,
                               'file.original_name': '',
                               'file.transient_name': '',
                               'file.mode': 'empty',}))
        data = form.python_data
        self.assertEqual(data['file'], None)

    def test_transient2persistent(self):
        persistent = self._create_persistent()
        transient = self._create_transient(b'transient1')
        form = FormWithFile(self.env, initial={'file': persistent})
        form.accept(MultiDict({'file.file': None,
                               'file.original_name': 'test.txt',
                               'file.transient_name': transient.name,
                               'file.mode': 'transient',}))
        data = form.python_data
        self.assertIsInstance(data['file'], TransientFile)
        self.assertEqual(data['file'].name, transient.name)

    def test_file2persistent(self):
        persistent = self._create_persistent()
        form = FormWithFile(self.env, initial={'file': persistent})
        fs = self._create_fs('text', 'file-content', 'filename.ttt')
        form.accept(MultiDict({'file.file': fs,
                               'file.mode': 'existing',}))
        data = form.python_data
        self.assertIsInstance(data['file'], TransientFile)
        self.assertEqual(os.path.splitext(data['file'].name)[1],
                         '.ttt')
        with open(data['file'].path) as fp:
            self.assertEqual(fp.read(), 'file-content')
Beispiel #4
0
class FormFilesTests(unittest.TestCase):
    def setUp(self):
        self.transient_root = tempfile.mkdtemp()
        self.persistent_root = tempfile.mkdtemp()
        self.transient_url = "/transient/"
        self.persistent_url = "/media/"
        self.file_manager = FileManager(
            self.transient_root, self.persistent_root, self.transient_url, self.persistent_url
        )
        self.env = AppEnvironment.create(file_manager=self.file_manager)

    def tearDown(self):
        shutil.rmtree(self.transient_root)
        shutil.rmtree(self.persistent_root)

    def _create_persistent(self):
        f = self.file_manager.get_persistent("test.txt")
        with open(f.path, "wb") as fp:
            fp.write(b"test")
        return f

    def _create_transient(self, content, original_name="test.txt"):
        f = self.file_manager.new_transient(ext=os.path.splitext(original_name)[1])
        with open(f.path, "wb") as fp:
            fp.write(content)
        return f

    def _create_fs(self, mimetype, content, filename="uploaded.txt", name="file"):
        # http://stackoverflow.com/questions/12032807/how-to-create-cgi-fieldstorage-for-testing-purposes
        content = content.encode("utf-8")
        m = {
            u"content-disposition": u'form-data; name="{}"; filename="{}"'.format(name, filename),
            u"content-length": len(content),
            u"content-type": mimetype,
        }
        environ = {"REQUEST_METHOD": "POST"}
        fp = BytesIO(content)
        return cgi.FieldStorage(fp=fp, headers=m, environ=environ)

    def test_none2empty(self):
        form = FormWithFile(self.env)
        form.accept(
            MultiDict({"file.file": None, "file.original_name": "", "file.transient_name": "", "file.mode": "empty"})
        )
        data = form.python_data
        self.assertEqual(data["file"], None)

    def test_transient2empty(self):
        transient = self._create_transient(b"transient1")
        form = FormWithFile(self.env)
        form.accept(
            MultiDict(
                {
                    "file.file": None,
                    "file.original_name": "test.txt",
                    "file.transient_name": transient.name,
                    "file.mode": "transient",
                }
            )
        )
        data = form.python_data
        self.assertIsInstance(data["file"], TransientFile)
        self.assertEqual(data["file"].name, transient.name)

    def test_transient_lost(self):
        form = FormWithFile(self.env)
        form.accept(
            MultiDict(
                {
                    "file.file": None,
                    "file.original_name": "test.txt",
                    "file.transient_name": "lost.txt",
                    "file.mode": "transient",
                }
            )
        )
        self.assertIn("file", form.errors)

    def test_transient_invalid(self):
        illegal = ["more/../../../lost.txt", "../pass", "/etc/pass", "..", "~"]
        for filename in illegal:
            form = FormWithFile(self.env)
            form.accept(
                MultiDict(
                    {
                        "file.file": None,
                        "file.original_name": "test.txt",
                        "file.transient_name": filename,
                        "file.mode": "transient",
                    }
                )
            )
            self.assertIn("file.transient_name", form.errors, "file name is not filtered: %s" % filename)

    def test_file2empty(self):
        form = FormWithFile(self.env)
        fs = self._create_fs("text", "file-content", "filename.ttt")
        form.accept(MultiDict({"file.file": fs, "file.mode": "existing"}))
        data = form.python_data
        self.assertIsInstance(data["file"], TransientFile)
        self.assertEqual(os.path.splitext(data["file"].name)[1], ".ttt")
        with open(data["file"].path) as fp:
            self.assertEqual(fp.read(), "file-content")

    def test_none2persistent(self):
        persistent = self._create_persistent()
        form = FormWithFile(self.env, initial={"file": persistent})
        form.accept(
            MultiDict({"file.file": None, "file.original_name": "", "file.transient_name": "", "file.mode": "existing"})
        )
        data = form.python_data
        self.assertEqual(data["file"], persistent)

    def test_empty2persistent(self):
        persistent = self._create_persistent()
        form = FormWithFile(self.env, initial={"file": persistent})
        form.accept(
            MultiDict({"file.file": None, "file.original_name": "", "file.transient_name": "", "file.mode": "empty"})
        )
        data = form.python_data
        self.assertEqual(data["file"], None)

    def test_transient2persistent(self):
        persistent = self._create_persistent()
        transient = self._create_transient(b"transient1")
        form = FormWithFile(self.env, initial={"file": persistent})
        form.accept(
            MultiDict(
                {
                    "file.file": None,
                    "file.original_name": "test.txt",
                    "file.transient_name": transient.name,
                    "file.mode": "transient",
                }
            )
        )
        data = form.python_data
        self.assertIsInstance(data["file"], TransientFile)
        self.assertEqual(data["file"].name, transient.name)

    def test_file2persistent(self):
        persistent = self._create_persistent()
        form = FormWithFile(self.env, initial={"file": persistent})
        fs = self._create_fs("text", "file-content", "filename.ttt")
        form.accept(MultiDict({"file.file": fs, "file.mode": "existing"}))
        data = form.python_data
        self.assertIsInstance(data["file"], TransientFile)
        self.assertEqual(os.path.splitext(data["file"].name)[1], ".ttt")
        with open(data["file"].path) as fp:
            self.assertEqual(fp.read(), "file-content")
Beispiel #5
0
class SqlaImagesTests(unittest.TestCase):

    def setUp(self):
        self.transient_root = tempfile.mkdtemp()
        self.persistent_root = tempfile.mkdtemp()
        self.transient_url = '/transient/'
        self.persistent_url = '/media/'
        self.file_manager = FileManager(self.transient_root,
                                        self.persistent_root,
                                        self.transient_url,
                                        self.persistent_url)
        Session = filesessionmaker(orm.sessionmaker(), self.file_manager)
        engine = create_engine('sqlite://')
        Base.metadata.create_all(engine)
        self.db = Session(bind=engine)

    def tearDown(self):
        shutil.rmtree(self.transient_root)
        shutil.rmtree(self.persistent_root)

    def test_create(self):
        obj = ObjWithImage()
        obj.image = f = self.file_manager.new_transient('.gif')
        _create_image(f.path)

        def return_image(image, filter):
            # asserting that filter was called
            self.assertEqual(filter, ImageFilter.BLUR)
            return image

        with mock.patch('PIL.Image.Image.filter',
                        side_effect=return_image,
                        autospec=True):
            self.db.add(obj)
            self.db.commit()

            self.assertIsInstance(obj.image, PersistentFile)
            self.assertIsInstance(obj.thumb, PersistentFile)
            self.assertIsInstance(obj.thumb_filter, PersistentFile)
            self.assertIsInstance(obj.thumb_optimize, PersistentFile)

            img = Image.open(obj.image.path)
            self.assertEqual(img.size, (200, 200))
            self.assertEqual(obj.image.width, img.width)
            self.assertEqual(obj.image.height, img.height)

            thumb = Image.open(obj.thumb.path)
            self.assertEqual(thumb.size, (100, 100))
            self.assertEqual(obj.thumb.height, thumb.height)
            self.assertEqual(obj.thumb.width, thumb.width)
            pixels = thumb.load()
            self.assertEqual(pixels[50, 50], (186, 186, 186))

            self.assertLessEqual(os.stat(obj.thumb_optimize.path).st_size,
                                 os.stat(obj.thumb.path).st_size)

    def test_no_original_image(self):
        obj = ObjWithImage()
        obj.image = f = self.file_manager.new_transient()
        _create_image(f.path, format='PNG')

        warn = []
        with mock.patch('os.path.isfile', return_value=False):
            with mock.patch('logging.Logger.warn',
                            side_effect=lambda m, *args: warn.append(m % args)):
                self.db.add(obj)
                self.db.commit()

        self.assertEqual(len(warn), 3)
        self.assertEqual(warn[0], warn[1])
        self.assertEqual(warn[1], warn[2])
        self.assertIn("Original file is absent", warn[0])
        self.assertIn(obj.image.path, warn[0])

    def test_no_ext(self):
        # test for extraction image extension from image instead of file path
        obj = ObjWithImage()
        obj.image = f = self.file_manager.new_transient()
        _create_image(f.path, format='PNG')
        self.db.add(obj)
        self.db.commit()
        self.assertIsInstance(obj.image, PersistentFile)
        self.assertTrue(obj.image.path.endswith('.png'))

    def test_no_size(self):
        obj = ObjWithImage()
        obj.icon = f = self.file_manager.new_transient('.gif')
        _create_image(f.path, 200, 300)
        self.db.add(obj)
        self.db.commit()

        self.assertIsNone(obj.image)
        self.assertIsNone(obj.thumb)
        self.assertIsInstance(obj.icon, PersistentFile)

        img = Image.open(obj.icon.path)
        self.assertEqual(img.size, (200, 300))

    def test_no_img(self):
        obj = ObjWithImage()
        self.db.add(obj)
        self.db.commit()

        self.assertEqual(obj.image, None)
        self.assertEqual(obj.thumb, None)
        self.assertEqual(obj.icon, None)

    def test_update(self):
        obj = ObjWithImage()
        self.db.add(obj)
        self.db.commit()

        obj.image = f = self.file_manager.new_transient('.gif')
        _create_image(f.path)
        self.db.commit()

        self.assertIsInstance(obj.image, PersistentFile)
        self.assertIsInstance(obj.thumb, PersistentFile)

        img = Image.open(obj.image.path)
        self.assertEqual(img.size, (200, 200))

        thumb = Image.open(obj.thumb.path)
        self.assertEqual(thumb.size, (100, 100))

    def test_invalid(self):
        obj = ObjWithImage()
        obj.image = f = self.file_manager.new_transient('.gif')
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.add(obj)
        with self.assertRaises(IOError):
            self.db.commit()

    def test_fill_without_size(self):
        with self.assertRaises(AssertionError):
            class Test(Base):

                id = Column(Integer, primary_key=True)
                image_name = Column(VARBINARY(250))
                image = ImageProperty(image_name,
                                      name_template='image/{random}')

                thumb_name = Column(VARBINARY(250))
                thumb = ImageProperty(thumb_name,
                                      name_template='thumb/{random}',
                                      fill_from='image')
Beispiel #6
0
class SqlaFilesTests(unittest.TestCase):

    Model = ObjWithFile

    def setUp(self):

        self.transient_root = tempfile.mkdtemp()
        self.persistent_root = tempfile.mkdtemp()
        self.transient_url = '/transient/'
        self.persistent_url = '/media/'

        self.file_manager = FileManager(self.transient_root,
                                        self.persistent_root,
                                        self.transient_url,
                                        self.persistent_url)

        self.metadata_transient_root = tempfile.mkdtemp()
        self.metadata_persistent_root = tempfile.mkdtemp()
        self.metadata_transient_url = '/metadata/transient/'
        self.metadata_persistent_url = '/metadata/media/'

        self.metadata_file_manager = FileManager(self.metadata_transient_root,
                                                 self.metadata_persistent_root,
                                                 self.metadata_transient_url,
                                                 self.metadata_persistent_url)

        self.model_transient_root = tempfile.mkdtemp()
        self.model_persistent_root = tempfile.mkdtemp()
        self.model_transient_url = '/model/transient/'
        self.model_persistent_url = '/model/media/'

        self.model_file_manager = FileManager(self.model_transient_root,
                                              self.model_persistent_root,
                                              self.model_transient_url,
                                              self.model_persistent_url)


        Session = filesessionmaker(orm.sessionmaker(), self.file_manager,
            file_managers={
                MetadataLevelObj.metadata: self.metadata_file_manager,
                ModelLevelObj: self.model_file_manager,
            })

        engine = create_engine('sqlite://')
        Base.metadata.create_all(engine)
        CustomBase.metadata.create_all(engine)
        self.db = Session(bind=engine)

    def tearDown(self):
        shutil.rmtree(self.transient_root)
        shutil.rmtree(self.persistent_root)
        shutil.rmtree(self.metadata_transient_root)
        shutil.rmtree(self.metadata_persistent_root)
        shutil.rmtree(self.model_transient_root)
        shutil.rmtree(self.model_persistent_root)

    def test_session(self):
        self.assertTrue(hasattr(self.db, 'file_manager'))
        self.assertIsInstance(self.db.file_manager, FileManager)

    def test_find_file_manager(self):
        self.assertTrue(hasattr(self.db, 'find_file_manager'))

        self.assertEqual(self.db.find_file_manager(self.Model.file),
                         self.file_manager)

        self.assertEqual(self.db.find_file_manager(ModelLevelObj.file),
                         self.model_file_manager)

        self.assertEqual(self.db.find_file_manager(MetadataLevelObj.file),
                         self.metadata_file_manager)

    def test_create(self):
        obj = self.Model()

        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.assertIsInstance(obj.file, TransientFile)
        self.assertIsNotNone(obj.file_name)
        self.db.add(obj)
        self.db.commit()
        self.assertIsInstance(obj.file, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertTrue(os.path.isfile(obj.file.path))
        self.assertEqual(open(obj.file.path).read(), 'test')

    def test_create_metadata_obj(self):
        metadata_obj = MetadataLevelObj()
        metadata_filemanager = self.db.find_file_manager(metadata_obj)
        metadata_file = metadata_filemanager.new_transient()
        with open(metadata_file.path, 'wb') as fp:
            fp.write(b'test')
        metadata_obj.file = metadata_file

        self.assertTrue(metadata_obj.file.path.startswith(self.metadata_transient_root))

        self.db.add(metadata_obj)
        self.db.commit()

        self.assertTrue(metadata_obj.file.path.startswith(self.metadata_persistent_root))

    def test_create_model_obj(self):
        model_obj = ModelLevelObj()
        model_filemanager = self.db.find_file_manager(model_obj)
        model_file = model_filemanager.new_transient()
        with open(model_file.path, 'wb') as fp:
            fp.write(b'test')
        model_obj.file = model_file

        self.assertTrue(model_obj.file.path.startswith(self.model_transient_root))

        self.db.add(model_obj)
        self.db.commit()

        self.assertTrue(model_obj.file.path.startswith(self.model_persistent_root))

    def test_update_none2file(self):
        obj = self.Model()
        self.db.add(obj)
        self.db.commit()

        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.assertIsInstance(obj.file, TransientFile)
        self.assertIsNotNone(obj.file_name)
        self.db.commit()

        # cleanup self.Model.file._states.items() to get the result
        # from scratch, not from cache
        obj_id = obj.id
        obj = None
        gc.collect()
        obj = self.db.query(self.Model).get(obj_id)

        self.assertIsInstance(obj.file, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertTrue(os.path.isfile(obj.file.path))
        self.assertEqual(open(obj.file.path).read(), 'test')
        self.assertEqual(obj.file.size, 4)
        self.assertEqual(obj.file_size, 4)

    def test_update_file2none(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.add(obj)
        self.db.commit()

        pf = obj.file

        obj.file = None
        self.assertIsNone(obj.file_name)
        self.assertTrue(os.path.exists(pf.path))
        self.assertEqual(obj.file_size, 4)
        self.db.commit()

        self.assertFalse(os.path.exists(pf.path))
        self.assertEqual(obj.file_size, None)

    def test_update_file2file(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test1')
        self.db.add(obj)
        self.db.commit()
        pf1 = obj.file

        self.assertEqual(obj.file_size, 5)

        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test22')
        self.assertIsInstance(obj.file, TransientFile)
        self.assertIsNotNone(obj.file_name)
        self.db.commit()

        self.assertIsInstance(obj.file, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertFalse(os.path.exists(pf1.path))
        self.assertTrue(os.path.isfile(obj.file.path))
        self.assertEqual(open(obj.file.path).read(), 'test22')
        self.assertEqual(obj.file_size, 6)

    def test_update_file2self(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test1')
        self.db.add(obj)
        self.db.commit()
        pf1 = obj.file

        obj.file = self.file_manager.get_persistent(obj.file.name)
        self.db.commit()

        self.assertIsInstance(obj.file, PersistentFile)
        self.assertTrue(os.path.exists(obj.file.path))
        self.assertEqual(pf1.path, obj.file.path)

        # XXX for test coverage
        #     have no idea what extra check can be performed
        obj.file = obj.file
        self.assertTrue(os.path.exists(obj.file.path))
        self.assertEqual(pf1.path, obj.file.path)

    @unittest.expectedFailure
    def test_update_file2file_not_random(self):
        obj = self.Model()

        obj.file_by_id = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test1')
        self.db.add(obj)
        self.db.commit()
        self.assertEqual(obj.file_by_id_name,
                         self.Model.file_by_id.name_template.format(item=obj))
        pf1 = obj.file_by_id

        obj.file_by_id = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test2')
        self.assertIsInstance(obj.file_by_id, TransientFile)
        self.assertIsNotNone(obj.file_by_id_name)
        self.db.commit()

        self.assertIsInstance(obj.file_by_id, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertFalse(os.path.exists(pf1.path))
        self.assertTrue(os.path.isfile(obj.file_by_id.path))
        self.assertEqual(open(obj.file_by_id.path).read(), 'test2')

    @unittest.expectedFailure
    def test_update_random_collision(self):
        obj = self.Model()
        self.db.add(obj)
        self.db.commit()

        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')

        persistent = self.file_manager.get_persistent(obj.file_name)
        dirname = os.path.dirname(persistent.path)
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        with open(persistent.path, 'wb') as fp:
            fp.write(b'taken')

        self.assertIsInstance(obj.file, TransientFile)
        self.assertIsNotNone(obj.file_name)
        self.db.commit()
        self.assertIsInstance(obj.file, PersistentFile)
        self.assertFalse(os.path.exists(f.path))
        self.assertTrue(os.path.isfile(obj.file.path))
        self.assertEqual(open(obj.file.path).read(), 'test')
        self.assertNotEqual(persistent.path, obj.file.path)
        self.assertEqual(open(persistent.path).read(), 'taken')

    def test_update_none2persistent(self):
        f = self.file_manager.get_persistent('persistent.txt')
        with open(f.path, 'wb') as fp:
            fp.write(b'test1')

        obj = self.Model()
        obj.file = f
        self.db.add(obj)
        self.db.commit()

        self.assertIsInstance(obj.file, PersistentFile)
        self.assertTrue(os.path.exists(obj.file.path))
        self.assertEqual(obj.file.name, 'persistent.txt')

    def test_delete(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.add(obj)
        self.db.commit()
        pf = obj.file
        self.db.delete(obj)
        self.db.commit()
        self.assertFalse(os.path.exists(pf.path))

    def test_set_invalid(self):
        obj = self.Model()
        self.assertRaises(ValueError, lambda: setattr(obj, 'file', 'test'))

    def test_cached_lost(self):
        obj = self.Model()
        self.db.add(obj)
        self.db.commit()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.commit()

        os.unlink(obj.file.path)

        self.assertEqual(obj.file.size, 4)
        self.assertEqual(obj.file_size, 4)

        del obj.file.size
        self.assertEqual(obj.file.size, None)

    def test_file2none_lost(self):
        obj = self.Model()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.add(obj)
        self.db.commit()

        os.unlink(obj.file.path)
        obj.file = None
        self.db.commit()

        self.assertEqual(obj.file_size, None)

    def test_file_manager_for_field(self):
        def make():
            file_manager = FileManager(self.transient_root,
                                       self.persistent_root,
                                       self.transient_url,
                                       self.persistent_url)
            filesessionmaker(orm.sessionmaker(), self.file_manager,
                file_managers={
                    ObjWithFile.file: file_manager,
                })

        self.assertRaises(NotImplementedError, make)

    def test_detached_object(self):
        obj = self.Model()
        self.db.add(obj)
        self.db.commit()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.commit()
        # cleanup self.Model.file._states.items() to get the result
        # from scratch, not from cache
        obj_id = obj.id
        obj = None
        gc.collect()
        obj = self.db.query(self.Model).get(obj_id)

        with mock.patch.object(iktomi.db.sqla.files,
                               'object_session', return_value=None):
            with self.assertRaises(RuntimeError) as exc:
                obj.file
            self.assertEqual('Object is detached', str(exc.exception))

    def test_absent_file_manager(self):
        obj = self.Model()
        del self.db.file_manager
        self.db.add(obj)
        self.db.commit()
        obj.file = f = self.file_manager.new_transient()
        with open(f.path, 'wb') as fp:
            fp.write(b'test')
        self.db.commit()
        # cleanup self.Model.file._states.items() to get the result
        # from scratch, not from cache
        obj_id = obj.id
        obj = None
        gc.collect()
        obj = self.db.query(self.Model).get(obj_id)

        with self.assertRaises(RuntimeError) as exc:
            obj.file
        self.assertEqual("Session doesn't support file management", str(exc.exception))