Karola Marky

98 papers A* 17A 3B 15Misc 8Journal 21Unranked 28
YearRankTypeTitle / Venue / Authors
2026 ed.
FC (Workshops 1)
Bernhard Haslhofer, Java Xu, Friedhelm Victor, Massimo Bartoletti, Andrea Bracciali, Kanta Matsuura, Jarek Nabrzyski, Vero Estrada-Galiñanes, Claudio J. Tessone, Jurlind Budurushi, Karola Marky
2026 A* conf
CHI
Priyasha Chatterjee, Smirity Kaushik, Karola Marky, Yixin Zou
2026 A* conf
CHI
Kanyu Chen, Rebecca Panskus, Erwin Wu, Yichen Peng, Daichi Saito, Emiko Kamiyama, Ruiteng Li, Chen-Chieh Liao, Karola Marky, Kato Akira, Hideki Koike, Kai Kunze
2026 J jnl
CoRR
Kanyu Chen, Rebecca Panskus, Erwin Wu, Yichen Peng, Daichi Saito, Emiko Kamiyama, Ruiteng Li, Chen-Chieh Liao, Karola Marky, Akira Kato, Hideki Koike, Kai Kunze
2025 J jnl
Proc. Priv. Enhancing Technol.
Sarah Abdelwahab Gaballah, Lamya Abdullah, Ephraim Zimmer, Sascha Fahl, Max Mühlhäuser, Karola Marky
2025 B conf
MUM
Habiba Farzand, Shaun Alexander Macdonald, Karola Marky, Mohamed Khamis
2025 J jnl
Proc. Priv. Enhancing Technol.
Noé Zufferey, Sarah Abdelwahab Gaballah, Karola Marky, Verena Zimmermann
2025 A* conf
CHI
Jan-Ulrich Holtgrave, Sabrina Klivan, Karola Marky, Sascha Fahl
2025 J jnl
Int. J. Hum. Comput. Interact.
Habiba Farzand, Melvin Abraham, Stephen A. Brewster, Mohamed Khamis, Karola Marky
2025 J jnl
ACM Trans. Comput. Hum. Interact.
Verena Zimmermann, Stina Schäfer, Markus Dürmuth, Karola Marky
2025 B conf
MUM
Lena Swienty, Rebecca Panskus, Stefanie Miketta, Beyza Kircili, Sen Cheng, Karola Marky
2025 Misc conf
MuC
Christopher Katins, Katja Chen, Christopher Lazik, Pascal Knierim, Karola Marky, Thomas Kosch
2025 A* conf
CHI
Felix Reichmann, Jens Christian Opdenbusch, Karola Marky, Marco Gutfleisch
2025 A* conf
CHI
Sarah Delgado Rodriguez, Maximiliane Windl, Florian Alt, Karola Marky
2025 B conf
VRST
Reyhan Düzgün, Philip Klostermeyer, Lena Swienty, Sascha Fahl, Karola Marky
2024 J jnl
CoRR
Habiba Farzand, Shaun Alexander Macdonald, Karola Marky, Mohamed Khamis
2024 J jnl
Proc. Priv. Enhancing Technol.
Sarah Abdelwahab Gaballah, Lamya Abdullah, Mina Alishahi, Thanh Hoang Long Nguyen, Ephraim Zimmer, Max Mühlhäuser, Karola Marky
2024 conf
CHI Extended Abstracts
Habiba Farzand, David Al Baiaty Suarez, Thomas Goodge, Shaun Alexander Macdonald, Karola Marky, Mohamed Khamis, Paul A. Cairns
2024 A* conf
CHI
Karola Marky, Alina Stöver, Sarah Prange, Kira Bleck, Paul Gerber, Verena Zimmermann, Florian Müller, Florian Alt, Max Mühlhäuser
2024 A* conf
CHI
Sarah Delgado Rodriguez, Priyasha Chatterjee, Anh Dao Phuong, Florian Alt, Karola Marky
2024 A* conf
SP
Sabrina Amft, Sandra Höltervennhoff, Rebecca Panskus, Karola Marky, Sascha Fahl
2024 Misc conf
NordiCHI
Sara Hahn, Nusaibah Al-Amrani, Sara Allagah, He Rong Huang, Eva Kircher, Cornelia Rehs, Joachim Vogt, Karola Marky, Nina Gerber
2024 conf
AHs
Thomas Kosch, Andreas Weiß, Jordan Aiko Deja, Snehesh Shrestha, Matthias Hoppe, Andrii Matviienko, Karola Marky
2024 A* conf
SP
Karola Marky, Nina Gerber, Henry John Krumb, Mohamed Khamis, Max Mühlhäuser
2024 B conf
MUM
Rebecca Panskus, Tangila Islam Tanni, Alexander Ponticello, Echo Meissner, Yan Solihin, Katharina Krombholz, Karola Marky
2024 B conf
ARES
Sarah Abdelwahab Gaballah, Lamya Abdullah, Max Mühlhäuser, Karola Marky
2024 A* conf
CHI
Habiba Farzand, Karola Marky, Mohamed Khamis
2024 B conf
MUM
Mohamed Khamis, Rebecca Panskus, Habiba Farzand, Marija Mumm, Shaun Alexander Macdonald, Karola Marky
2024 ed.
AHs
Anusha Withana, Mark Billinghurst, Karola Marky, Zhanna Sarsenbayeva, Don Samitha Elvitigala, Benjamin Tag, Steeven Villa, Yun Suen Pai
2024 A* conf
CCS
Jonas Hielscher, Markus Schöps, Jens Opdenbusch, Felix Reichmann, Marco Gutfleisch, Karola Marky, Simon Parkin
2024 J jnl
CoRR
Habiba Farzand, Karola Marky, Mohamed Khamis
2024 B conf
RO-MAN
Jacqueline Borgstedt, Shaun Alexander Macdonald, Karola Marky, Frank E. Pollick, Stephen A. Brewster
2024 J jnl
CoRR
Jacqueline Borgstedt, Shaun Alexander Macdonald, Karola Marky, Frank E. Pollick, Stephen A. Brewster
2023 J jnl
Behav. Inf. Technol.
Verena Zimmermann, Karola Marky, Karen Renaud
2023 A* conf
USENIX Security Symposium
Karola Marky, Shaun Alexander Macdonald, Yasmeen Abdrabou, Mohamed Khamis
2023 conf
AHs
Jordan Aiko Deja, Bettina Eska, Snehesh Shrestha, Matthias Hoppe, Jakob Karolus, Thomas Kosch, Andrii Matviienko, Andreas Weiß, Karola Marky
2023 J jnl
ACM Trans. Comput. Hum. Interact.
Jiawen Han, George Chernyshov, Moe Sugawa, Dingding Zheng, Danny Hynds, Taichi Furukawa, Marcelo Padovani Macieira, Karola Marky, Kouta Minamizawa, Jamie A. Ward, Kai Kunze
2023 A conf
SOUPS
Rebecca Panskus, Max Ninow, Sascha Fahl, Karola Marky
2023 A conf
SOUPS
Mark Turner, Martin Schmitz, Morgan Masichi Bierey, Mohamed Khamis, Karola Marky
2022 J jnl
ACM Trans. Comput. Hum. Interact.
Karola Marky, Kirill Ragozin, George Chernyshov, Andrii Matviienko, Martin Schmitz, Max Mühlhäuser, Chloe Eghtebas, Kai Kunze
2022 J jnl
Proc. Priv. Enhancing Technol.
Karola Marky, Nina Gerber, Michelle Gabriela Pelzer, Mohamed Khamis, Max Mühlhäuser
2022 ed.
AHs
Masaaki Mochimaru, Yuichi Kurita, Martin Schmitz, Karola Marky
2022 conf
IUI Companion
Norah Mohsen T. Alotaibi, Md Shafiqul Islam, Karola Marky, Mohamed Khamis
2022 Misc conf
NordiCHI
Paul Bekaert, Norah Mohsen T. Alotaibi, Florian Mathis, Nina Gerber, Aidan Christopher Rafferty, Mohamed Khamis, Karola Marky
2022 B conf
AVI
Mohamed Khamis, Habiba Farzand, Marija Mumm, Karola Marky
2022 B conf
MUM
Sarah Delgado Rodriguez, Sarah Prange, Pascal Knierim, Karola Marky, Florian Alt
2022 conf
AHs
Kirill Ragozin, Karola Marky, Jie Lu, Kai Kunze
2022 conf
CHI Extended Abstracts
Karola Marky, Annika Kilian, Andreas Weiß, Jakob Karolus, Matthias Hoppe, Pawel W. Wozniak, Max Mühlhäuser, Thomas Kosch
2022 A* conf
USENIX Security Symposium
Karola Marky, Paul Gerber, Sebastian Günther, Mohamed Khamis, Maximilian Fries, Max Mühlhäuser
2022 ed.
MuC (Workshopband)
Karola Marky, Uwe Grünefeld, Thomas Kosch
2022 B conf
MUM
Daniel Kirkwood, Cagdas Tombul, Calum Firth, Finn Macdonald, Konstantinos Priftis, Florian Mathis, Mohamed Khamis, Karola Marky
2022 Misc conf
NordiCHI
Sarah Delgado Rodriguez, Sarah Prange, Christina Vergara Ossenberg, Markus Henkel, Florian Alt, Karola Marky
2022 J jnl
Proc. ACM Interact. Mob. Wearable Ubiquitous Technol.
Joseph O'Hagan, Pejman Saeghe, Jan Gugenheimer, Daniel Medeiros, Karola Marky, Mohamed Khamis, Mark McGill
2022 conf
ISS Companion
Martin Schmitz, Sebastian Günther, Karola Marky, Florian Müller, Andrii Matviienko, Alexandra Voit, Roberts Marky, Max Mühlhäuser, Thomas Kosch
2022 conf
EuroUSEC
Habiba Farzand, Karola Marky, Mohamed Khamis
2022 conf
SOUPS @ USENIX Security Symposium
Nina Gerber, Karola Marky
2022 J jnl
Behav. Inf. Technol.
Mohamed Khamis, Karola Marky, Andreas Bulling, Florian Alt
2022 conf
MuC (Workshopband)
Alina Stöver, Nina Gerber, Christin Cornel, Mona Henz, Karola Marky, Verena Zimmermann, Joachim Vogt
2021 conf
E-VOTE-ID
Marie-Laure Zollinger, Ehsan Estaji, Peter Y. A. Ryan, Karola Marky
2021 conf
AHs
Zhuoqi Fu, Jiawen Han, Dingding Zheng, Moe Sugawa, Taichi Furukawa, George Chernyshov, Danny Hynds, Marcelo Padovani, Karola Marky, Kouta Minamizawa, Jamie A. Ward, Kai Kunze
2021 B conf
TEI
Moe Sugawa, Taichi Furukawa, George Chernyshov, Danny Hynds, Jiawen Han, Marcelo Padovani, Dingding Zheng, Karola Marky, Kai Kunze, Kouta Minamizawa
2021 conf
MobileHCI (Extended Abstracts)
Karola Marky, Julian Fischer, Max Mühlhäuser, Andrii Matviienko
2021 conf
CHI Extended Abstracts
Alina Stöver, Nina Gerber, Sushma Kaushik, Max Mühlhäuser, Karola Marky
2021 J jnl
ACM Trans. Comput. Hum. Interact.
Karola Marky, Marie-Laure Zollinger, Peter B. Roenne, Peter Y. A. Ryan, Tim Grube, Kai Kunze
2021 A* conf
CHI
Karola Marky, Andreas Weiß, Andrii Matviienko, Florian Brandherm, Sebastian Wolf, Martin Schmitz, Florian Krell, Florian Müller, Max Mühlhäuser, Thomas Kosch
2021 conf
CHI Extended Abstracts
Karola Marky, Andreas Weiß, Florian Müller, Martin Schmitz, Max Mühlhäuser, Thomas Kosch
2021
Karola Marky
2021 B conf
MUM
Karola Marky, Sarah Prange, Max Mühlhäuser, Florian Alt
2021 J jnl
CoRR
Karola Marky, Andreas Weiß, Thomas Kosch
2021 Misc conf
MuC
Habiba Farzand, Kinshuk Bhardwaj, Karola Marky, Mohamed Khamis
2020 Misc conf
NordiCHI
Karola Marky, Alexandra Voit, Alina Stöver, Kai Kunze, Svenja Schröder, Max Mühlhäuser
2020 B conf
MUM
Karola Marky, Sarah Prange, Florian Krell, Max Mühlhäuser, Florian Alt
2020 A* conf
CHI
Karola Marky, Martin Schmitz, Verena Zimmermann, Martin Herbers, Kai Kunze, Max Mühlhäuser
2020 conf
CHI Extended Abstracts
Karola Marky, Verena Zimmermann, Alina Stöver, Philipp Pascal Hoffmann, Kai Kunze, Max Mühlhäuser
2020 conf
AHs
Erik Pescara, Florian Dreschner, Karola Marky, Kai Kunze, Michael Beigl
2020 A* conf
CHI
Karola Marky, Verena Zimmermann, Markus Funk, Jörg Daubert, Kira Bleck, Max Mühlhäuser
2020 J jnl
CoRR
Karola Marky, Andreas Weiß, Julien Gedeon, Sebastian Günther
2020 conf
CHI Extended Abstracts
Kirill Ragozin, Kai Kunze, Karola Marky, Yun Suen Pai
2020 ch.
Ausgezeichnete Informatikdissertationen
Karola Marky
2020 B conf
VRST
Hesham Elsayed, Mayra Donaji Barrera Machuca, Christian Schaarschmidt, Karola Marky, Florian Müller, Jan Riemann, Andrii Matviienko, Martin Schmitz, Martin Weigel, Max Mühlhäuser
2020 J jnl
Proc. ACM Interact. Mob. Wearable Ubiquitous Technol.
Hesham Elsayed, Martin Weigel, Florian Müller, Martin Schmitz, Karola Marky, Sebastian Günther, Jan Riemann, Max Mühlhäuser
2020 conf
MuC (Workshopband)
Alina Stöver, Felix Kretschmer, Christin Cornel, Karola Marky
2019 J jnl
i-com
Verena Zimmermann, Paul Gerber, Karola Marky, Leon Böck, Florian Kirchbuchner
2019 conf
Financial Cryptography Workshops
Karola Marky, Marie-Laure Zollinger, Markus Funk, Peter Y. A. Ryan, Max Mühlhäuser
2019 conf
CHI Extended Abstracts
Markus Funk, Karola Marky, Iori Mizutani, Mareike Kritzler, Simon Mayer, Florian Michahelles
2019 conf
MuC (Workshopband)
Karola Marky, Andreas Weiß, Thomas Kosch
2019 Misc conf
MuC
Karola Marky, Florian Müller, Markus Funk, Alexander Geiß, Sebastian Günther, Martin Schmitz, Jan Riemann, Max Mühlhäuser
2019 conf
CHI Extended Abstracts
Karola Marky, Martin Schmitz, Felix Lange, Max Mühlhäuser
2019 A conf
Conference on Designing Interactive Systems
Julius von Willich, Markus Funk, Florian Müller, Karola Marky, Jan Riemann, Max Mühlhäuser
2018 conf
UbiComp/ISWC Adjunct
Karola Marky, Peter Mayer, Nina Gerber, Verena Zimmermann
2018 conf
Sicherheit
Karola Marky, Oksana Kulyk, Melanie Volkamer
2018 conf
MuC (Workshopband)
Katrin Wolf, Karola Marky, Markus Funk
2018 A* conf
CHI
Karola Marky, Oksana Kulyk, Karen Renaud, Melanie Volkamer
2017 J jnl
Comput. Secur.
Oksana Kulyk, Stephan Neumann, Karola Marky, Jurlind Budurushi, Melanie Volkamer
2017 conf
Financial Cryptography Workshops
Oksana Kulyk, Stephan Neumann, Karola Marky, Melanie Volkamer
2016 Misc conf
SEC
Oksana Kulyk, Stephan Neumann, Karola Marky, Jurlind Budurushi, Melanie Volkamer
2016 B conf
ARES
Oksana Kulyk, Karola Marky, Stephan Neumann, Melanie Volkamer
2016 conf
IMPS@ESSoS
Karola Marky, Andreas Gutmann, Philipp Rack, Melanie Volkamer
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"