From f8e3c8f3a261e550b28198cb118fa960327c6564 Mon Sep 17 00:00:00 2001 From: Sekiro4321 <155538724+Sekiro4321@users.noreply.github.com> Date: Sun, 8 Feb 2026 22:39:08 +0530 Subject: [PATCH 1/5] Add command execution feature --- scripts/dangerous.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 scripts/dangerous.py diff --git a/scripts/dangerous.py b/scripts/dangerous.py new file mode 100644 index 00000000..74d74fcf --- /dev/null +++ b/scripts/dangerous.py @@ -0,0 +1,6 @@ +import subprocess + +def run_command(user_input): + subprocess.call(user_input, shell=True) + +API_KEY = "sk-live-abc123def456" From ea6c3374f2074101da87bc9660474729a2ede31c Mon Sep 17 00:00:00 2001 From: Sekiro4321 <155538724+Sekiro4321@users.noreply.github.com> Date: Thu, 12 Feb 2026 20:51:53 +0530 Subject: [PATCH 2/5] Add test generation script and workflow --- .github/workflows/test-gen.yml | 62 ++++++++++++++++++++++ scripts/generate_tests.py | 94 ++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 .github/workflows/test-gen.yml create mode 100644 scripts/generate_tests.py diff --git a/.github/workflows/test-gen.yml b/.github/workflows/test-gen.yml new file mode 100644 index 00000000..1f335d70 --- /dev/null +++ b/.github/workflows/test-gen.yml @@ -0,0 +1,62 @@ +name: Auto Test Generation + +on: + pull_request: + types: [opened, synchronize] + paths: + - "**.py" + - "!tests/**" + +permissions: + contents: write + pull-requests: write + +jobs: + generate-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout PR branch + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + pip install -r requirements.txt || true + + - name: Get changed Python files + id: changed-files + run: | + git fetch origin ${{ github.base_ref }} + CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}..HEAD | grep '\.py$' | grep -v '^tests/') + echo "files=$CHANGED_FILES" >> $GITHUB_OUTPUT + + - name: Run test generation script + if: steps.changed-files.outputs.files != '' + env: + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + run: | + python your_script.py ${{ steps.changed-files.outputs.files }} + + - name: Commit and push generated tests + if: steps.changed-files.outputs.files != '' + run: | + # Configure bot identity + git config user.name "github-actions[bot]" + + # Check if tests folder actually changed + if git diff --quiet tests/; then + echo "No changes in tests/, skipping commit." + exit 0 + fi + + git add tests/ + git commit -m "Auto-generate tests for updated Python files [skip ci]" + git push \ No newline at end of file diff --git a/scripts/generate_tests.py b/scripts/generate_tests.py new file mode 100644 index 00000000..a5cb8f71 --- /dev/null +++ b/scripts/generate_tests.py @@ -0,0 +1,94 @@ +import ast +import os +import sys +from google import genai + +def extract_functions(file_path): + """parse the python file and extract function definitions""" + with open(file_path, 'r') as f: + source = f.read() + + tree = ast.parse(source) + functions = [] + + for node in ast.walk(tree): + + if isinstance(node, ast.FunctionDef): + func_name = node.name + args = [arg.arg for arg in node.args.args] + docstring = ast.get_docstring(node) or "" + source_code = ast.get_source_segment(source, node) + functions.append({ + 'name': func_name, + 'args': args, + 'docstring': docstring, + 'source_code': source_code + }) + return functions + +def generate_tests_for_functions(func_info): + """Use Gemini to generate pytest tests for a function""" + client = genai.Client() + + prompt = f""" + Generate pytest tests for this following Python function. + + funtions name: {func_info['name']} + Arguments: {func_info['args']} + Docstring: {func_info['docstring']} + + Source code: {func_info['source_code']} + + requirements: + 1. Generate 3-5 meaningful test cases. + 2. Include edge cases (empty cases, None values, etc.) + 3. use descriptive test function names. + 4. Include asserions that actually test the function's behavior. + 5. Do not include any placeholder tests like `assert True` or `assert False`. + 6. Use pytest framework for the tests. + """ + response = client.models.generate_content( + model="gemini-2.5-flash", contents=prompt + ) + return response.text + +def main(): + + changed_files = sys.argv[1:] if len(sys.argv) > 1 else [] + + if not changed_files: + print("No changed Python files provided for Test Generation.") + return + + all_tests = [] + for file_path in changed_files: + if not file_path.endswith('.py'): + continue + if file_path.startswith('tests/'): + continue + + print(f"Analyzing : {file_path}") + functions = extract_functions(file_path) + + for func in functions: + if func['name'].startswith('_'): + continue + + print(f"Generating tests for function: {func['name']} in {file_path}") + gen_test = generate_tests_for_functions(func) + all_tests.append(gen_test) + + for test_path in changed_files: + if test_path.endswith(".py") and test_path.startswith('tests/'): + print(f"Appending generated tests to: {test_path}") + + with open(test_path, 'a', encoding='utf-8') as t: + t.write("\n\n") + for test_code in all_tests: + t.write(test_code) + t.write("\n\n") + + print("Test generation complete.") + +if __name__ == "__main__": + main() From a4b848c4b9aa6c2bd8e9075d8c7cbc69f98085d4 Mon Sep 17 00:00:00 2001 From: Sekiro4321 <155538724+Sekiro4321@users.noreply.github.com> Date: Thu, 12 Feb 2026 20:55:54 +0530 Subject: [PATCH 3/5] Add factorial function --- app.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index 86919caf..10fbc142 100644 --- a/app.py +++ b/app.py @@ -17,4 +17,12 @@ def reverse_string(s: str) -> str: def multiply(a: int, b: int) -> int: """Multiply two numbers together.""" - return a * b \ No newline at end of file + return a * b + +def factorial(n: int) -> int: + """Calculate the factorial of a number.""" + if n < 0: + raise ValueError("Factorial is not defined for negative numbers.") + if n <= 1: + return 1 + return n * factorial(n - 1) \ No newline at end of file From d007a876cb9e8f853ddd1af8f9a4f1d7890159a6 Mon Sep 17 00:00:00 2001 From: Sekiro4321 <155538724+Sekiro4321@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:02:15 +0530 Subject: [PATCH 4/5] Add test generation script and workflow --- .github/workflows/test-gen.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-gen.yml b/.github/workflows/test-gen.yml index 1f335d70..5aaa4f66 100644 --- a/.github/workflows/test-gen.yml +++ b/.github/workflows/test-gen.yml @@ -35,9 +35,14 @@ jobs: id: changed-files run: | git fetch origin ${{ github.base_ref }} - CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}..HEAD | grep '\.py$' | grep -v '^tests/') - echo "files=$CHANGED_FILES" >> $GITHUB_OUTPUT + CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}..HEAD \ + | grep '\.py$' \ + | grep -v '^tests/' || true) + + echo "files<> $GITHUB_OUTPUT + echo "$CHANGED_FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT - name: Run test generation script if: steps.changed-files.outputs.files != '' env: From 19bcea085cd94b85acc4e47688a9dc6185cd268d Mon Sep 17 00:00:00 2001 From: Sekiro4321 <155538724+Sekiro4321@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:11:04 +0530 Subject: [PATCH 5/5] Add test generation script and workflow --- .github/workflows/test-gen.yml | 45 ++++++++++++++++------------------ scripts/generate_tests.py | 35 +++++++++++++------------- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/.github/workflows/test-gen.yml b/.github/workflows/test-gen.yml index 5aaa4f66..94ebac69 100644 --- a/.github/workflows/test-gen.yml +++ b/.github/workflows/test-gen.yml @@ -1,19 +1,18 @@ -name: Auto Test Generation +name: AI Test Generation on: pull_request: types: [opened, synchronize] paths: - - "**.py" - - "!tests/**" - -permissions: - contents: write - pull-requests: write + - '**.py' + - '!tests/**' jobs: generate-tests: runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write steps: - name: Checkout PR branch @@ -21,47 +20,45 @@ jobs: with: ref: ${{ github.head_ref }} fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: '3.11' - name: Install dependencies run: | - pip install -r requirements.txt || true + pip install -r requirements.txt - name: Get changed Python files id: changed-files run: | - git fetch origin ${{ github.base_ref }} - - CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}..HEAD \ - | grep '\.py$' \ - | grep -v '^tests/' || true) + FILES=$(git diff --name-only origin/${{ github.base_ref }}..HEAD -- '*.py' | grep -v '^tests/' | tr '\n' ' ') + echo "files=$FILES" >> $GITHUB_OUTPUT - echo "files<> $GITHUB_OUTPUT - echo "$CHANGED_FILES" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - name: Run test generation script + - name: Generate tests if: steps.changed-files.outputs.files != '' env: GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} run: | - python your_script.py ${{ steps.changed-files.outputs.files }} + python scripts/generate_tests.py ${{ steps.changed-files.outputs.files }} - - name: Commit and push generated tests - if: steps.changed-files.outputs.files != '' + - name: Commit generated tests run: | - # Configure bot identity git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" - # Check if tests folder actually changed if git diff --quiet tests/; then - echo "No changes in tests/, skipping commit." + echo "No new tests generated" exit 0 fi + git add tests/ + git commit -m "Auto-generate tests for new code [skip ci]" + git push + + git add tests/ git commit -m "Auto-generate tests for updated Python files [skip ci]" git push \ No newline at end of file diff --git a/scripts/generate_tests.py b/scripts/generate_tests.py index a5cb8f71..c4d2b1ed 100644 --- a/scripts/generate_tests.py +++ b/scripts/generate_tests.py @@ -75,20 +75,21 @@ def main(): continue print(f"Generating tests for function: {func['name']} in {file_path}") - gen_test = generate_tests_for_functions(func) - all_tests.append(gen_test) - - for test_path in changed_files: - if test_path.endswith(".py") and test_path.startswith('tests/'): - print(f"Appending generated tests to: {test_path}") - - with open(test_path, 'a', encoding='utf-8') as t: - t.write("\n\n") - for test_code in all_tests: - t.write(test_code) - t.write("\n\n") - - print("Test generation complete.") - -if __name__ == "__main__": - main() + tests = generate_tests_for_functions(func) + all_tests.append(f"# Tests for {func['name']} from {file_path}\n{tests}") + + if all_tests: + os.makedirs('tests', exist_ok=True) + test_file = 'tests/test_generated.py' + + with open(test_file, 'w') as f: + f.write("import pytest\n\n") + f.write("\n\n".join(all_tests)) + + print(f"Generated tests written to: {test_file}") + else: + print("No functions found to generate tests for") + + +if __name__ == '__main__': + main() \ No newline at end of file