Haipeng Li

68 papers A* 3A 2B 2C 1Misc 1Journal 32Unranked 27
YearRankTypeTitle / Venue / Authors
2026 J jnl
CoRR
Zhaocheng Yu, Xiang Chen, Runzhe Li, Zihan Geng, Guangyu Sun, Haipeng Li, Kui Jiang
2026 A* conf
AAAI
Yu Liu, Haoqin Yang, Jinping Sui, Hui Wang, Haipeng Li, Weimin Wang, Qi Jia
2026 J jnl
CoRR
Haipeng Li, Rongxuan Peng, Anwei Luo, Shunquan Tan, Changsheng Chen, Anastasia Antsiferova
2025 A conf
SDM
Haipeng Li, Jiangxia Cao, Yiwen Gao, Yunhuai Liu, Shuchao Pang
2025 B conf
SMC
Zhan Wu, Haipeng Li, Di Wu, Shuchao Pang
2025 J jnl
Symmetry
Li Jiang, Haipeng Li, Lei Ma, Weiming Guan, Haosen Wang, Haochen Feng, Bei Zhang, Rui Wang
2025 conf
OFC
Biondo Biondi, Haipeng Li
2025 J jnl
CoRR
Shuo Yang, Jiangxia Cao, Haipeng Li, Yuqi Mao, Shuchao Pang
2025 conf
CCNC
Fahmida Afrin, Haipeng Li, Boyang Wang, Nirnimesh Ghose
2025 J jnl
IEEE Geosci. Remote. Sens. Lett.
Xiao-Wen Liu, Bo Peng, Ying Luo, Yi-Chang Chen, Haipeng Li, Qun Zhang
2025 A* conf
CVPR
Weilong Yan, Ming Li, Haipeng Li, Shuwei Shao, Robby T. Tan
2025 J jnl
CoRR
Weilong Yan, Ming Li, Haipeng Li, Shuwei Shao, Robby T. Tan
2024 J jnl
IEEE Trans. Veh. Technol.
Hancheng Yang, Haiyang Ding, Kunrui Cao, Maged Elkashlan, Haipeng Li, Kewei Xin
2024 J jnl
CoRR
Feng Liu, Haipeng Li, Guangyuan Zou, Junlun Li
2024 J jnl
CoRR
Haipeng Li, Jiangxia Cao, Yiwen Gao, Yunhuai Liu, Shuchao Pang
2024 conf
MMAsia
Haipeng Li, Guangcun Wei, Haochen Xu, Boyan Guo
2024 J jnl
CoRR
Luoqi Wang, Haipeng Li, Linshu Hu, Jiarui Cai, Zhenhong Du
2024 conf
HOST
Haipeng Li, Mabon Ninan, Boyang Wang, John Marty Emmert
2024 J jnl
Int. J. Digit. Earth
Shuyan Xu, Linshu Hu, Haipeng Li, Mengjiao Qin, Sensen Wu, Zhenhong Du
2023 J jnl
IEEE Trans. Veh. Technol.
Hancheng Yang, Haiyang Ding, Maged Elkashlan, Haipeng Li, Kewei Xin
2023 conf
RICAI
Haipeng Li, Lan Shen, Ying Li, Yanhong Jing, Song Wang, Tian Lv
2022 J jnl
J. Circuits Syst. Comput.
Hong-an Li, Guanyi Wang, Kun Gao, Haipeng Li
2022 C conf
CGI
Zhaoyu Xiong, Hao Zhang, Haipeng Li, Dan Xu
2022 conf
CODASPY
Haipeng Li, Nan Niu, Boyang Wang
2022 J jnl
Briefings Bioinform.
Dalang Yu, Xiao Yang, Bixia Tang, Yi-Hsuan Pan, Jianing Yang, Guangya Duan, Junwei Zhu, Zi-Qian Hao, Hailong Mu, Long Dai, Wangjie Hu, Mochen Zhang, Ying Cui, Tong Jin, Cui-Ping Li, Lina Ma, Xiao Su, Guoqing Zhang, Wenming Zhao, Haipeng Li
2022 J jnl
CoRR
Haipeng Li, Jonathan Dunn
2022 conf
ICIGP
Yuting Fu, Dan Xu, Kangjian He, Haipeng Li, Tingting Zhang
2022 J jnl
Wirel. Networks
Haipeng Li, Dazheng Feng, Chao Liu, Chi Zhang
2022 conf
ICIGP
Jian Gong, Haipeng Li, Dan Xu, Kangjian He, Hao Zhang, Chao Zhang, Zhong Luo
2022 B conf
LREC
Jonathan Dunn, Haipeng Li, Damian Sastre
2022 J jnl
CoRR
Jonathan Dunn, Haipeng Li, Damian Sastre
2022 conf
CNS
Haipeng Li, Kaustubh Gupta, Chenggang Wang, Nirnimesh Ghose, Boyang Wang
2022 J jnl
CoRR
Haipeng Li, Jonathan Dunn, Andrea Nini
2022 J jnl
IEEE Internet Things J.
Haiyang Ding, Mohamed-Slim Alouini, Kewei Xin, Haipeng Li, Shengzhi Xu
2022 conf
VTC Fall
Haiyang Ding, Maged Elkashlan, Hancheng Yang, Haipeng Li, Kewei Xin
2021 conf
ASICON
Kewei Xin, Bing Li, Haiyang Ding, Lei Bao, Haipeng Li
2021 conf
CSAI
Haipeng Li, Wenjuan Zheng, Bin Zhou, YanYangshuo Liu
2021 J jnl
IEEE Access
Haipeng Li, Ke Qu, Jianbo Zhou
2021 conf
CSAI
Zhang bo chuan, Liu Guo Ming, Pei Sheng Wang, Yu Li, Haipeng Li
2021 conf
WISEC
Haipeng Li, Chenggang Wang, Nirnimesh Ghose, Boyang Wang
2020 J jnl
CoRR
Chenggang Wang, Sean Kennedy, Haipeng Li, King Hudson, Gowtham Atluri, Xuetao Wei, Wenhai Sun, Boyang Wang
2020 conf
WISEC
Chenggang Wang, Sean Kennedy, Haipeng Li, King Hudson, Gowtham Atluri, Xuetao Wei, Wenhai Sun, Boyang Wang
2020 conf
SecureComm (1)
Haipeng Li, Ben Niu, Boyang Wang
2019 conf
ICII
Zhiyong Cao, Xiaobo Cai, Haipeng Li, Wei Huang, Xiujuan Yang, Linli Tao, Xi Zhang, Huihui Wang
2019 J jnl
IEEE Access
Peng Xie, Guangming Wang, Haipeng Li, Xiangjun Gao
2019 conf
CNS
Sean Kennedy, Haipeng Li, Chenggang Wang, Hao Liu, Boyang Wang, Wenhai Sun
2018 J jnl
Sensors
Qiusheng Wang, Haipeng Li, Jinyong Lin, Chunxia Zhang
2018 conf
ICVISP
Haipeng Li, Cuie Zheng, Jucheng Zhang
2018 J jnl
Wirel. Commun. Mob. Comput.
Haipeng Li, Zhen Xu
2018 conf
ICC Workshops
Haipeng Li, Zhen Xu
2017 conf
5GWN
Guixiang Jiang, Rong Chai, Haipeng Li
2017 J jnl
Sci. China Inf. Sci.
Rong Chai, Haipeng Li, Feiying Meng, Qianbin Chen
2017 J jnl
Future Internet
Haipeng Li, Hidenori Nakazato, Syed Hassan Ahmed
2016 conf
WPMC
Haipeng Li, Guixiang Jiang, Rong Chai
2016 J jnl
Quant. Biol.
Jinggong Xiang-Yu, Zongfeng Yang, Kun Tang, Haipeng Li
2016 J jnl
IEICE Trans. Commun.
Haipeng Li, Hidenori Nakazato
2015 conf
ChinaCom
Xun Hu, Rong Chai, Guixiang Jiang, Haipeng Li
2015 conf
EuCNC
Haipeng Li, Hidenori Nakazato, Andrea Detti, Nicola Blefari-Melazzi
2015 J jnl
计算机科学
Haipeng Li, Jingjiao Li, Aiyun Yan, Aixia Wang, Jiao Wang
2014 J jnl
J. Multim.
Shuai Liu, Mengxi Liu, Qi Jia, Lingyun Qi, Haipeng Li
2014 A conf
IROS
Dengpeng Xing, De Xu, Haipeng Li
2014 A* conf
ICRA
Dengpeng Xing, De Xu, Haipeng Li, Liyan Luo
2013 J jnl
PLoS Comput. Biol.
Haipeng Li, Thomas Wiehe
2013 conf
BIC-TA
Haipeng Li, Xu Ji, Jianbo Lu
2013 J jnl
Int. J. Comput. Appl. Technol.
Jingjiao Li, Zhen-lin Lu, Jing Wang, Haipeng Li
2011 conf
CyberC
Jianbo Lu, Guanzhong Dai, Dejun Mu, Jiang Yu, Haipeng Li
2010 Misc conf
ICNC
Haipeng Li, Jingjiao Li
2009 conf
CSIE (7)
Lei Song, Tianfang Liu, Jiahui Huang, Haipeng Li, Xiaojun Zhang
tests/unit/test_apk_dataclasses.py
← Index tests/unit/test_apk_dataclasses.py python
"""
Unit tests for APK dataclasses in redb/models/dataclasses.py.
"""
import pytest
from dataclasses import asdict, fields

pytestmark = [pytest.mark.unit, pytest.mark.dataclass, pytest.mark.apk]


# ============================================================================
# APKFeatures
# ============================================================================

class TestAPKFeaturesDataclass:
    """Tests for APKFeatures dataclass."""

    def test_named_fields(self):
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures(
            package_name="com.example.app",
            app_name="Example App",
            version_code=1,
            version_name="1.0.0",
        )
        assert f.package_name == "com.example.app"
        assert f.app_name == "Example App"
        assert f.version_code == 1
        assert f.version_name == "1.0.0"

    def test_all_fields_default_none_or_safe(self):
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures()
        # Nullable fields default to None
        assert f.package_name is None
        assert f.app_name is None
        assert f.version_code is None
        assert f.version_name is None
        assert f.min_sdk_version is None
        assert f.target_sdk_version is None
        assert f.compile_sdk_version is None
        assert f.main_activity is None
        # Non-nullable fields have safe defaults
        assert f.is_debuggable is False
        assert f.allow_backup is True
        assert f.uses_cleartext_traffic is False
        assert f.supported_abis == []
        assert f.dex_count == 0
        assert f.total_dex_size == 0
        assert f.total_file_count == 0
        assert f.has_native_code is False
        assert f.has_assets is False
        assert f.uses_libraries == []
        assert f.earliest_content_modification is None
        assert f.latest_content_modification is None
        assert f.contains_embedded_apk is False

    def test_all_fields_populated(self):
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures(
            package_name="com.malware.test",
            app_name="Malware",
            version_code=42,
            version_name="6.6.6",
            min_sdk_version=21,
            target_sdk_version=33,
            compile_sdk_version=33,
            main_activity="com.malware.test.MainActivity",
            is_debuggable=True,
            allow_backup=False,
            uses_cleartext_traffic=True,
            supported_abis=["armeabi-v7a", "arm64-v8a", "x86"],
            dex_count=3,
            total_dex_size=5000000,
            total_file_count=200,
            has_native_code=True,
            has_assets=True,
            uses_libraries=["com.google.android.maps"],
            earliest_content_modification="2024-01-01T00:00:00Z",
            latest_content_modification="2024-06-15T12:00:00Z",
            contains_embedded_apk=True,
        )
        assert f.is_debuggable is True
        assert f.dex_count == 3
        assert len(f.supported_abis) == 3
        assert f.contains_embedded_apk is True

    def test_asdict_roundtrip(self):
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures(
            package_name="com.test",
            app_name="Test",
            version_code=1,
            version_name="1.0",
        )
        d = asdict(f)
        assert d["package_name"] == "com.test"
        assert d["is_debuggable"] is False
        assert d["supported_abis"] == []

    def test_partial_extraction(self):
        """Verify that APKFeatures can be created with only some fields populated,
        simulating a partial extraction where some fields failed."""
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures(
            package_name="com.example.app",
            # app_name failed to extract — left as None
            version_code=10,
            # version_name failed to extract — left as None
            is_debuggable=True,
            dex_count=2,
        )
        assert f.package_name == "com.example.app"
        assert f.app_name is None
        assert f.version_code == 10
        assert f.version_name is None
        assert f.is_debuggable is True
        assert f.dex_count == 2


# ============================================================================
# APKManifestComponent
# ============================================================================

class TestAPKManifestComponentDataclass:

    def test_creation(self):
        from redb.models.dataclasses import APKManifestComponent

        c = APKManifestComponent(
            component_type="activity",
            class_name="com.example.MainActivity",
            is_exported=True,
            intent_actions=["android.intent.action.MAIN"],
            intent_categories=["android.intent.category.LAUNCHER"],
        )
        assert c.component_type == "activity"
        assert c.class_name == "com.example.MainActivity"
        assert c.is_exported is True
        assert len(c.intent_actions) == 1
        assert len(c.intent_categories) == 1

    def test_defaults(self):
        from redb.models.dataclasses import APKManifestComponent

        c = APKManifestComponent(
            component_type="service",
            class_name="com.example.MyService",
            is_exported=False,
        )
        assert c.intent_actions == []
        assert c.intent_categories == []


# ============================================================================
# APKManifest
# ============================================================================

class TestAPKManifestDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKManifest

        m = APKManifest(
            activity_count=5,
            service_count=2,
            receiver_count=3,
            provider_count=1,
        )
        assert m.activity_count == 5
        assert m.service_count == 2
        assert m.receiver_count == 3
        assert m.provider_count == 1

    def test_optional_list_defaults(self):
        from redb.models.dataclasses import APKManifest

        m = APKManifest(
            activity_count=0,
            service_count=0,
            receiver_count=0,
            provider_count=0,
        )
        assert m.activities == []
        assert m.services == []
        assert m.receivers == []
        assert m.providers == []
        assert m.exported_components == []
        assert m.intent_filters_by_action == []
        assert m.intent_filters_by_category == []
        assert m.uses_features == []
        assert m.meta_data is None
        assert m.manifest_xml is None

    def test_fully_populated(self):
        from redb.models.dataclasses import APKManifest

        m = APKManifest(
            activity_count=2,
            service_count=1,
            receiver_count=1,
            provider_count=0,
            activities=["MainActivity", "SettingsActivity"],
            services=["SyncService"],
            receivers=["BootReceiver"],
            providers=[],
            exported_components=["MainActivity", "BootReceiver"],
            intent_filters_by_action=["android.intent.action.MAIN"],
            uses_features=["android.hardware.camera"],
            manifest_xml="<manifest>...</manifest>",
        )
        assert len(m.activities) == 2
        assert len(m.exported_components) == 2
        assert m.manifest_xml is not None


# ============================================================================
# APKPermission / APKPermissions
# ============================================================================

class TestAPKPermissionDataclass:

    def test_creation(self):
        from redb.models.dataclasses import APKPermission

        p = APKPermission(
            permission_name="android.permission.CAMERA",
            protection_level="dangerous",
        )
        assert p.permission_name == "android.permission.CAMERA"
        assert p.protection_level == "dangerous"
        assert p.is_custom is False

    def test_custom_permission(self):
        from redb.models.dataclasses import APKPermission

        p = APKPermission(
            permission_name="com.example.CUSTOM_PERM",
            protection_level="signature",
            is_custom=True,
        )
        assert p.is_custom is True


class TestAPKPermissionsDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKPermissions

        p = APKPermissions(
            total_permission_count=10,
            dangerous_permission_count=3,
        )
        assert p.total_permission_count == 10
        assert p.dangerous_permission_count == 3

    def test_defaults(self):
        from redb.models.dataclasses import APKPermissions

        p = APKPermissions(
            total_permission_count=0,
            dangerous_permission_count=0,
        )
        assert p.permissions == []
        assert p.dangerous_permissions == []
        assert p.custom_permissions == []
        assert p.permission_details == []
        assert p.permhash is None

    def test_with_permission_details(self):
        from redb.models.dataclasses import APKPermission, APKPermissions

        details = [
            APKPermission("android.permission.INTERNET", "normal"),
            APKPermission("android.permission.CAMERA", "dangerous"),
        ]
        p = APKPermissions(
            total_permission_count=2,
            dangerous_permission_count=1,
            permissions=["android.permission.INTERNET", "android.permission.CAMERA"],
            dangerous_permissions=["android.permission.CAMERA"],
            permission_details=details,
            permhash="abc123def456",
        )
        assert len(p.permission_details) == 2
        assert p.permhash == "abc123def456"


# ============================================================================
# APKCertificate / APKCodeSigningInfo
# ============================================================================

class TestAPKCertificateDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKCertificate

        c = APKCertificate(
            subject="CN=Android Debug",
            issuer="CN=Android Debug",
            serial_number="1234567890",
            valid_from="2024-01-01",
            valid_to="2054-01-01",
            thumbprint_sha1="aabbccdd" * 5,
            thumbprint_sha256="aabbccdd" * 8,
        )
        assert c.subject == "CN=Android Debug"
        assert c.issuer == "CN=Android Debug"
        assert c.is_self_signed is False

    def test_optional_fields(self):
        from redb.models.dataclasses import APKCertificate

        c = APKCertificate(
            subject="CN=Test",
            issuer="CN=CA",
            serial_number="1",
            valid_from="2024-01-01",
            valid_to="2025-01-01",
            thumbprint_sha1="aa" * 20,
            thumbprint_sha256="bb" * 32,
            algorithm="SHA256withRSA",
            key_size=2048,
            is_self_signed=True,
        )
        assert c.algorithm == "SHA256withRSA"
        assert c.key_size == 2048
        assert c.is_self_signed is True


class TestAPKCodeSigningInfoDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKCodeSigningInfo

        info = APKCodeSigningInfo(
            _id="abcdef1234567890" * 4,
            is_signed=True,
        )
        assert info.is_signed is True
        assert info.signature_scheme_versions == []
        assert info.number_of_certificates == 0
        assert info.x509_certificates is None
        assert info.apk_signer_subject is None
        assert info.apk_signer_issuer is None

    def test_fully_populated(self):
        from redb.models.dataclasses import APKCertificate, APKCodeSigningInfo

        cert = APKCertificate(
            subject="CN=Test",
            issuer="CN=Test",
            serial_number="1",
            valid_from="2024-01-01",
            valid_to="2025-01-01",
            thumbprint_sha1="aa" * 20,
            thumbprint_sha256="bb" * 32,
        )
        info = APKCodeSigningInfo(
            _id="cc" * 32,
            is_signed=True,
            signature_scheme_versions=[1, 2, 3],
            number_of_certificates=1,
            x509_certificates=[cert],
            apk_signer_subject="CN=Test",
            apk_signer_issuer="CN=Test",
        )
        assert len(info.signature_scheme_versions) == 3
        assert info.number_of_certificates == 1
        assert len(info.x509_certificates) == 1


# ============================================================================
# APKDexFile
# ============================================================================

class TestAPKDexFileDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKDexFile

        d = APKDexFile(
            filename="classes.dex",
            sha256="aa" * 32,
            class_count=100,
            method_count=500,
            string_count=1000,
        )
        assert d.filename == "classes.dex"
        assert d.class_count == 100
        assert d.method_count == 500
        assert d.string_count == 1000

    def test_optional_fields_defaults(self):
        from redb.models.dataclasses import APKDexFile

        d = APKDexFile(
            filename="classes2.dex",
            sha256="bb" * 32,
            class_count=0,
            method_count=0,
            string_count=0,
        )
        assert d.tlsh is None
        assert d.top_packages is None
        assert d.api_usage is None
        assert d.obfuscation_indicators is None

    def test_tlsh_populated(self):
        from redb.models.dataclasses import APKDexFile

        tlsh_value = "T1" + "A" * 68
        d = APKDexFile(
            filename="classes.dex",
            sha256="aa" * 32,
            class_count=100,
            method_count=500,
            string_count=1000,
            tlsh=tlsh_value,
        )
        assert d.tlsh == tlsh_value

    def test_with_api_usage(self):
        from redb.models.dataclasses import APKDexFile

        d = APKDexFile(
            filename="classes.dex",
            sha256="cc" * 32,
            class_count=50,
            method_count=200,
            string_count=300,
            top_packages=[{"name": "com.example", "class_count": 30}],
            api_usage={
                "crypto": ["Ljavax/crypto/Cipher;->getInstance"],
                "reflection": ["Ljava/lang/Class;->forName"],
            },
            obfuscation_indicators={"short_class_names_ratio": 0.8, "single_char_methods_ratio": 0.5},
        )
        assert len(d.api_usage) == 2
        assert d.obfuscation_indicators["short_class_names_ratio"] == 0.8


# ============================================================================
# APKResource
# ============================================================================

class TestAPKResourceDataclass:

    def test_creation(self):
        from redb.models.dataclasses import APKResource

        r = APKResource(
            path="res/drawable/icon.png",
            size=1024,
            sha256="dd" * 32,
            filetype_magika="png",
        )
        assert r.path == "res/drawable/icon.png"
        assert r.size == 1024
        assert r.is_suspicious is False

    def test_suspicious_resource(self):
        from redb.models.dataclasses import APKResource

        r = APKResource(
            path="assets/payload.dex",
            size=50000,
            sha256="ee" * 32,
            filetype_magika="dex",
            is_suspicious=True,
        )
        assert r.is_suspicious is True


# ============================================================================
# APKNativeLib
# ============================================================================

class TestAPKNativeLibDataclass:

    def test_creation(self):
        from redb.models.dataclasses import APKNativeLib

        lib = APKNativeLib(
            abi="arm64-v8a",
            filename="libnative.so",
            size=200000,
            sha256="ff" * 32,
        )
        assert lib.abi == "arm64-v8a"
        assert lib.filename == "libnative.so"
        assert lib.is_known_packer is False

    def test_known_packer(self):
        from redb.models.dataclasses import APKNativeLib

        lib = APKNativeLib(
            abi="armeabi-v7a",
            filename="libjiagu.so",
            size=100000,
            sha256="aa" * 32,
            is_known_packer=True,
        )
        assert lib.is_known_packer is True


# ============================================================================
# APKInconsistencyTests
# ============================================================================

class TestAPKInconsistencyTestsDataclass:

    def test_all_defaults_none(self):
        from redb.models.dataclasses import APKInconsistencyTests

        t = APKInconsistencyTests()
        assert t.test_zip_bomb is None
        assert t.test_zip_duplicate_entries is None
        assert t.test_zip_path_traversal is None
        assert t.test_zip_suspicious_timestamps is None
        assert t.test_hidden_dex_files is None
        assert t.test_manifest_component_mismatch is None
        assert t.test_debuggable_release is None
        assert t.test_emulator_detection_strings is None
        assert t.test_debugger_detection is None
        assert t.test_root_detection is None

    def test_all_fields_populated(self):
        from redb.models.dataclasses import APKInconsistencyTests

        t = APKInconsistencyTests(
            test_zip_bomb=True,
            test_zip_duplicate_entries=False,
            test_zip_path_traversal=True,
            test_zip_suspicious_timestamps=False,
            test_hidden_dex_files=True,
            test_manifest_component_mismatch=False,
            test_debuggable_release=True,
            test_emulator_detection_strings=True,
            test_debugger_detection=False,
            test_root_detection=True,
        )
        assert t.test_zip_bomb is True
        assert t.test_debuggable_release is True
        # Count true values
        d = asdict(t)
        true_count = sum(1 for v in d.values() if v is True)
        assert true_count == 6

    def test_field_count(self):
        from redb.models.dataclasses import APKInconsistencyTests

        # Exactly 10 inconsistency tests per PDD specification
        assert len(fields(APKInconsistencyTests)) == 10


# ============================================================================
# Hashes dataclass — APK permhash field
# ============================================================================

class TestHashesAPKField:

    def test_permhash_default_none(self):
        from redb.models.dataclasses import Hashes

        h = Hashes(md5="a", sha1="b", sha256="c", ssdeep_hash="d", tlsh_hash="e")
        assert h.permhash is None

    def test_permhash_populated(self):
        from redb.models.dataclasses import Hashes

        h = Hashes(
            md5="a", sha1="b", sha256="c", ssdeep_hash="d", tlsh_hash="e",
            permhash="abc123" * 10 + "abcd",
        )
        assert h.permhash is not None
        assert len(h.permhash) == 64


# ============================================================================
# Tag enum — APK entries
# ============================================================================

class TestAPKTagEnum:

    def test_all_apk_tags_exist(self):
        from redb.extractors.enum import Tag

        expected_tags = [
            "APK_FEATURES", "APK_MANIFEST", "APK_PERMISSIONS", "APK_SIGNATURE",
            "APK_DEX", "APK_RESOURCES", "APK_NATIVE_LIBS", "APK_INCONSISTENCY_TESTS",
        ]
        for name in expected_tags:
            assert hasattr(Tag, name), f"Tag.{name} missing"

    def test_apk_tag_values(self):
        from redb.extractors.enum import Tag

        assert Tag.APK_FEATURES.value == "apk_features"
        assert Tag.APK_MANIFEST.value == "apk_manifest"
        assert Tag.APK_PERMISSIONS.value == "apk_permissions"
        assert Tag.APK_SIGNATURE.value == "apk_signature"
        assert Tag.APK_DEX.value == "apk_dex"
        assert Tag.APK_RESOURCES.value == "apk_resources"
        assert Tag.APK_NATIVE_LIBS.value == "apk_native_libs"
        assert Tag.APK_INCONSISTENCY_TESTS.value == "apk_inconsistency_tests"