Han Fu

59 papers A* 3A 3C 4Journal 41Unranked 8
YearRankTypeTitle / Venue / Authors
2026 J jnl
Comput. Electron. Agric.
Feng Liu, Dongying Mo, Han Fu, Shaozheng Yu, Xuan Chu, Nan She, Xing Xu, Weizu Wang, Jieli Duan
2026 J jnl
Comput. Methods Programs Biomed.
Kellie J. Archer, Han Fu
2026 J jnl
Autom. Softw. Eng.
Mengliang Li, Qiang Shen, Xiaoxue Ren, Han Fu, Zhuo Li, Jianling Sun
2026 J jnl
CoRR
Han Fu, Andreas Ermedahl, Sigrid Eldh, Kristian Wiklund, Philipp Haller, Cyrille Artho
2025 J jnl
IEEE Geosci. Remote. Sens. Lett.
Zizhen Li, Shichao Jin, Guangjun He, Xueliang Zhang, Pengming Feng, Han Fu, Ying Liang
2025 C conf
DSD
Han Fu, Sigrid Eldh, Kristian Wiklund, Andreas Ermedahl, Philipp Haller, Cyrille Artho
2025 J jnl
CoRR
Han Fu, Sigrid Eldh, Kristian Wiklund, Andreas Ermedahl, Philipp Haller, Cyrille Artho
2025 J jnl
IEEE Trans. Knowl. Data Eng.
Mouxiang Chen, Han Fu, Chenghao Liu, Xiaoyun Joy Wang, Zhuo Li, Jianling Sun
2025 A conf
ECAI
Amer Mustajbasic, Han Fu, Jialu Xu, Shuangshuang Chen, Erik Stenborg
2025 conf
ICIC (15)
Han Fu, Zhuo Xu, Zhipeng Cui, Lu Bai, Lixin Cui
2025 J jnl
IEEE Trans. Geosci. Remote. Sens.
Bo Sun, Yiming Yan, Han Fu, Guangjun He, Yanming He, Zhengning Zhang, Fengjiao Gao
2025 J jnl
CoRR
Lefei Shen, Mouxiang Chen, Han Fu, Xiaoxue Ren, Xiaoyun Joy Wang, Jianling Sun, Zhuo Li, Chenghao Liu
2025 J jnl
CoRR
Lefei Shen, Mouxiang Chen, Xu Liu, Han Fu, Xiaoxue Ren, Jianling Sun, Zhuo Li, Chenghao Liu
2024 A* conf
KDD
Mouxiang Chen, Lefei Shen, Han Fu, Zhuo Li, Jianling Sun, Chenghao Liu
2024 J jnl
Remote. Sens.
Qi Chen, Han Fu, Xiaoming Li, Xiaochuan Qin, Lin Yan
2024 C conf
APSEC
Mengliang Li, Xiaoxue Ren, Han Fu, Zhuo Li, Jianling Sun
2024 J jnl
IEICE Electron. Express
Long Qiu, Yongheng Ai, Jianxing Liu, Cheng Xie, Han Fu, Yitao Yan
2024 J jnl
Remote. Sens.
Yunsheng Zhang, Jianguo Yao, Ruixiang Zhang, Xuying Wang, Siyang Chen, Han Fu
2024 A conf
ICST
Han Fu, Sigrid Eldh, Kristian Wiklund, Andreas Ermedahl, Philipp Haller, Cyrille Artho
2024 J jnl
CoRR
Han Fu, Sigrid Eldh, Kristian Wiklund, Andreas Ermedahl, Philipp Haller, Cyrille Artho
2024 J jnl
Comput. Biol. Medicine
Min Yan, Ruixin Wu, Han Fu, Chao Hu, Yanan Hao, Jie Zeng, Tong Chen, Yingming Wang, Yingying Wang, Jing Hu, Aishun Jin
2024 conf
ACL (Findings)
Zhisheng Lin, Han Fu, Chenghao Liu, Zhuo Li, Jianling Sun
2024 J jnl
CoRR
Zhisheng Lin, Han Fu, Chenghao Liu, Zhuo Li, Jianling Sun
2024 J jnl
IEEE Geosci. Remote. Sens. Lett.
Yiming Yan, Yanming He, Nan Su, Guangjun He, Han Fu
2024 conf
CCL
Qi Huang, Han Fu, Wenbin Luo, Mingwen Wang, Kaiwei Luo
2024 A* conf
ICML
Han Fu, Jian Tan, Pinhan Zhang, Feifei Li, Jianling Sun
2024 J jnl
Comput. Electron. Agric.
Xing Xu, Jinming Liang, Jianying Li, Guang Wu, Jieli Duan, Mohui Jin, Han Fu
2023 J jnl
CoRR
Mouxiang Chen, Lefei Shen, Han Fu, Zhuo Li, Jianling Sun, Chenghao Liu
2023 J jnl
Proc. VLDB Endow.
Han Fu, Chang Liu, Bin Wu, Feifei Li, Jian Tan, Jianling Sun
2023 A conf
ISSRE
Mengliang Li, Xiaoxue Ren, Han Fu, Zhuo Li, Jianling Sun
2023 J jnl
IEEE Control. Syst. Lett.
Han Fu, Hugh H. T. Liu
2023 J jnl
Autom.
Han Fu, Hugh Hong-Tao Liu
2023 conf
Agro-Geoinformatics
Zizhen Li, Guangjun He, Han Fu, Qianqian Chen, Boyi Shangguan, Pengming Feng, Shichao Jin
2023 conf
CASE
Han Fu, L. Jeff Hong, Guangxin Jiang
2022 J jnl
IEEE Control. Syst. Lett.
Han Fu, Hugh H. T. Liu
2022 J jnl
Sensors
Qiqi Li, Shanxu Duan, Han Fu
2022 J jnl
Int. J. Medical Informatics
Yi Tao, Yu Cai, Han Fu, Licheng Song, Lixin Xie, Kaifei Wang
2022 J jnl
Int. J. Digit. Earth
Xiao-ming Li, Yujia Qiu, Yacheng Wang, Bingqing Huang, Haiming Lu, Min Chu, Han Fu, Fengming Hui
2022 conf
ISSRE Workshops
Han Fu, Sigrid Eldh, Kristian Wiklund, Andreas Ermedahl, Cyrille Artho
2022 C conf
ISNCC
Guangjun He, Han Fu, Pengming Feng, Shishuo Liu, Jiang Chang, Shichao Jin
2021 J jnl
IEEE Softw.
Benoit Baudry, Zimin Chen, Khashayar Etemadi, Han Fu, Davide Ginelli, Steve Kommrusch, Matias Martinez, Martin Monperrus, Javier Ron Arteaga, He Ye, Zhongxing Yu
2021 J jnl
Remote. Sens.
Han Fu, Bihong Fu, Pilong Shi
2021 J jnl
ISPRS Int. J. Geo Inf.
Han Fu, Xiangtao Fan, Zhenzhen Yan, Xiaoping Du
2021 J jnl
Comput. Electron. Agric.
Jie Guo, Manoj Karkee, Zhou Yang, Han Fu, Jun Li, Yinlong Jiang, Tingting Jiang, Enxiu Liu, Jieli Duan
2021 J jnl
Briefings Bioinform.
Han Fu, Kellie J. Archer
2021 J jnl
Unmanned Syst.
Han Fu, Hugh H. T. Liu
2021 J jnl
Comput. Electron. Agric.
Jie Guo, Manoj Karkee, Zhou Yang, Han Fu, Jun Li, Yinlong Jiang, Tingting Jiang, Enxiu Liu, Jieli Duan
2020 J jnl
CoRR
Han Fu, Yunyu Bai, Zhuo Li, Jun Shen, Jianling Sun
2020 J jnl
Symmetry
Jie Guo, Mohui Jin, Jieli Duan, Jun Li, Han Fu, Lei Zhao, Zeyu Xu, Yunhe Ding, Yinlong Jiang, Zhou Yang
2020 J jnl
J. Sensors
Xiaomin Li, Lixue Zhu, Xuan Chu, Han Fu
2020 J jnl
Comput. Electron. Agric.
Weizu Wang, Shuangming Zhang, Han Fu, Huazhong Lu, Zhou Yang
2020 A* conf
CVPR
Han Fu, Rui Wu, Chenghao Liu, Jianling Sun
2020 J jnl
CoRR
Han Fu, Rui Wu, Chenghao Liu, Jianling Sun
2020 J jnl
CoRR
Benoit Baudry, Zimin Chen, Khashayar Etemadi, Han Fu, Davide Ginelli, Steve Kommrusch, Matias Martinez, Martin Monperrus, Javier Ron, He Ye, Zhongxing Yu
2019 C conf
IGARSS
Bihong Fu, Pilong Shi, Han Fu, Yoshiki Ninomiya, Jiaxin Du
2019 J jnl
Remote. Sens.
Han Fu, Bihong Fu, Yoshiki Ninomiya, Pilong Shi
2019 conf
ACL (1)
Han Fu, Chenghao Liu, Jianling Sun
2019 J jnl
CoRR
Han Fu, Chenghao Liu, Jianling Sun
2013 conf
TAAI
Han Fu, Ke Wu, Chao Wang, Yougui Wang
tests/unit/test_database_exporters.py
← Index tests/unit/test_database_exporters.py python
"""
Unit tests for database exporters.
"""
import pytest
import json
from dataclasses import dataclass, asdict
from datetime import datetime, timezone
from unittest.mock import Mock, patch, MagicMock

pytestmark = [pytest.mark.unit, pytest.mark.exporters]


# ============================================================================
# Sample Dataclass for Testing
# ============================================================================

@dataclass
class SampleData:
    """Sample dataclass for export testing."""
    sha256: str = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    name: str = "test_sample"
    value: int = 42
    _id: str = "test_id"


# ============================================================================
# PrintExporter Tests
# ============================================================================

class TestPrintExporter:
    """Tests for PrintExporter class."""

    def test_initialization(self, mock_logger):
        """Test PrintExporter initialization."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")

        assert exporter.index_prefix == "test_index"
        assert exporter.log is mock_logger

    def test_export_dataclass(self, mock_logger, capsys):
        """Test exporting a dataclass."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        sample = SampleData()

        result = exporter.export(sample)

        assert result is True
        captured = capsys.readouterr()
        assert "test_sample" in captured.out

    def test_export_list_of_dataclasses(self, mock_logger, capsys):
        """Test exporting a list of dataclasses."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        samples = [SampleData(name="sample1"), SampleData(name="sample2")]

        result = exporter.export(samples)

        assert result is True
        captured = capsys.readouterr()
        assert "sample1" in captured.out
        assert "sample2" in captured.out

    def test_export_dict(self, mock_logger, capsys):
        """Test exporting a dictionary."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        data = {"key": "value", "number": 42}

        result = exporter.export(data)

        assert result is True
        captured = capsys.readouterr()
        assert "key" in captured.out
        assert "value" in captured.out

    def test_export_none(self, mock_logger):
        """Test exporting None."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")

        result = exporter.export(None)

        assert result is True

    def test_export_empty_list(self, mock_logger):
        """Test exporting empty list."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")

        result = exporter.export([])

        assert result is True


# ============================================================================
# ElasticsearchExporter Tests
# ============================================================================

class TestElasticsearchExporter:
    """Tests for ElasticsearchExporter class."""

    def test_initialization(self, mock_logger):
        """Test ElasticsearchExporter initialization."""
        with patch('redb.settings.get_elasticsearch_client') as mock_client:
            mock_client.return_value = Mock()
            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")

            assert exporter.index_prefix == "test_index"
            assert exporter.log is mock_logger
            assert exporter.client is not None

    def test_export_dataclass(self, mock_logger):
        """Test exporting a dataclass to Elasticsearch."""
        with patch('redb.settings.get_elasticsearch_client') as mock_get_client:
            mock_client = Mock()
            mock_get_client.return_value = mock_client

            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")
            sample = SampleData()

            result = exporter.export(
                sample,
                index="test_index",
                tag="test_tag",
                hashes={"sha256": sample.sha256}
            )

            assert result is True
            mock_client.index.assert_called()

    def test_export_list(self, mock_logger):
        """Test exporting a list to Elasticsearch."""
        with patch('redb.settings.get_elasticsearch_client') as mock_get_client:
            mock_client = Mock()
            mock_get_client.return_value = mock_client

            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")
            samples = [SampleData(name="sample1"), SampleData(name="sample2")]

            result = exporter.export(
                samples,
                index="test_index",
                tag="test_tag",
                hashes={"sha256": "test_hash"}
            )

            assert result is True
            # Should be called twice (once per sample)
            assert mock_client.index.call_count == 2

    def test_export_failure(self, mock_logger):
        """Test handling export failure."""
        with patch('redb.settings.get_elasticsearch_client') as mock_get_client:
            mock_client = Mock()
            mock_client.index.side_effect = Exception("Connection failed")
            mock_get_client.return_value = mock_client

            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")
            sample = SampleData()

            result = exporter.export(
                sample,
                index="test_index",
                tag="test_tag",
                hashes={"sha256": sample.sha256}
            )

            assert result is False
            mock_logger.error.assert_called()

    def test_export_with_custom_id(self, mock_logger):
        """Test exporting with custom _id field."""
        with patch('redb.settings.get_elasticsearch_client') as mock_get_client:
            mock_client = Mock()
            mock_get_client.return_value = mock_client

            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")
            sample = SampleData(_id="custom_id")

            result = exporter.export(
                sample,
                index="test_index",
                tag="test_tag",
                hashes={"sha256": sample.sha256}
            )

            assert result is True


# ============================================================================
# ClickHouseExporter Tests
# ============================================================================

class TestClickHouseExporter:
    """Tests for ClickHouseExporter class."""

    def test_initialization(self, mock_logger):
        """Test ClickHouseExporter initialization."""
        from redb.extractors.database_exporters import ClickHouseExporter

        mock_client = Mock()
        exporter = ClickHouseExporter(mock_logger, "test_index", client=mock_client)

        assert exporter.index_prefix == "test_index"
        assert exporter.log is mock_logger
        assert exporter.client is mock_client

    def test_export_tuple_data(self, mock_logger):
        """Test exporting tuple data to ClickHouse."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = [["hash123", "sample.exe", 1024]]
            column_names = ["sha256", "name", "size"]
            column_type_names = ["String", "String", "UInt64"]

            result = exporter.export(
                (data, column_names, column_type_names),
                table="test_table"
            )

            assert result is True

    def test_export_multi_table(self, mock_logger):
        """Test exporting multi-table data to ClickHouse."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = {
                'multi_table': True,
                'table1': {
                    'table': 'test_table1',
                    'data': [["hash123", "sample.exe"]],
                    'column_names': ["sha256", "name"],
                    'column_type_names': ["String", "String"]
                }
            }

            result = exporter.export(data)

            assert result is True

    def test_export_failure(self, mock_logger):
        """Test handling export failure."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.side_effect = Exception("Connection failed")
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = [["hash123", "sample.exe", 1024]]
            column_names = ["sha256", "name", "size"]
            column_type_names = ["String", "String", "UInt64"]

            result = exporter.export(
                (data, column_names, column_type_names),
                table="test_table"
            )

            assert result is False

    def test_export_missing_table(self, mock_logger):
        """Test export without table name raises error."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = [["hash123", "sample.exe", 1024]]
            column_names = ["sha256", "name", "size"]
            column_type_names = ["String", "String", "UInt64"]

            result = exporter.export(
                (data, column_names, column_type_names)
                # Missing table parameter
            )

            assert result is False

    def test_get_client(self, mock_logger):
        """Test get_client method."""
        from redb.extractors.database_exporters import ClickHouseExporter

        mock_client = Mock()
        exporter = ClickHouseExporter(mock_logger, "test_index", client=mock_client)

        result = exporter.get_client()

        assert result is mock_client

    def test_get_client_creates_new(self, mock_logger):
        """Test get_client creates new client if none exists."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_new_client = Mock()
            mock_create.return_value = mock_new_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            result = exporter.get_client()

            assert result is mock_new_client


# ============================================================================
# DatabaseExporter Abstract Base Class Tests
# ============================================================================

class TestDatabaseExporter:
    """Tests for DatabaseExporter abstract base class."""

    def test_cannot_instantiate_abstract(self):
        """Test that DatabaseExporter cannot be instantiated directly."""
        from redb.extractors.database_exporters import DatabaseExporter

        with pytest.raises(TypeError):
            DatabaseExporter()

    def test_subclass_must_implement_export(self):
        """Test that subclasses must implement export method."""
        from redb.extractors.database_exporters import DatabaseExporter

        # Define incomplete subclass
        class IncompleteExporter(DatabaseExporter):
            pass

        with pytest.raises(TypeError):
            IncompleteExporter()


# ============================================================================
# Null/Edge Case Tests
# ============================================================================

class TestExporterEdgeCases:
    """Tests for edge cases in exporters."""

    def test_print_exporter_with_complex_nested_data(self, mock_logger, capsys):
        """Test PrintExporter with complex nested data."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        data = {
            "level1": {
                "level2": {
                    "level3": [1, 2, 3]
                }
            },
            "array": [{"a": 1}, {"b": 2}]
        }

        result = exporter.export(data)

        assert result is True
        captured = capsys.readouterr()
        assert "level1" in captured.out

    def test_clickhouse_null_handling(self, mock_logger):
        """Test ClickHouse exporter handles null values."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            # Multi-table data with null values
            data = {
                'multi_table': True,
                'table1': {
                    'table': 'test_table',
                    'data': [[None, "sample.exe", None]],
                    'column_names': ["sha256", "name", "size"],
                    'column_type_names': ["Nullable(String)", "String", "Nullable(UInt64)"]
                }
            }

            result = exporter.export(data)

            # Should handle nulls appropriately
            assert result is True

    def test_clickhouse_array_handling(self, mock_logger):
        """Test ClickHouse exporter handles array types."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = {
                'multi_table': True,
                'table1': {
                    'table': 'test_table',
                    'data': [["hash", ["tag1", "tag2"]]],
                    'column_names': ["sha256", "tags"],
                    'column_type_names': ["String", "Array(String)"]
                }
            }

            result = exporter.export(data)

            assert result is True


# ============================================================================
# Type Conversion Tests
# ============================================================================

class TestExporterTypeConversions:
    """Tests for type conversions in exporters."""

    def test_clickhouse_datetime_handling(self, mock_logger):
        """Test ClickHouse exporter handles datetime values."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            now = datetime.now(timezone.utc)
            data = {
                'multi_table': True,
                'table1': {
                    'table': 'test_table',
                    'data': [["hash", now]],
                    'column_names': ["sha256", "timestamp"],
                    'column_type_names': ["String", "DateTime64(3, 'UTC')"]
                }
            }

            result = exporter.export(data)

            assert result is True

    def test_print_exporter_datetime_serialization(self, mock_logger, capsys):
        """Test PrintExporter handles datetime serialization."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        now = datetime.now(timezone.utc)
        data = {"timestamp": now, "name": "test"}

        result = exporter.export(data)

        assert result is True
        captured = capsys.readouterr()
        assert "timestamp" in captured.out


# ============================================================================
# Connection Context Manager Tests
# ============================================================================

class TestClickHouseConnectionManager:
    """Tests for ClickHouse connection context manager."""

    def test_clickhouse_connection_context(self, mock_logger):
        """Test clickhouse_connection context manager."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            with exporter.clickhouse_connection() as client:
                assert client is mock_client

            # Client should be closed after context exits
            mock_client.close.assert_called()

    def test_clickhouse_connection_close_on_error(self, mock_logger):
        """Test client is closed even if operation fails."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            try:
                with exporter.clickhouse_connection() as client:
                    raise ValueError("Test error")
            except ValueError:
                pass

            # Client should still be closed
            mock_client.close.assert_called()