← Files Arabic DOCX RTLARCHIVED FILE

tests/test_plugin_submission.py

11.2 KB · Oct 3, 2026 · 06:33 UTC

↓ Download file

from __future__ import annotations

import importlib.util
import io
import json
import struct
import tempfile
import unittest
import zlib
from pathlib import Path

from PIL import Image


REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
CHECKER_PATH = REPOSITORY_ROOT / "scripts" / "check_plugin_submission.py"


def load_checker():
    if not CHECKER_PATH.is_file():
        raise AssertionError(f"submission checker is missing: {CHECKER_PATH}")
    spec = importlib.util.spec_from_file_location("plugin_submission_checker", CHECKER_PATH)
    if spec is None or spec.loader is None:
        raise AssertionError("could not load plugin submission checker")
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


class PluginSubmissionCheckerTests(unittest.TestCase):
    def setUp(self) -> None:
        self.temp_dir = tempfile.TemporaryDirectory()
        self.addCleanup(self.temp_dir.cleanup)
        self.root = Path(self.temp_dir.name)
        self.write_clean_fixture()

    def write_clean_fixture(self) -> None:
        manifest = {
            "name": "arabic-word-production",
            "version": "0.2.0",
            "license": "Apache-2.0",
            "skills": "./skills/",
            "interface": {
                "displayName": "Arabic Word Production",
                "shortDescription": "Audited Arabic Word files",
                "longDescription": "Create, repair, and audit Arabic-first and bilingual Word documents with explicit RTL controls.",
                "websiteURL": "https://example.test/project",
                "privacyPolicyURL": "https://example.test/privacy",
                "termsOfServiceURL": "https://example.test/terms",
                "logo": "./assets/logo.png",
                "composerIcon": "./assets/icon.png",
                "defaultPrompt": [
                    "Create and audit an Arabic RTL Word document from this content.",
                    "Repair the RTL structure in this Arabic-English DOCX.",
                    "Audit this Arabic Word file and report its actual validation surface.",
                ],
            },
        }
        self.write_json(".codex-plugin/plugin.json", manifest)
        self.write_bytes("assets/logo.png", (REPOSITORY_ROOT / "assets" / "logo.png").read_bytes())
        self.write_bytes("assets/icon.png", (REPOSITORY_ROOT / "assets" / "icon.png").read_bytes())
        self.write_text("submission/listing.en.md", "Arabic Word Production\n")
        self.write_text("submission/listing.ar.md", "Arabic Word Production\n")
        self.write_text("submission/availability.md", "Available wherever the plugin directory is available.\n")
        self.write_text("submission/release-notes.md", "Initial directory submission.\n")
        cases = {"positive": [self.case("positive", number) for number in range(5)], "negative": [self.case("negative", number) for number in range(3)]}
        self.write_json("submission/reviewer-tests.json", cases)

    @staticmethod
    def case(kind: str, number: int) -> dict[str, str]:
        return {
            "id": f"{kind}-{number + 1}",
            "prompt": f"Synthetic {kind} case {number + 1}",
            "expected": "A safe, specific and truthful result.",
        }

    def write_text(self, relative: str, content: str) -> None:
        path = self.root / relative
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(content, encoding="utf-8")

    def write_bytes(self, relative: str, content: bytes) -> None:
        path = self.root / relative
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_bytes(content)

    @staticmethod
    def png_bytes(width: int, height: int) -> bytes:
        output = io.BytesIO()
        Image.new("1", (width, height), 0).save(output, format="PNG")
        return output.getvalue()

    def write_json(self, relative: str, content: object) -> None:
        self.write_text(relative, json.dumps(content, ensure_ascii=False, indent=2))

    def scan(self) -> dict[str, object]:
        return load_checker().scan_submission(self.root)

    def assert_category(self, result: dict[str, object], category: str) -> None:
        self.assertFalse(result["ok"])
        findings = result["findings"]
        self.assertIn(category, {item["category"] for item in findings})

    def test_clean_submission_passes(self) -> None:
        result = self.scan()
        self.assertTrue(result["ok"], result["findings"])

    def test_display_name_over_30_characters_is_rejected(self) -> None:
        manifest = self.read_manifest()
        manifest["interface"]["displayName"] = "A" * 31
        self.write_json(".codex-plugin/plugin.json", manifest)
        self.assert_category(self.scan(), "display-name-too-long")

    def test_short_description_over_30_characters_is_rejected(self) -> None:
        manifest = self.read_manifest()
        manifest["interface"]["shortDescription"] = "A" * 31
        self.write_json(".codex-plugin/plugin.json", manifest)
        self.assert_category(self.scan(), "short-description-too-long")

    def test_more_than_three_starter_prompts_is_rejected(self) -> None:
        manifest = self.read_manifest()
        manifest["interface"]["defaultPrompt"].append("Fourth prompt")
        self.write_json(".codex-plugin/plugin.json", manifest)
        self.assert_category(self.scan(), "starter-prompt-count")

    def test_starter_prompt_over_128_characters_is_rejected(self) -> None:
        manifest = self.read_manifest()
        manifest["interface"]["defaultPrompt"][0] = "A" * 129
        self.write_json(".codex-plugin/plugin.json", manifest)
        self.assert_category(self.scan(), "starter-prompt-too-long")

    def test_duplicate_starter_prompt_is_rejected(self) -> None:
        manifest = self.read_manifest()
        manifest["interface"]["defaultPrompt"][1] = manifest["interface"]["defaultPrompt"][0]
        self.write_json(".codex-plugin/plugin.json", manifest)
        self.assert_category(self.scan(), "starter-prompt-duplicate")

    def test_mention_in_starter_prompt_is_rejected(self) -> None:
        manifest = self.read_manifest()
        manifest["interface"]["defaultPrompt"][0] = "Ask @ArabicWordProduction to make a DOCX"
        self.write_json(".codex-plugin/plugin.json", manifest)
        self.assert_category(self.scan(), "starter-prompt-mention")

    def test_non_https_policy_url_is_rejected(self) -> None:
        manifest = self.read_manifest()
        manifest["interface"]["privacyPolicyURL"] = "http://example.test/privacy"
        self.write_json(".codex-plugin/plugin.json", manifest)
        self.assert_category(self.scan(), "url-not-https")

    def test_missing_referenced_asset_is_rejected(self) -> None:
        (self.root / "assets/logo.png").unlink()
        self.assert_category(self.scan(), "asset-missing")

    def test_non_square_declared_logo_is_rejected(self) -> None:
        self.write_bytes("assets/logo.png", self.png_bytes(1024, 512))
        self.assert_category(self.scan(), "asset-not-square")

    def test_corrupt_declared_logo_is_rejected(self) -> None:
        self.write_bytes("assets/logo.png", b"\x89PNG\r\n\x1a\ntruncated")
        self.assert_category(self.scan(), "asset-unreadable")

    def test_png_with_valid_crc_but_invalid_pixel_stream_is_rejected(self) -> None:
        valid_png = self.png_bytes(64, 64)
        corrupted = bytearray(valid_png[:8])
        offset = 8
        while offset < len(valid_png):
            length = struct.unpack(">I", valid_png[offset:offset + 4])[0]
            kind = valid_png[offset + 4:offset + 8]
            payload = valid_png[offset + 8:offset + 8 + length]
            if kind == b"IDAT":
                payload = b"invalid pixel stream"
            corrupted.extend(struct.pack(">I", len(payload)) + kind + payload)
            corrupted.extend(struct.pack(">I", zlib.crc32(kind + payload)))
            offset += length + 12
        self.write_bytes("assets/logo.png", bytes(corrupted))
        self.assert_category(self.scan(), "asset-unreadable")

    def test_declared_logo_below_minimum_dimensions_is_rejected(self) -> None:
        self.write_bytes("assets/logo.png", self.png_bytes(32, 32))
        self.assert_category(self.scan(), "asset-dimensions-out-of-range")

    def test_png_bad_idat_crc_is_reported_without_crashing(self) -> None:
        data = bytearray(self.png_bytes(64, 64))
        offset = 8
        while offset < len(data):
            length = struct.unpack(">I", data[offset:offset + 4])[0]
            if data[offset + 4:offset + 8] == b"IDAT":
                data[offset + 8 + length] ^= 1
                break
            offset += length + 12
        self.write_bytes("assets/logo.png", bytes(data))
        self.assert_category(self.scan(), "asset-unreadable")

    def test_png_decompression_bomb_is_reported_without_crashing(self) -> None:
        data = bytearray(self.png_bytes(64, 64))
        data[16:24] = struct.pack(">II", 20000, 20000)
        data[29:33] = struct.pack(">I", zlib.crc32(data[12:29]))
        self.write_bytes("assets/logo.png", bytes(data))
        self.assert_category(self.scan(), "asset-unreadable")

    def test_declared_logo_above_maximum_dimensions_is_rejected(self) -> None:
        self.write_bytes("assets/logo.png", self.png_bytes(4097, 4097))
        self.assert_category(self.scan(), "asset-dimensions-out-of-range")

    def test_declared_logo_over_five_mib_is_rejected(self) -> None:
        oversized_png = self.png_bytes(512, 512) + (b"\0" * (5 * 1024 * 1024))
        self.write_bytes("assets/logo.png", oversized_png)
        self.assert_category(self.scan(), "asset-too-large")

    def test_identical_logo_and_composer_icon_are_rejected(self) -> None:
        logo_bytes = (self.root / "assets/logo.png").read_bytes()
        self.write_bytes("assets/icon.png", logo_bytes)
        self.assert_category(self.scan(), "asset-duplicate")

    def test_reviewer_case_counts_must_be_five_positive_and_three_negative(self) -> None:
        self.write_json("submission/reviewer-tests.json", {"positive": [], "negative": []})
        result = self.scan()
        self.assert_category(result, "reviewer-positive-count")
        self.assert_category(result, "reviewer-negative-count")

    def test_invalid_reviewer_json_is_rejected(self) -> None:
        self.write_text("submission/reviewer-tests.json", "not json")
        self.assert_category(self.scan(), "reviewer-tests-invalid")

    def test_unfinished_placeholder_is_rejected(self) -> None:
        marker = "[" + "TODO" + ": write listing]"
        self.write_text("submission/listing.en.md", marker)
        self.assert_category(self.scan(), "unfinished-placeholder")

    def test_digital_commerce_language_is_rejected_without_echoing_text(self) -> None:
        forbidden = "Buy a paid subscription for this digital service"
        self.write_text("submission/listing.en.md", forbidden)
        result = self.scan()
        self.assert_category(result, "digital-commerce-language")
        self.assertNotIn(forbidden, json.dumps(result))

    def read_manifest(self) -> dict[str, object]:
        return json.loads((self.root / ".codex-plugin/plugin.json").read_text(encoding="utf-8"))


if __name__ == "__main__":
    unittest.main()

SHA-256: a23bca5e6b1d8aaaf99cf11718a89d4a8099561ee1ded5feb916b29ed71d9f7d