Xiaojun Zhu

75 papers A* 4B 14C 4Journal 35Unranked 18
YearRankTypeTitle / Venue / Authors
2026 J jnl
IEEE Trans. Veh. Technol.
Anrun Wang, Xiaojun Zhu, Yuben Qu, Chao Dong
2025 conf
ICC
Yu Zhu, Kefeng Guo, Chao Dong, Xiaojun Zhu, Qingnan Sun, Sunder Ali Khowaja
2025 J jnl
Ad Hoc Networks
Siyue Zheng, Xiaojun Zhu, Zhengrui Qin, Chao Dong
2025 conf
VTC2025-Spring
Shujun Zhao, Simeng Feng, Chao Dong, Xiaojun Zhu, Qihui Wu
2025 J jnl
IEEE Internet Things J.
Chao Dong, Ying Wang, Wei Wang, Xiaojun Zhu, Min Zhang, Qihui Wu
2025 J jnl
ACM Trans. Sens. Networks
Jiajing Wu, Xiaojun Zhu, Chao Dong, Zhengrui Qin, Fuhui Zhou
2025 conf
MeditCom
Ruyan Zhang, Yuben Qu, Xiaojun Zhu, Jianchao Zheng, Chao Dong
2024 conf
NPC (2)
Jia Chen, Xiaojun Zhu, Chao Dong
2024 B conf
WCNC
Jiajing Wu, Chao Dong, Xiaojun Zhu, Zhengrui Qin
2024 conf
VTC Fall
Jinyao Sun, Chao Dong, Xiaojun Zhu, Zhengrui Qin, Lei Zhang
2024 J jnl
IEEE Netw. Lett.
Xiaojun Zhu, Jiao Tang
2024 B conf
WCNC
Ziming Zeng, Chao Dong, Jiajing Wu, Xiaojun Zhu, Lei Zhang
2024 J jnl
J. Supercomput.
Xiaojun Zhu, Xiaowei Zhao, Chao Dong
2024 J jnl
Wirel. Networks
Xiaojun Zhu, Hao Han
2024 J jnl
IEEE Trans. Mob. Comput.
Hao Han, Kunming Xie, Tongyu Wang, Xiaojun Zhu, Yanchao Zhao, Fengyuan Xu
2024 conf
VTC Spring
Qingnan Sun, Lei Zhang, Xiaojun Zhu, Chao Dong
2023 C conf
CSCWD
Qingxin Liu, Xiaojun Zhu, Chuanxin Zhou, Chao Dong
2023 J jnl
ACM Trans. Sens. Networks
Xiaojun Zhu, Zhouqing Han, Shaojie Tang, Lijie Xu, Chao Dong
2023 C conf
CSCWD
Junkai Fei, Xiaojun Zhu, Ruyan Zhang
2023 conf
ISPA/BDCloud/SocialCom/SustainCom
Ziming Zeng, Chao Dong, Xiaojun Zhu, Lei Zhang
2023 J jnl
J. Supercomput.
Ji'ao Tang, Xiaojun Zhu, Lu Lin, Chao Dong, Lei Zhang
2023 B conf
IFIP Networking
Lu Lin, Xiaojun Zhu, Ji'ao Tang, Chao Dong
2023 B conf
ICPADS
Siyue Zheng, Xiaojun Zhu, Zhenrui Qin, Chao Dong
2023 conf
MSN
Huiping Qian, Hao Han, Xiaojun Zhu, Fengyuan Xu
2022 B conf
ICPADS
Yifan Zhu, Xiaojun Zhu
2022 J jnl
IEEE Internet Things J.
Xiaojun Zhu, Youpeng Wang, Lijie Xu
2022 conf
ISPA/BDCloud/SocialCom/SustainCom
Chuanxin Zhou, Chao Dong, Xiaojun Zhu, Lei Zhang
2022 conf
HPCC/DSS/SmartCity/DependSys
Ji'ao Tang, Xiaojun Zhu, Chao Dong, Lei Zhang
2022 B conf
GLOBECOM
Shijie Liu, Chao Dong, Xiaojun Zhu, Ji'ao Tang, Lei Zhang
2022 J jnl
J. Supercomput.
Xiaojun Zhu, Yu Wang, Zhengrui Qin
2021 J jnl
INFORMS J. Comput.
Xiaojun Zhu, Shaojie Tang
2021 conf
WASA (1)
Yifan Zhu, Xiaojun Zhu, Cheng Qian
2021 B conf
IWQoS
Yu Wang, Xiaojun Zhu, Hao Han
2021 J jnl
INFORMS J. Comput.
Xiaojun Zhu, Shaojie Tang
2021 J jnl
IEEE Internet Things J.
Wenjing You, Chao Dong, Xiao Cheng, Xiaojun Zhu, Qihui Wu, Guihai Chen
2021 conf
WASA (2)
Jia Yang, Kun Zhu, Xiaojun Zhu, Junhua Wang
2021 B conf
ICPADS
Xuemeng Li, Xiaojun Zhu, Chao Dong
2021 J jnl
J. Supercomput.
Xiaojun Zhu
2021 conf
WCSP
Ting Tao, Chao Dong, Xiaojun Zhu, Rong He
2020 J jnl
IEEE Access
Yao Ji, Chao Dong, Xiaojun Zhu, Qihui Wu
2020 B conf
WCNC
Youpeng Wang, Xiaojun Zhu, Lijie Xu
2020 J jnl
J. Comput. Sci. Technol.
Xiaojun Zhu, Lijie Xu, Xiaobing Wu, Bing Chen
2020 B conf
ICPADS
Zhouqing Han, Xiaojun Zhu, Lijie Xu
2020 conf
MSN
Junchao Gong, Xiaojun Zhu, Lijie Xu
2019 conf
ICC
Yao Ji, Chao Dong, Xiaojun Zhu, Qihui Wu
2019 J jnl
J. Supercomput.
Xiaojun Zhu, Bing Chen, Muhui Shen, Yanchao Zhao
2019 B conf
ICCCN
Li Zhang, Xiaojun Zhu, Xiaobing Wu
2019 J jnl
IEEE/ACM Trans. Netw.
Jiaqi Zheng, Hong Xu, Xiaojun Zhu, Guihai Chen, Yanhui Geng
2018 B conf
ICPADS
Hui Zhong, Xiaojun Zhu, Bing Chen, Shiqing Shen
2018 J jnl
Wirel. Commun. Mob. Comput.
Pengyuan Cao, Xiaojun Zhu
2017 B conf
WCNC
Xinshu Ma, Xiaojun Zhu, Bing Chen
2017 J jnl
IEEE Trans. Mob. Comput.
Xiaojun Zhu, Fengyuan Xu, Edmund Novak, Chiu C. Tan, Qun Li, Guihai Chen
2016 J jnl
Comput. Commun.
Pan Wu, Xiaobing Wu, Guihai Chen, Mengfan Shan, Xiaojun Zhu
2016 J jnl
INFORMS J. Comput.
Xiaojun Zhu, Guihai Chen, Shaojie Tang, Xiaobing Wu, Bing Chen
2016 conf
Trustcom/BigDataSE/ISPA
Feng Hu, Bing Chen, Xiaojun Zhu, Hong Shen
2016 J jnl
Comput. Commun.
Lijie Xu, Xiaojun Zhu, Haipeng Dai, Xiaobing Wu, Guihai Chen
2016 C conf
ISCC
Muhui Shen, Bing Chen, Xiaojun Zhu, Yanchao Zhao
2016 B conf
ICNP
Jiaqi Zheng, Hong Xu, Xiaojun Zhu, Guihai Chen, Yanhui Geng
2015 J jnl
Wirel. Networks
Xiaojun Zhu, Xiaobing Wu, Guihai Chen
2015 J jnl
Comput. Commun.
Pan Wu, Guihai Chen, Xiaojun Zhu, Xiaobing Wu
2014 J jnl
ACM Trans. Sens. Networks
Mengfan Shan, Guihai Chen, Dijun Luo, Xiaojun Zhu, Xiaobing Wu
2014 J jnl
IEEE/ACM Trans. Netw.
Yifan Zhang, Gexin Yu, Qun Li, Haodong Wang, Xiaojun Zhu, Baosheng Wang
2014 A* conf
INFOCOM
Wei Wei, Xiaojun Zhu, Qun Li
2014 J jnl
J. Parallel Distributed Comput.
Xiaojun Zhu, Qun Li, Weizhen Mao, Guihai Chen
2014 conf
ICC
Yafei Mao, Chao Dong, Haipeng Dai, Xiaojun Zhu, Guihai Chen
2013 A* conf
INFOCOM
Xiaojun Zhu, Qun Li, Guihai Chen
2013 A* conf
INFOCOM
Xiaojun Zhu, Fengyuan Xu, Edmund Novak, Chiu C. Tan, Qun Li, Guihai Chen
2013 J jnl
Comput. Commun.
Xiaojun Zhu, Xiaobing Wu, Guihai Chen
2013 J jnl
IEEE Trans. Parallel Distributed Syst.
Fengyuan Xu, Xiaojun Zhu, Chiu C. Tan, Qun Li, Guanhua Yan, Jie Wu
2012 J jnl
Int. J. Sens. Networks
Huda El Hag Mustafa, Xiaojun Zhu, Qun Li, Guihai Chen
2012 conf
ICC
Lizhao You, Xiaojun Zhu, Guihai Chen
2012 J jnl
CoRR
Xiaojun Zhu, Qun Li, Weizhen Mao, Guihai Chen
2012 J jnl
Int. J. Sens. Networks
Xiaojun Zhu, Xiaobing Wu, Guihai Chen
2011 A* conf
INFOCOM
Dijun Luo, Xiaojun Zhu, Xiaobing Wu, Guihai Chen
2011 C conf
ISPA
Xiaojun Zhu, Guihai Chen
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"