Caroline Petitjean

91 papers A 1B 7Misc 3Journal 38Unranked 36
YearRankTypeTitle / Venue / Authors
2026 J jnl
J. Math. Imaging Vis.
Diane Chan Sock Line, Noémie Debroux, Carole Le Guyader, Caroline Petitjean
2025 J jnl
IEEE J. Biomed. Health Informatics
Jiong Zhang, Huazhu Fu, Caroline Petitjean, Xiaoxiao Li, Julia A. Schnabel
2025 conf
SSVM (2)
Diane Chan Sock Line, Noémie Debroux, Carole Le Guyader, Caroline Petitjean
2024 conf
xAI (2)
Syed Nouman Hasany, Fabrice Mériaudeau, Caroline Petitjean
2024 ed.
MIDL
Ninon Burgos, Caroline Petitjean, Maria Vakalopoulou, Stergios Christodoulidis, Pierrick Coupé, Hervé Delingette, Carole Lartizien, Diana Mateus
2024 J jnl
CoRR
Syed Nouman Hasany, Fabrice Mériaudeau, Caroline Petitjean
2024 conf
MIDL
Ninon Burgos, Caroline Petitjean, Maria Vakalopoulou, Stergios Christodoulidis, Pierrick Coupé, Hervé Delingette, Carole Lartizien, Diana Mateus
2024 conf
MIDL
Théo Sourget, Ahmet Akkoç, Stinna Winther, Christine Lyngbye Galsgaard, Amelia Jiménez-Sánchez, Dovile Juodelyte, Caroline Petitjean, Veronika Cheplygina
2024 J jnl
CoRR
Théo Sourget, Ahmet Akkoç, Stinna Winther, Christine Lyngbye Galsgaard, Amelia Jiménez-Sánchez, Dovile Juodelyte, Caroline Petitjean, Veronika Cheplygina
2023 B conf
Image Processing
Syed Nouman Hasany, Caroline Petitjean, Fabrice Mériaudeau
2023 conf
MIUA
Théo Sourget, Syed Nouman Hasany, Fabrice Mériaudeau, Caroline Petitjean
2023 ed.
MTSAIL/LEAF/AI4Treat/MMMI/REMIA@MICCAI
Jonghye Woo, Alessa Hering, Wilson Silva, Xiang Li, Huazhu Fu, Xiaofeng Liu, Fangxu Xing, Sanjay Purushotham, Tejas Sudharshan Mathai, Pritam Mukherjee, Max De Grauw, Regina Beets-Tan, Valentina Corbetta, Elmar Kotter, Mauricio Reyes, Christian F. Baumgartner, Quanzheng Li, Richard M. Leahy, Bin Dong, Hao Chen, Yuankai Huo, Jinglei Lv, Xinxing Xu, Xiaomeng Li, Dwarikanath Mahapatra, Li Cheng, Caroline Petitjean, Benoît Presles
2023 conf
SSVM
Zoé Lambert, Carole Le Guyader, Caroline Petitjean
2023 conf
CVPR Workshops
Syed Nouman Hasany, Caroline Petitjean, Fabrice Mériaudeau
2023 conf
XAI4CV
Syed Nouman Hasany, Caroline Petitjean, Fabrice Mériaudeau
2022 J jnl
CoRR
Matthias Eisenmann, Annika Reinke, Vivienn Weru, Minu Dietlinde Tizabi, Fabian Isensee, Tim J. Adler, Patrick Godau, Veronika Cheplygina, Michal Kozubek, Sharib Ali, Anubha Gupta, Jan Kybic, J. Alison Noble, Carlos Ortiz-de-Solórzano, Samiksha Pachade, Caroline Petitjean, Daniel Sage, Donglai Wei, Elizabeth Wilden, Deepak Alapatt, Vincent Andrearczyk, Ujjwal Baid, Spyridon Bakas, Niranjan Balu, Sophia Bano, Vivek Singh Bawa, Jorge Bernal, Sebastian Bodenstedt, Alessandro Casella, Jinwook Choi, Olivier Commowick, Marie Daum, Adrien Depeursinge, Reuben Dorent, Jan Egger, Hannah Eichhorn, Sandy Engelhardt, Melanie Ganz, Gabriel Girard, Lasse Hansen, Mattias P. Heinrich, Nicholas Heller, Alessa Hering, Arnaud Huaulmé, Hyunjeong Kim, Bennett A. Landman, Hongwei Bran Li, Jianning Li, Jun Ma, Anne L. Martel, et al.
2022 J jnl
CoRR
Rosana El Jurdi, Caroline Petitjean, Veronika Cheplygina, Paul Honeine, Fahed Abdallah
2022 J jnl
J. Math. Imaging Vis.
Zoé Lambert, Carole Le Guyader, Caroline Petitjean
2022 ed.
REMIA@MICCAI
Xinxing Xu, Xiaomeng Li, Dwarikanath Mahapatra, Li Cheng, Caroline Petitjean, Huazhu Fu
2022 J jnl
J. Imaging
Jing Zhang, Caroline Petitjean, Samia Ainouz
2021 conf
ISBI
Zoé Lambert, Carole Le Guyader, Caroline Petitjean
2021 conf
MIDL
Rosana El Jurdi, Caroline Petitjean, Paul Honeine, Veronika Cheplygina, Fahed Abdallah
2021 J jnl
Comput. Vis. Image Underst.
Rosana El Jurdi, Caroline Petitjean, Paul Honeine, Veronika Cheplygina, Fahed Abdallah
2020 conf
IPTA
Zoé Lambert, Carole Le Guyader, Caroline Petitjean
2020 J jnl
IEEE J. Sel. Top. Signal Process.
Rosana El Jurdi, Caroline Petitjean, Paul Honeine, Fahed Abdallah
2020 J jnl
IEEE Trans. Biomed. Eng.
Dong Nie, Roger Trullo, Jun Lian, Li Wang, Caroline Petitjean, Su Ruan, Qian Wang, Dinggang Shen
2020 conf
MIDL
Jing Zhang, Caroline Petitjean, Pierre Lopez, Samia Ainouz
2020 conf
iMIMIC/MIL3ID/LABELS@MICCAI
Jing Zhang, Caroline Petitjean, Florian Yger, Samia Ainouz
2020 J jnl
CoRR
Rosana El Jurdi, Caroline Petitjean, Paul Honeine, Veronika Cheplygina, Fahed Abdallah
2020 J jnl
Pattern Recognit. Lett.
Ignacio Úbeda, José M. Saavedra, Stéphane Nicolas, Caroline Petitjean, Laurent Heutte
2020 conf
IPTA
Rosana El Jurdi, Thomas Dargent, Caroline Petitjean, Paul Honeine, Fahed Abdallah
2020 conf
ISBI
Jing Zhang, Caroline Petitjean, Samia Ainouz
2020 conf
IPTA
Zoé Lambert, Caroline Petitjean, Bernard Dubray, Su Ruan
2019 J jnl
Expert Syst. Appl.
P. J. Sudharshan, Caroline Petitjean, Fabio A. Spanhol, Luiz Eduardo Soares de Oliveira, Laurent Heutte, Paul Honeine
2019 conf
HIP@ICDAR
Ignacio Úbeda, José M. Saavedra, Stéphane Nicolas, Caroline Petitjean, Laurent Heutte
2019 J jnl
CoRR
Ignacio Úbeda, José M. Saavedra, Stéphane Nicolas, Caroline Petitjean, Laurent Heutte
2019 ed.
SegTHOR@ISBI
Caroline Petitjean, Su Ruan, Zoé Lambert, Bernard Dubray
2019 J jnl
CoRR
Zoé Lambert, Caroline Petitjean, Bernard Dubray, Su Ruan
2018 J jnl
IEEE Trans. Biomed. Eng.
Dong Nie, Roger Trullo, Jun Lian, Li Wang, Caroline Petitjean, Su Ruan, Qian Wang, Dinggang Shen
2017 J jnl
Comput. Biol. Medicine
Violeta Chang, Laurent Heutte, Caroline Petitjean, Steffen Härtel, Nancy Hitschfeld
2017 B conf
SMC
Fabio A. Spanhol, Luiz S. Oliveira, Paulo R. Cavalin, Caroline Petitjean, Laurent Heutte
2017 conf
ICSIPA
Roger Trullo, Caroline Petitjean, Dong Nie, Dinggang Shen, Su Ruan
2017 conf
DLMIA/ML-CDS@MICCAI
Roger Trullo, Caroline Petitjean, Dong Nie, Dinggang Shen, Su Ruan
2017 conf
MICCAI (3)
Dong Nie, Roger Trullo, Jun Lian, Caroline Petitjean, Su Ruan, Qian Wang, Dinggang Shen
2017 J jnl
J. Electronic Imaging
Sovann En, Stéphane Nicolas, Caroline Petitjean, Frédéric Jurie, Laurent Heutte
2017 conf
ISBI
Roger Trullo, Caroline Petitjean, Su Ruan, Bernard Dubray, Dong Nie, Dinggang Shen
2017 J jnl
Comput. Vis. Image Underst.
Fan Wang, Samia Ainouz, Caroline Petitjean, Abdelaziz Bensrhair
2016 J jnl
IEEE Trans. Biomed. Eng.
Fabio A. Spanhol, Luiz S. Oliveira, Caroline Petitjean, Laurent Heutte
2016 J jnl
Pattern Recognit.
Sovann En, Caroline Petitjean, Stéphane Nicolas, Laurent Heutte
2016 B conf
IJCNN
Fabio Alexandre Spanhol, Luiz S. Oliveira, Caroline Petitjean, Laurent Heutte
2016 J jnl
Pattern Recognit. Lett.
Arturo Mendoza Quispe, Caroline Petitjean, Laurent Heutte
2016 J jnl
CoRR
Dong Nie, Roger Trullo, Caroline Petitjean, Su Ruan, Dinggang Shen
2016 J jnl
IET Image Process.
Damien Grosgeorge, Caroline Petitjean, Su Ruan
2016 B conf
ICPR
Sovann En, Caroline Petitjean, Stéphane Nicolas, Laurent Heutte, Frédéric Jurie
2016 B conf
ICIP
Fan Wang, Samia Ainouz, Caroline Petitjean, Abdelaziz Bensrhair
2016 ch.
Handbook of Pattern Recognition and Computer Vision
Laurent Heutte, Caroline Petitjean, Chesner Désir
2016 B conf
ICFHR
Sovann En, Caroline Petitjean, Stéphane Nicolas, Laurent Heutte, Frédéric Jurie
2015 J jnl
Medical Image Anal.
Hongmei Mi, Caroline Petitjean, Pierre Vera, Su Ruan
2015 conf
VISAPP (2)
Sovann En, Frédéric Jurie, Stéphane Nicolas, Caroline Petitjean, Laurent Heutte
2015 J jnl
Medical Image Anal.
Caroline Petitjean, Maria A. Zuluaga, Wenjia Bai, Jean-Nicolas Dacher, Damien Grosgeorge, Jérôme Caudron, Su Ruan, Ismail Ben Ayed, Manuel Jorge Cardoso, Hsiang-Chou Chen, Daniel Jimenez-Carretero, María J. Ledesma-Carbayo, Christos Davatzikos, Jimit Doshi, Güray Erus, Oskar M. O. Maier, Cyrus M. S. Nambakhsh, Yangming Ou, Sébastien Ourselin, Chun-Wei Peng, Nicholas S. Peters, Terry M. Peters, Martin Rajchl, Daniel Rueckert, Andrés Santos, Wenzhe Shi, Ching-Wei Wang, Haiyan Wang, Jing Yuan
2015 J jnl
Artif. Intell. Medicine
Hongmei Mi, Caroline Petitjean, Bernard Dubray, Pierre Vera, Su Ruan
2015 A conf
ICDAR
Sovann En, Caroline Petitjean, Stéphane Nicolas, Laurent Heutte
2015 conf
IPTA
Arturo Mendoza Quispe, Caroline Petitjean
2014 conf
IPTA
Paul Desbordes, Caroline Petitjean, Su Ruan
2014 conf
ISBI
Hongmei Mi, Caroline Petitjean, Bernard Dubray, Pierre Vera, Su Ruan
2014 conf
ISBI
Damien Grosgeorge, Caroline Petitjean, Su Ruan
2014 J jnl
IEEE Trans. Medical Imaging
Hongmei Mi, Caroline Petitjean, Bernard Dubray, Pierre Vera, Su Ruan
2014 J jnl
Artif. Intell. Medicine
Alain Rakotomamonjy, Caroline Petitjean, Mathieu Salaün, Luc Thiberville
2013 J jnl
Comput. Math. Methods Medicine
Damien Grosgeorge, Caroline Petitjean, Bernard Dubray, Su Ruan
2013 J jnl
Comput. Vis. Image Underst.
Damien Grosgeorge, Caroline Petitjean, Jean-Nicolas Dacher, Su Ruan
2013 J jnl
Pattern Recognit.
Chesner Désir, Simon Bernard, Caroline Petitjean, Laurent Heutte
2013 conf
ISBI
Hongmei Mi, Caroline Petitjean, Su Ruan, Pierre Vera, Bernard Dubray
2012 conf
SSPR/SPR
Chesner Désir, Simon Bernard, Caroline Petitjean, Laurent Heutte
2012 conf
MLMI
Chesner Désir, Simon Bernard, Caroline Petitjean, Laurent Heutte
2012 J jnl
Comput. Medical Imaging Graph.
Chesner Désir, Caroline Petitjean, Laurent Heutte, Luc Thiberville, Mathieu Salaün
2012 J jnl
IEEE Trans. Biomed. Eng.
Chesner Désir, Caroline Petitjean, Laurent Heutte, Mathieu Salaün, Luc Thiberville
2012 conf
ISBI
David Hebert, Chesner Désir, Caroline Petitjean, Laurent Heutte, Luc Thiberville
2011 J jnl
Medical Image Anal.
Caroline Petitjean, Jean-Nicolas Dacher
2011 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Damien Grosgeorge, Caroline Petitjean, Jérôme Caudron, Jeannette Fares, Jean-Nicolas Dacher
2010 conf
ICIC (2)
Chesner Désir, Caroline Petitjean, Laurent Heutte, Luc Thiberville
2009 Misc conf
IbPRIA
Caroline Petitjean, Laurent Heutte, Régis Kouadio, Vincent Delcourt
2009 Misc conf
MVA
Caroline Petitjean, Laurent Heutte, Régis Kouadio, Vincent Delcourt
2009 conf
ICIC (1)
Aurélien Saint-Réquier, Benoît Lelandais, Caroline Petitjean, Chesner Désir, Laurent Heutte, Mathieu Salaün, Luc Thiberville
2009 Misc conf
MVA
Caroline Petitjean, Jonathan Benoist, Luc Thiberville, Mathieu Salaün, Laurent Heutte
2008 conf
EUSIPCO
Florent Montreuil, Régis Kouadio, Caroline Petitjean, Laurent Heutte, Vincent Delcourt
2005 J jnl
Medical Image Anal.
Nicolas Rougon, Caroline Petitjean, Françoise J. Prêteux, Philippe Cluzel, Philippe Grenier
2004 B conf
Image Processing
Nicolas F. Rougon, Caroline Petitjean, Françoise J. Prêteux
2003 conf
FIMH
Caroline Petitjean, Nicolas Rougon, Françoise J. Prêteux, Philippe Cluzel, Philippe Grenier
2003 conf
CARS
Caroline Petitjean, Nicolas Rougon, Françoise J. Prêteux, Philippe Cluzel, Philippe Grenier
2003
Caroline Petitjean
2003 conf
ICIP (2)
Nicolas Rougon, Caroline Petitjean, Françoise J. Prêteux
tests/integration/test_hashes.py
← Index tests/integration/test_hashes.py python
"""
Integration tests for the HashExtractor class.
"""
import pytest
import hashlib
from unittest.mock import Mock, patch, MagicMock

pytestmark = [pytest.mark.integration]


class TestHashExtractorInitialization:
    """Tests for HashExtractor initialization."""

    def test_initialization_elf(self, elf_binary_path, mock_logger):
        """Test initialization with ELF file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            assert extractor.filepath == elf_binary_path
            assert extractor.hashes is None
            assert extractor.filetype == "elf"
            assert extractor.macho is None

    def test_initialization_with_macho_object(self, elf_binary_path, mock_logger):
        """Test initialization with pre-parsed macho object."""
        from redb.extractors.hashes import HashExtractor

        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64"]

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)

            assert extractor.macho is mock_macho

    def test_initialization_pe(self, pe_binary_path, mock_logger):
        """Test initialization with PE file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            assert extractor.filepath == pe_binary_path
            assert extractor.filetype == "pebin"
            # PE object should be created
            assert extractor.pe is not None

    def test_elastic_index_suffix(self, elf_binary_path, mock_logger):
        """Test that elastic_index has correct suffix."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            assert "-hashes" in extractor.elastic_index


class TestHashExtractorTag:
    """Tests for tag method."""

    def test_tag_value(self, elf_binary_path, mock_logger):
        """Test that tag returns correct value."""
        from redb.extractors.hashes import HashExtractor
        from redb.extractors.enum import Tag

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            assert extractor.tag() == Tag.HASH.value
            assert extractor.tag() == "hash"


class TestHashExtractorBasicHashes:
    """Tests for basic hash extraction."""

    def test_extract_md5(self, elf_binary_path, mock_logger, known_hashes):
        """Test MD5 hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.md5 == known_hashes['md5']

    def test_extract_sha1(self, elf_binary_path, mock_logger, known_hashes):
        """Test SHA1 hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.sha1 == known_hashes['sha1']

    def test_extract_sha256(self, elf_binary_path, mock_logger, known_hashes):
        """Test SHA256 hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.sha256 == known_hashes['sha256']

    def test_extract_ssdeep(self, elf_binary_path, mock_logger):
        """Test ssdeep hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # ssdeep should be a string
            assert isinstance(result.ssdeep_hash, str)

    def test_extract_tlsh(self, elf_binary_path, mock_logger):
        """Test TLSH hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # TLSH should be a string
            assert isinstance(result.tlsh_hash, str)


class TestHashExtractorPEHashes:
    """Tests for PE-specific hash extraction."""

    def test_extract_imphash(self, pe_binary_path, mock_logger):
        """Test imphash extraction for PE file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Imphash should be set for PE files
            if extractor.pe is not None:
                assert result.imphash is not None or result.imphash is None  # May or may not have imports

    def test_extract_authentihash(self, pe_binary_path, mock_logger):
        """Test authentihash extraction for PE file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Authentihash should be set for PE files
            if extractor.pe is not None:
                assert result.authentihash is not None

    def test_pe_hashes_not_set_for_elf(self, elf_binary_path, mock_logger):
        """Test that PE-specific hashes are not set for ELF files."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # PE-specific hashes should be None for ELF
            assert result.imphash is None
            assert result.authentihash is None
            assert result.richhash is None


class TestHashExtractorRichHeaderHashes:
    """Tests for Rich header hash extraction."""

    def test_compute_richhash(self, pe_binary_path, mock_logger):
        """Test Rich header hash computation."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Rich hash may or may not be present depending on the PE
            if result.richhash is not None:
                assert isinstance(result.richhash, str)
                assert len(result.richhash) == 32  # MD5 hex length

    def test_compute_richpe_hash(self, pe_binary_path, mock_logger):
        """Test RichPE hash computation."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # RichPE hash may or may not be present
            if result.richpe_hash is not None:
                assert isinstance(result.richpe_hash, str)
                assert len(result.richpe_hash) == 32  # MD5 hex length

    def test_compute_richpv_hash(self, pe_binary_path, mock_logger):
        """Test RichPV hash computation."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # RichPV hashes may or may not be present
            if result.richpv_hash is not None:
                assert isinstance(result.richpv_hash, str)
            if result.richpv_hash_sorted is not None:
                assert isinstance(result.richpv_hash_sorted, str)


class TestHashExtractorELFHashes:
    """Tests for ELF-specific hash extraction."""

    def test_extract_elf_import_hash(self, elf_binary_path, mock_logger):
        """Test ELF import hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Import hash may or may not be present
            if result.import_hash is not None:
                assert isinstance(result.import_hash, str)
                assert len(result.import_hash) == 32

    def test_extract_elf_export_hash(self, elf_binary_path, mock_logger):
        """Test ELF export hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Export hash may or may not be present
            if result.export_hash is not None:
                assert isinstance(result.export_hash, str)

    def test_extract_elf_section_hash(self, elf_binary_path, mock_logger):
        """Test ELF section hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Section hash should be present for valid ELF
            if result.section_hash is not None:
                assert isinstance(result.section_hash, str)

    def test_extract_elf_symbol_hash(self, elf_binary_path, mock_logger):
        """Test ELF symbol hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Symbol hash (symhash) may or may not be present
            if result.symbol_hash is not None:
                assert isinstance(result.symbol_hash, str)

    def test_extract_elf_dynamic_hash(self, elf_binary_path, mock_logger):
        """Test ELF dynamic hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Dynamic hash may or may not be present
            if result.dynamic_hash is not None:
                assert isinstance(result.dynamic_hash, str)


class TestHashExtractorPrepareExportData:
    """Tests for prepare_export_data method."""

    def test_prepare_export_elasticsearch(self, elf_binary_path, mock_logger):
        """Test prepare_export_data for Elasticsearch."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ElasticsearchExporter")

            assert result is extractor.hashes

    def test_prepare_export_clickhouse(self, elf_binary_path, mock_logger):
        """Test prepare_export_data for ClickHouse."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ClickHouseExporter")

            # Should return tuple of (data, column_names, column_type_names)
            assert isinstance(result, tuple)
            assert len(result) == 3

            data, column_names, column_type_names = result

            assert isinstance(data, list)
            assert isinstance(column_names, list)
            assert isinstance(column_type_names, list)

            # Check expected columns
            assert 'sha256' in column_names
            assert 'md5' in column_names
            assert 'sha1' in column_names
            assert 'ssdeep_hash' in column_names
            assert 'tlsh_hash' in column_names

    def test_prepare_export_clickhouse_pe(self, pe_binary_path, mock_logger):
        """Test prepare_export_data for ClickHouse with PE file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ClickHouseExporter")

            data, column_names, column_type_names = result

            # PE-specific columns should be present
            assert 'authentihash' in column_names
            assert 'imphash' in column_names
            assert 'richhash' in column_names

    def test_prepare_export_clickhouse_has_macho_columns(self, elf_binary_path, mock_logger):
        """Test prepare_export_data for ClickHouse includes Mach-O hash columns."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ClickHouseExporter")

            data, column_names, column_type_names = result

            # Mach-O hash columns should be present
            assert 'macho_dylib_hash' in column_names
            assert 'macho_import_hash' in column_names
            assert 'macho_export_hash' in column_names
            assert 'macho_entitlement_hash' in column_names
            assert 'macho_symhash' in column_names


class TestHashExtractorClickHouseTable:
    """Tests for get_clickhouse_table method."""

    def test_clickhouse_table_name(self, elf_binary_path, mock_logger):
        """Test correct ClickHouse table name."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            assert extractor.get_clickhouse_table() == "redb_hashes"


@pytest.mark.unit
@pytest.mark.dataclass
class TestHashExtractorHashesDataclass:
    """Tests for Hashes dataclass structure."""

    def test_hashes_dataclass_structure(self, elf_binary_path, mock_logger):
        """Test that Hashes dataclass has expected structure."""
        from redb.extractors.hashes import HashExtractor
        from redb.models.dataclasses import Hashes

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert isinstance(result, Hashes)
            # Required fields
            assert hasattr(result, 'md5')
            assert hasattr(result, 'sha1')
            assert hasattr(result, 'sha256')
            assert hasattr(result, 'ssdeep_hash')
            assert hasattr(result, 'tlsh_hash')
            # Optional fields
            assert hasattr(result, 'authentihash')
            assert hasattr(result, 'imphash')
            assert hasattr(result, 'richhash')

    def test_hashes_dataclass_has_macho_fields(self):
        """Test that Hashes dataclass has Mach-O hash fields."""
        from redb.models.dataclasses import Hashes

        # Create instance with default values
        hashes = Hashes(
            md5="d41d8cd98f00b204e9800998ecf8427e",
            sha1="da39a3ee5e6b4b0d3255bfef95601890afd80709",
            sha256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
            ssdeep_hash="",
            tlsh_hash=""
        )

        # Verify Mach-O hash fields exist and default to None
        assert hasattr(hashes, 'macho_dylib_hash')
        assert hasattr(hashes, 'macho_import_hash')
        assert hasattr(hashes, 'macho_export_hash')
        assert hasattr(hashes, 'macho_entitlement_hash')
        assert hasattr(hashes, 'macho_symhash')

        assert hashes.macho_dylib_hash is None
        assert hashes.macho_import_hash is None
        assert hashes.macho_export_hash is None
        assert hashes.macho_entitlement_hash is None
        assert hashes.macho_symhash is None

    def test_hashes_dataclass_with_macho_values(self):
        """Test that Hashes dataclass can be created with Mach-O hash values."""
        from redb.models.dataclasses import Hashes

        hashes = Hashes(
            md5="d41d8cd98f00b204e9800998ecf8427e",
            sha1="da39a3ee5e6b4b0d3255bfef95601890afd80709",
            sha256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
            ssdeep_hash="",
            tlsh_hash="",
            macho_dylib_hash="abc123def456789012345678901234567",
            macho_import_hash="def456abc123789012345678901234567",
            macho_export_hash="789012abc123def456345678901234567",
            macho_entitlement_hash="345678abc123def456789012901234567",
            macho_symhash="901234abc123def456789012345678567"
        )

        assert hashes.macho_dylib_hash == "abc123def456789012345678901234567"
        assert hashes.macho_import_hash == "def456abc123789012345678901234567"
        assert hashes.macho_export_hash == "789012abc123def456345678901234567"
        assert hashes.macho_entitlement_hash == "345678abc123def456789012901234567"
        assert hashes.macho_symhash == "901234abc123def456789012345678567"


class TestHashExtractorErrorHandling:
    """Tests for error handling in HashExtractor."""

    def test_extract_handles_hash_errors(self, elf_binary_path, mock_logger):
        """Test that extract handles hash computation errors gracefully."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            # Even if some hashes fail, we should get a result
            result = extractor.extract()

            assert result is not None

    def test_extract_with_invalid_pe(self, elf_binary_path, mock_logger):
        """Test extraction when PE parsing fails."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            # Force filetype to be 'pebin' but use ELF file
            with patch.object(HashExtractor, '__init__', lambda self, *args, **kwargs: None):
                extractor = HashExtractor.__new__(HashExtractor)
                extractor.filepath = elf_binary_path
                extractor.log = mock_logger
                extractor.hashes = None
                extractor.pe = None
                extractor.filetype = 'pebin'

                with open(elf_binary_path, 'rb') as f:
                    extractor.binary = f.read()

                # Should not crash
                result = extractor._extract_hashes()
                assert result is not None


class TestHashExtractorMachoHashes:
    """Tests for Mach-O specific hash extraction."""

    def test_macho_hashes_not_set_for_elf(self, elf_binary_path, mock_logger):
        """Test that Mach-O hashes are not set for ELF files."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.macho_dylib_hash is None
            assert result.macho_import_hash is None
            assert result.macho_export_hash is None
            assert result.macho_entitlement_hash is None
            assert result.macho_symhash is None

    def test_macho_hashes_not_set_for_pe(self, pe_binary_path, mock_logger):
        """Test that Mach-O hashes are not set for PE files."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.macho_dylib_hash is None
            assert result.macho_import_hash is None
            assert result.macho_export_hash is None
            assert result.macho_entitlement_hash is None
            assert result.macho_symhash is None

    def test_extract_macho_hashes_with_mock(self, elf_binary_path, mock_logger):
        """Test Mach-O hash extraction with mocked macho object."""
        from redb.extractors.hashes import HashExtractor

        # Create mock macho object
        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64"]
        mock_macho.get_similarity_hashes.return_value = {
            'dylib_hash': 'abc123def456789012345678901234ab',
            'import_hash': 'def456abc789012345678901234567cd',
            'export_hash': '789012abc456def345678901234567ef',
            'entitlement_hash': '345678abc123def789012901234567gh',
            'symhash': '901234abc123def456789012345678ij'
        }

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)
            # Override filetype to trigger macho hash extraction
            extractor.filetype = 'macho'

            result = extractor.extract()

            assert result is not None
            assert result.macho_dylib_hash == 'abc123def456789012345678901234ab'
            assert result.macho_import_hash == 'def456abc789012345678901234567cd'
            assert result.macho_export_hash == '789012abc456def345678901234567ef'
            assert result.macho_entitlement_hash == '345678abc123def789012901234567gh'
            assert result.macho_symhash == '901234abc123def456789012345678ij'

    def test_extract_macho_hashes_fat_binary_mock(self, elf_binary_path, mock_logger):
        """Test Mach-O hash extraction for FAT binary with mocked macho object."""
        from redb.extractors.hashes import HashExtractor

        # Create mock macho object for FAT binary
        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64", "arm64"]  # Multiple archs = FAT
        mock_macho.get_similarity_hashes.return_value = {
            'fat': {
                'dylib_hash': 'fat_dylib_hash_12345678901234567',
                'import_hash': 'fat_import_hash_12345678901234567',
            },
            'combined': {
                'dylib_hash': 'combined_dylib_hash_1234567890123',
            }
        }

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)
            extractor.filetype = 'macho'

            result = extractor.extract()

            assert result is not None
            # Should use 'fat' key for FAT binaries
            assert result.macho_dylib_hash == 'fat_dylib_hash_12345678901234567'
            assert result.macho_import_hash == 'fat_import_hash_12345678901234567'

    def test_extract_macho_hashes_handles_empty(self, elf_binary_path, mock_logger):
        """Test Mach-O hash extraction handles empty hash dict."""
        from redb.extractors.hashes import HashExtractor

        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64"]
        mock_macho.get_similarity_hashes.return_value = {}

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)
            extractor.filetype = 'macho'

            result = extractor.extract()

            # Should not crash, hashes should be None
            assert result is not None
            assert result.macho_dylib_hash is None

    def test_extract_macho_hashes_handles_none(self, elf_binary_path, mock_logger):
        """Test Mach-O hash extraction handles None return."""
        from redb.extractors.hashes import HashExtractor

        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64"]
        mock_macho.get_similarity_hashes.return_value = None

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)
            extractor.filetype = 'macho'

            result = extractor.extract()

            # Should not crash
            assert result is not None


class TestHashExtractorConsistency:
    """Tests for hash consistency."""

    def test_hash_consistency_across_extractions(self, elf_binary_path, mock_logger):
        """Test that hashes are consistent across multiple extractions."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor1 = HashExtractor(elf_binary_path, mock_logger)
            result1 = extractor1.extract()

            extractor2 = HashExtractor(elf_binary_path, mock_logger)
            result2 = extractor2.extract()

            assert result1.md5 == result2.md5
            assert result1.sha1 == result2.sha1
            assert result1.sha256 == result2.sha256

    def test_hash_values_are_lowercase(self, elf_binary_path, mock_logger):
        """Test that hash values are lowercase hex strings."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result.md5 == result.md5.lower()
            assert result.sha1 == result.sha1.lower()
            assert result.sha256 == result.sha256.lower()

    def test_hash_lengths_correct(self, elf_binary_path, mock_logger):
        """Test that hash lengths are correct."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert len(result.md5) == 32  # MD5 = 128 bits = 32 hex chars
            assert len(result.sha1) == 40  # SHA1 = 160 bits = 40 hex chars
            assert len(result.sha256) == 64  # SHA256 = 256 bits = 64 hex chars