Nana Liu

68 papers A* 1B 1Journal 64Unranked 2
YearRankTypeTitle / Venue / Authors
2026 J jnl
CoRR
Jieyun Bai, Zihao Zhou, Yitong Tang, Jie Gan, Zhuonan Liang, Jianan Fan, Lisa Mcguire, Jillian Clarke, Weidong Cai, Jacqueline Spurway, Yubo Tang, Shiye Wang, Wenda Shen, Wangwang Yu, Yihao Li, Philippe Zhang, Weili Jiang, Yongjie Li, Salem Muhsin Ali Binqahal Al Nasim, Arsen Abzhanov, Numan Saeed, Mohammad Yaqub, Zunhui Xian, Hongxing Lin, Libin Lan, Jayroop Ramesh, Valentin Bacher, Mark Eid, Hoda Kalabizadeh, Christian Rupprecht, Ana I. L. Namburete, Pak-Hei Yeung, Madeleine K. Wyburd, Nicola K. Dinsdale, Assanali Serikbey, Jiankai Li, Sung-Liang Chen, Zicheng Hu, Nana Liu, Yian Deng, Wei Hu, Cong Tan, Wenfeng Zhang, Mai Tuyet Nhi, Gregor Koehler, Rapheal Stock, Klaus Maier-Hein, Marawan Elbatel, Xiaomeng Li, Saad Slimani, Victor M. Campello, Benard Ohene-Botwe, Isaac Khobo, Yuxin Huang, Zhenyan Han, Hongying Hou, Di Qiu, Zheng Zheng, Gongning Luo, Dong Ni, Yaosheng Lu, Karim Lekadir, Shuo Li
2026 J jnl
Medical Image Anal.
Jieyun Bai, Zihao Zhou, Yitong Tang, Jie Gan, Zhuonan Liang, Jianan Fan, Lisa Mcguire, Jillian Clarke, Tom Weidong Cai, Jacqueline Spurway, Yubo Tang, Shiye Wang, Wenda Shen, Wangwang Yu, Yihao Li, Philippe Zhang, Weili Jiang, Yongjie Li, Salem Muhsin Ali Binqahal Al Nasim, Arsen Abzhanov, Numan Saeed, Mohammad Yaqub, Zunhui Xia, Hongxing Li, Libin Lan, Jayroop Ramesh, Valentin Bacher, Mark Eid, Hoda Kalabizadeh, Christian Rupprecht, Ana I. L. Namburete, Pak-Hei Yeung, Madeleine K. Wyburd, Nicola K. Dinsdale, Assanali Serikbey, Jiankai Li, Sung-Liang Chen, Zicheng Hu, Nana Liu, Yian Deng, Wei Hu, Cong Tan, Wenfeng Zhang, Mai Tuyet Nhi, Gregor Koehler, Rapheal Stock, Klaus Maier-Hein, Marawan Elbatel, Xiaomeng Li, Saad Slimani, Victor M. Campello, Benard Ohene-Botwe, Isaac Khobo, Yuxin Huang, Zhenyan Han, Hongying Hou, Di Qiu, Zheng Zheng, Gongning Luo, Dong Ni, Yaosheng Lu, Karim Lekadir, Shuo Li
2026 J jnl
Discov. Artif. Intell.
Fengwei Zhang, Nana Liu
2026 J jnl
Appl. Intell.
Mengxing Huang, Zhihao Huang, Zehao Ni, Yu Zhang, Nana Liu, Uzair Aslam Bhatti, Jing Chen, Gang Wang, Zhiming Bai
2026 J jnl
CoRR
Nana Liu, Mark M. Wilde
2025 J jnl
Inf. Fusion
Nana Liu, Xianzhe Zhang, Hangyao Wu
2025 J jnl
Inf. Sci.
Nana Liu, Xianzhe Zhang, Hangyao Wu, Bai Yang, Yuelong Zheng
2025 J jnl
J. Mach. Learn. Res.
Sungduk Yu, Zeyuan Hu, Akshay Subramaniam, Walter M. Hannah, Liran Peng, Jerry Lin, Mohamed Aziz Bhouri, Ritwik Gupta, Björn Lütjens, Justus C. Will, Gunnar Behrens, Julius J. M. Busecke, Nora Loose, Charles Stern, Tom Beucler, Bryce E. Harrop, Helge Heuer, Benjamin R. Hillman, Andrea M. Jenney, Nana Liu, Alistair White, Tian Zheng, Zhiming Kuang, Fiaz Ahmed, Elizabeth A. Barnes, Noah D. Brenowitz, Christopher S. Bretherton, Veronika Eyring, Savannah L. Ferretti, Nicholas J. Lutsko, Pierre Gentine, Stephan Mandt, J. David Neelin, Rose Yu, Laure Zanna, Nathan M. Urban, Janni Yuval, Ryan Abernathey, Pierre Baldi, Wayne Chuang, Yu Huang, Fernando Iglesias-Suarez, Sanket R. Jantre, Po-Lun Ma, Sara Shamekh, Guang Zhang, Michael S. Pritchard
2025 J jnl
CoRR
Michele Minervini, Madison Chin, Jacob Kupperman, Nana Liu, Ivy Luo, Meghan Ly, Soorya Rethinasamy, Kathie Wang, Mark M. Wilde
2025 J jnl
CoRR
Outongyi Lv, Yewei Yuan, Nana Liu
2025 J jnl
SIAM J. Numer. Anal.
Shi Jin, Nana Liu, Chuwen Ma
2025 J jnl
CoRR
Shi Jin, Nana Liu, Chuwen Ma, Yue Yu
2025 J jnl
J. Sci. Comput.
Shi Jin, Nana Liu, Wei Wei
2025 J jnl
J. Comput. Phys.
Shi Jin, Nana Liu, Yue Yu
2025 J jnl
CoRR
Shi Jin, Nana Liu, Chuwen Ma, Yue Yu
2025 J jnl
CoRR
Nana Liu, Michele Minervini, Dhrumil Patel, Mark M. Wilde
2025 J jnl
CoRR
Shi Jin, Nana Liu, Yue Yu
2025 J jnl
SIAM J. Sci. Comput.
Shi Jin, Nana Liu, Chuwen Ma
2025 J jnl
CoRR
Shi Jin, Nana Liu, Yue Yu
2025 J jnl
Int. Arab J. Inf. Technol.
Jianzhong Li, Fengwei Zhang, Nana Liu
2024 J jnl
Remote. Sens.
Shunping Chen, Congming Dai, Nana Liu, Wentao Lian, Yuxuan Zhang, Fan Wu, Cong Zhang, Shengcheng Cui, Heli Wei
2024 J jnl
Remote. Sens.
Qing Ji, Nana Liu, Mengqin Yu, Zhiming Zhang, Zehui Xiao, Xiaoping Pang
2024 J jnl
NeuroImage
Mengjing Cai, Yuan Ji, Qiyu Zhao, Hui Xue, Zuhao Sun, He Wang, Yijing Zhang, Yayuan Chen, Yao Zhao, Yujie Zhang, Minghuan Lei, Chunyang Wang, Chuanjun Zhuo, Nana Liu, Huaigui Liu, Feng Liu
2024 J jnl
Remote. Sens.
Xuebin Ma, Tao Luo, Xuebin Li, Changyu Liu, Nana Liu, Qiang Liu, Kun Zhang, Jie Chen, Liming Zhu
2024 conf
IUGC@MICCAI
Zicheng Hu, Yian Deng, Ruisheng Ran, Nana Liu, Xinru Wei, Jieyun Bai, Wenfeng Zhang, Wei Hu
2024 J jnl
CoRR
Shi Jin, Nana Liu, Chuwen Ma
2024 conf
DSAI
Tianjia Wang, Siyi Sun, Nana Liu, Yile Chen, Liang Zheng
2024 J jnl
CoRR
Junpeng Hu, Shi Jin, Nana Liu, Lei Zhang
2024 J jnl
Quantum
Junpeng Hu, Shi Jin, Nana Liu, Lei Zhang
2024 J jnl
CoRR
Shi Jin, Nana Liu, Yue Yu
2024 J jnl
SIAM J. Sci. Comput.
Shi Jin, Xiantao Li, Nana Liu, Yue Yu
2024 J jnl
J. Comput. Phys.
Shi Jin, Xiantao Li, Nana Liu, Yue Yu
2024 J jnl
CoRR
Shi Jin, Nana Liu, Yue Yu
2024 J jnl
IEEE Access
Baojun Tian, Nana Liu, Jiandong Fang
2024 J jnl
CoRR
Hao-Chung Cheng, Nilanjana Datta, Nana Liu, Theshani Nuradha, Robert Salzmann, Mark M. Wilde
2024 J jnl
CoRR
Shi Jin, Nana Liu, Chuwen Ma
2024 J jnl
CoRR
Chuwen Ma, Shi Jin, Nana Liu, Kezhen Wang, Lei Zhang
2023 J jnl
Appl. Intell.
Yue He, Dong-Ling Xu, Jianbo Yang, Zeshui Xu, Nana Liu
2023 J jnl
IEEE J. Sel. Top. Appl. Earth Obs. Remote. Sens.
Yehui Chen, Xiaomei Jin, Ningquan Weng, Wenyue Zhu, Qing Liu, Nana Liu
2023 A* conf
NeurIPS
Sungduk Yu, Walter M. Hannah, Liran Peng, Jerry Lin, Mohamed Aziz Bhouri, Ritwik Gupta, Björn Lütjens, Justus C. Will, Gunnar Behrens, Julius Busecke, Nora Loose, Charles Stern, Tom Beucler, Bryce E. Harrop, Benjamin R. Hillman, Andrea M. Jenney, Savannah L. Ferretti, Nana Liu, Animashree Anandkumar, Noah D. Brenowitz, Veronika Eyring, Nicholas Geneva, Pierre Gentine, Stephan Mandt, Jaideep Pathak, Akshay Subramaniam, Carl Vondrick, Rose Yu, Laure Zanna, Tian Zheng, Ryan Abernathey, Fiaz Ahmed, David C. Bader, Pierre Baldi, Elizabeth A. Barnes, Christopher S. Bretherton, Peter M. Caldwell, Wayne Chuang, Yilun Han, Yu Huang, Fernando Iglesias-Suarez, Sanket R. Jantre, Karthik Kashinath, Marat Khairoutdinov, Thorsten Kurth, Nicholas J. Lutsko, Po-Lun Ma, Griffin Mooers, J. David Neelin, David A. Randall, Sara Shamekh, Mark Taylor, Nathan M. Urban, Janni Yuval, Guang Zhang, Mike Pritchard
2023 J jnl
CoRR
Sungduk Yu, Walter M. Hannah, Liran Peng, Mohamed Aziz Bhouri, Ritwik Gupta, Jerry Lin, Björn Lütjens, Justus C. Will, Tom Beucler, Bryce E. Harrop, Benjamin R. Hillman, Andrea M. Jenney, Savannah L. Ferretti, Nana Liu, Anima Anandkumar, Noah D. Brenowitz, Veronika Eyring, Pierre Gentine, Stephan Mandt, Jaideep Pathak, Carl Vondrick, Rose Yu, Laure Zanna, Ryan P. Abernathey, Fiaz Ahmed, David C. Bader, Pierre Baldi, Elizabeth A. Barnes, Gunnar Behrens, Christopher S. Bretherton, Julius J. M. Busecke, Peter M. Caldwell, Wayne Chuang, Yilun Han, Yu Huang, Fernando Iglesias-Suarez, Sanket R. Jantre, Karthik Kashinath, Marat Khairoutdinov, Thorsten Kurth, Nicholas J. Lutsko, Po-Lun Ma, Griffin Mooers, J. David Neelin, David A. Randall, Sara Shamekh, Akshay Subramaniam, Mark A. Taylor, et al.
2023 J jnl
Appl. Soft Comput.
Nana Liu, Zeshui Xu, Hangyao Wu
2023 J jnl
CoRR
Shi Jin, Nana Liu, Xiantao Li, Yue Yu
2023 J jnl
J. Comput. Phys.
Shi Jin, Nana Liu, Yue Yu
2022 J jnl
Remote. Sens.
Nana Liu, Shengcheng Cui, Tao Luo, Shunping Chen, Kaixuan Yang, Xuebin Ma, Gang Sun, Xuebin Li
2022 J jnl
Remote. Sens.
Lei Ji, Weixin Xu, Haonan Chen, Nana Liu
2022 J jnl
Expert Syst. Appl.
Yufei Xia, Hanfei Ren, Yinguo Li, Jiahui Xia, Lingyun He, Nana Liu
2022 J jnl
Soft Comput.
Nana Liu, Zeshui Xu, Hangyao Wu, Peijia Ren
2022 J jnl
Quantum
Shi Jin, Xiantao Li, Nana Liu
2022 J jnl
Appl. Intell.
Yue He, Zeshui Xu, Nana Liu
2022 J jnl
CoRR
Shi Jin, Nana Liu, Yue Yu
2022 J jnl
CoRR
Shi Jin, Nana Liu, Yue Yu
2022 J jnl
J. Comput. Phys.
Shi Jin, Nana Liu, Yue Yu
2021 J jnl
Inf. Sci.
Nana Liu, Zeshui Xu, Xiao-Jun Zeng, Peijia Ren
2021 J jnl
Fuzzy Optim. Decis. Mak.
Nana Liu, Zeshui Xu, Yue He, Xiao-Jun Zeng
2021 J jnl
Int. J. Intell. Syst.
Nana Liu, Zeshui Xu
2021 J jnl
Knowl. Based Syst.
Nana Liu, Zeshui Xu, Hangyao Wu, Peijia Ren
2021 J jnl
CoRR
Shi Jin, Xiantao Li, Nana Liu
2021 J jnl
Quantum Inf. Process.
Wooyeong Song, Marcin Wiesniak, Nana Liu, Marcin Pawlowski, Jin-Hyoung Lee, Jaewan Kim, Jeongho Bang
2020 B conf
IJCNN
Nana Liu, Zeshui Xu, Hangyao Wu, Peijia Ren, Fanlin Meng
2020 J jnl
CoRR
Maurice Weber, Nana Liu, Bo Li, Ce Zhang, Zhikuan Zhao
2020 J jnl
CoRR
Yuxuan Du, Min-Hsiu Hsieh, Tongliang Liu, Dacheng Tao, Nana Liu
2019 J jnl
Appl. Soft Comput.
Nana Liu, Yue He, Zeshui Xu
2019 J jnl
CoRR
Haochuan Lu, Huanlin Xu, Nana Liu, Yangfan Zhou, Xin Wang
2019 J jnl
Remote. Sens.
Wenzhong Zhang, Shumei Deng, Tao Luo, Yang Wu, Nana Liu, Xuebin Li, Yinbo Huang, Wenyue Zhu
2018 J jnl
Photonic Netw. Commun.
Shibao Wu, Nana Liu, Yaling Cui
2017 J jnl
Expert Syst. Appl.
Yufei Xia, Chuanzhe Liu, Yuying Li, Nana Liu
2017 J jnl
Electron. Commer. Res. Appl.
Yufei Xia, Chuanzhe Liu, Nana Liu
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