File size: 5,720 Bytes
b15e89c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35755b6
b15e89c
 
35755b6
 
 
 
b15e89c
 
 
35755b6
 
 
 
 
 
 
b15e89c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
"""
Smoke-tests for the multi-file processing utilities in main.py.

These tests run WITHOUT starting the server (no CUSTOM_API_KEY needed)
by importing the utilities directly after patching the environment.
"""
import io
import os
import zipfile
import tarfile
import sys

# Patch env so main.py doesn't explode on import
os.environ.setdefault("CUSTOM_API_KEY", "test-key-for-ci")
os.environ.setdefault("OPENROUTER_API_KEY", "test-key-for-ci")

# Add parent to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from main import (
    _is_skippable_path,
    _safe_decode,
    _build_directory_tree,
    _build_project_representation,
    _normalize_archive_path,
    _strip_common_prefix,
    _extract_zip,
    _extract_tar,
)


def test_is_skippable_path():
    # Skip hidden dirs
    assert _is_skippable_path(".git/config") is True
    assert _is_skippable_path(".env") is True
    # Skip known dirs
    assert _is_skippable_path("node_modules/express/index.js") is True
    assert _is_skippable_path("__pycache__/mod.cpython-311.pyc") is True
    # Skip binary extensions (not on CODE_EXTENSIONS allowlist)
    assert _is_skippable_path("assets/logo.png") is True
    assert _is_skippable_path("build/app.exe") is True
    # Skip unknown extensions (not on allowlist)
    assert _is_skippable_path("data/file.xyz") is True
    assert _is_skippable_path("archive.tar.gz") is True
    # Allow code files (on allowlist)
    assert _is_skippable_path("src/main.py") is False
    assert _is_skippable_path("index.js") is False
    assert _is_skippable_path("README.md") is False
    assert _is_skippable_path("styles.css") is False
    assert _is_skippable_path("app.java") is False
    assert _is_skippable_path("config.yaml") is False
    # Allow extension-less files (Makefile, Dockerfile, Procfile)
    assert _is_skippable_path("Makefile") is False
    assert _is_skippable_path("Dockerfile") is False
    assert _is_skippable_path("src/Procfile") is False
    print("  ✅ _is_skippable_path")


def test_safe_decode():
    # Normal UTF-8
    assert _safe_decode(b"hello world") == "hello world"
    # Binary (null bytes) → None
    assert _safe_decode(b"hello\x00world") is None
    # Invalid UTF-8 with replacement
    result = _safe_decode(b"hello \xff world")
    assert result is not None
    assert "hello" in result
    print("  ✅ _safe_decode")


def test_normalize_archive_path():
    assert _normalize_archive_path("src/main.py") == "src/main.py"
    assert _normalize_archive_path("./src/main.py") == "src/main.py"
    # Traversal attacks
    assert _normalize_archive_path("../../../etc/passwd") is None
    assert _normalize_archive_path("/etc/passwd") is None
    # Windows paths
    assert _normalize_archive_path("src\\main.py") == "src/main.py"
    print("  ✅ _normalize_archive_path")


def test_build_directory_tree():
    paths = ["src/index.js", "src/utils.js", "package.json"]
    tree = _build_directory_tree(paths)
    assert "src/" in tree
    assert "index.js" in tree
    assert "utils.js" in tree
    assert "package.json" in tree
    print("  ✅ _build_directory_tree")
    print(f"     Tree output:\n{tree}")


def test_build_project_representation():
    file_map = {
        "src/index.js": "console.log('hi');",
        "package.json": '{"name": "test"}',
    }
    result = _build_project_representation(file_map)
    assert "[PROJECT DIRECTORY TREE]" in result
    assert '<FILE path="src/index.js">' in result
    assert '<FILE path="package.json">' in result
    assert "console.log('hi');" in result
    print("  ✅ _build_project_representation")


def test_strip_common_prefix():
    # With common prefix
    fm = {"myproject/src/a.py": "a", "myproject/src/b.py": "b"}
    stripped = _strip_common_prefix(fm)
    assert "a.py" in stripped or "src/a.py" in stripped
    # Without common prefix
    fm2 = {"src/a.py": "a", "lib/b.py": "b"}
    stripped2 = _strip_common_prefix(fm2)
    assert "src/a.py" in stripped2
    print("  ✅ _strip_common_prefix")


def test_extract_zip():
    # Build a zip in memory
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, "w") as zf:
        zf.writestr("project/src/main.py", "print('hello')")
        zf.writestr("project/src/utils.py", "def add(a, b): return a + b")
        zf.writestr("project/README.md", "# My Project")
        # Binary file — should be skipped
        zf.writestr("project/logo.png", b"\x89PNG\r\n\x1a\n\x00\x00".decode("latin-1"))

    result = _extract_zip(buf.getvalue())
    assert "src/main.py" in result
    assert "src/utils.py" in result
    assert "README.md" in result
    # .png should be skipped
    assert "logo.png" not in result
    print(f"  ✅ _extract_zip — extracted {len(result)} files")


def test_extract_tar():
    buf = io.BytesIO()
    with tarfile.open(fileobj=buf, mode="w:gz") as tf:
        for name, content in [
            ("project/app.py", "import flask"),
            ("project/config.yaml", "debug: true"),
        ]:
            data = content.encode("utf-8")
            info = tarfile.TarInfo(name=name)
            info.size = len(data)
            tf.addfile(info, io.BytesIO(data))

    result = _extract_tar(buf.getvalue())
    assert "app.py" in result
    assert "config.yaml" in result
    print(f"  ✅ _extract_tar — extracted {len(result)} files")


if __name__ == "__main__":
    print("\n🧪 Running multi-file utility tests...\n")
    test_is_skippable_path()
    test_safe_decode()
    test_normalize_archive_path()
    test_build_directory_tree()
    test_build_project_representation()
    test_strip_common_prefix()
    test_extract_zip()
    test_extract_tar()
    print("\n✅ All tests passed!\n")