Skip to content

Instantly share code, notes, and snippets.

@simonw

simonw/tests.md Secret

Created April 8, 2024 06:03
Show Gist options
  • Save simonw/511e1dbede6aba25b2d7027c55cdf759 to your computer and use it in GitHub Desktop.
Save simonw/511e1dbede6aba25b2d7027c55cdf759 to your computer and use it in GitHub Desktop.

2024-04-08T05:54:01 conversation: 01hty4b8jkh8t7bhsyn9557k3q

Model: claude-3-opus-20240229

Prompt:

diff.txt
---
diff --git a/files_to_prompt/cli.py b/files_to_prompt/cli.py
index ef0b83a..972fce6 100644
--- a/files_to_prompt/cli.py
+++ b/files_to_prompt/cli.py
@@ -27,13 +27,17 @@ def process_path(
     path, include_hidden, ignore_gitignore, gitignore_rules, ignore_patterns
 ):
     if os.path.isfile(path):
-        with open(path, "r") as f:
-            file_contents = f.read()
-        click.echo(path)
-        click.echo("---")
-        click.echo(file_contents)
-        click.echo()
-        click.echo("---")
+        try:
+            with open(path, "r") as f:
+                file_contents = f.read()
+            click.echo(path)
+            click.echo("---")
+            click.echo(file_contents)
+            click.echo()
+            click.echo("---")
+        except UnicodeDecodeError:
+            warning_message = f"Warning: Skipping file {path} due to UnicodeDecodeError"
+            click.echo(click.style(warning_message, fg='red'), err=True)
     elif os.path.isdir(path):
         for root, dirs, files in os.walk(path):
             if not include_hidden:
@@ -62,14 +66,18 @@ def process_path(
 
             for file in files:
                 file_path = os.path.join(root, file)
-                with open(file_path, "r") as f:
-                    file_contents = f.read()
+                try:
+                    with open(file_path, "r") as f:
+                        file_contents = f.read()
 
-                click.echo(file_path)
-                click.echo("---")
-                click.echo(file_contents)
-                click.echo()
-                click.echo("---")
+                    click.echo(file_path)
+                    click.echo("---")
+                    click.echo(file_contents)
+                    click.echo()
+                    click.echo("---")
+                except UnicodeDecodeError:
+                    warning_message = f"Warning: Skipping file {file_path} due to UnicodeDecodeError"
+                    click.echo(click.style(warning_message, fg='red'), err=True)
 
 
 @click.command()
@@ -114,4 +122,4 @@ def cli(paths, include_hidden, ignore_gitignore, ignore_patterns):
             gitignore_rules.extend(read_gitignore(os.path.dirname(path)))
         process_path(
             path, include_hidden, ignore_gitignore, gitignore_rules, ignore_patterns
-        )
+        )
\ No newline at end of file


---
tests/test_files_to_prompt.py
---
import os
from click.testing import CliRunner
from files_to_prompt.cli import cli


def test_basic_functionality(tmpdir):
    runner = CliRunner()
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/file1.txt", "w") as f:
            f.write("Contents of file1")
        with open("test_dir/file2.txt", "w") as f:
            f.write("Contents of file2")

        result = runner.invoke(cli, ["test_dir"])
        assert result.exit_code == 0
        assert "test_dir/file1.txt" in result.output
        assert "Contents of file1" in result.output
        assert "test_dir/file2.txt" in result.output
        assert "Contents of file2" in result.output


def test_include_hidden(tmpdir):
    runner = CliRunner()
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/.hidden.txt", "w") as f:
            f.write("Contents of hidden file")

        result = runner.invoke(cli, ["test_dir"])
        assert result.exit_code == 0
        assert "test_dir/.hidden.txt" not in result.output

        result = runner.invoke(cli, ["test_dir", "--include-hidden"])
        assert result.exit_code == 0
        assert "test_dir/.hidden.txt" in result.output
        assert "Contents of hidden file" in result.output


def test_ignore_gitignore(tmpdir):
    runner = CliRunner()
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/.gitignore", "w") as f:
            f.write("ignored.txt")
        with open("test_dir/ignored.txt", "w") as f:
            f.write("This file should be ignored")
        with open("test_dir/included.txt", "w") as f:
            f.write("This file should be included")

        result = runner.invoke(cli, ["test_dir"])
        assert result.exit_code == 0
        assert "test_dir/ignored.txt" not in result.output
        assert "test_dir/included.txt" in result.output

        result = runner.invoke(cli, ["test_dir", "--ignore-gitignore"])
        assert result.exit_code == 0
        assert "test_dir/ignored.txt" in result.output
        assert "This file should be ignored" in result.output
        assert "test_dir/included.txt" in result.output


def test_multiple_paths(tmpdir):
    runner = CliRunner()
    with tmpdir.as_cwd():
        os.makedirs("test_dir1")
        with open("test_dir1/file1.txt", "w") as f:
            f.write("Contents of file1")
        os.makedirs("test_dir2")
        with open("test_dir2/file2.txt", "w") as f:
            f.write("Contents of file2")
        with open("single_file.txt", "w") as f:
            f.write("Contents of single file")

        result = runner.invoke(cli, ["test_dir1", "test_dir2", "single_file.txt"])
        assert result.exit_code == 0
        assert "test_dir1/file1.txt" in result.output
        assert "Contents of file1" in result.output
        assert "test_dir2/file2.txt" in result.output
        assert "Contents of file2" in result.output
        assert "single_file.txt" in result.output
        assert "Contents of single file" in result.output


def test_ignore_patterns(tmpdir):
    runner = CliRunner()
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/file_to_ignore.txt", "w") as f:
            f.write("This file should be ignored due to ignore patterns")
        with open("test_dir/file_to_include.txt", "w") as f:
            f.write("This file should be included")

        result = runner.invoke(cli, ["test_dir", "--ignore", "*.txt"])
        assert result.exit_code == 0
        assert "test_dir/file_to_ignore.txt" not in result.output
        assert "This file should be ignored due to ignore patterns" not in result.output
        assert "test_dir/file_to_include.txt" not in result.output

        result = runner.invoke(cli, ["test_dir", "--ignore", "file_to_ignore.*"])
        assert result.exit_code == 0
        assert "test_dir/file_to_ignore.txt" not in result.output
        assert "This file should be ignored due to ignore patterns" not in result.output
        assert "test_dir/file_to_include.txt" in result.output
        assert "This file should be included" in result.output


def test_mixed_paths_with_options(tmpdir):
    runner = CliRunner()
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/.gitignore", "w") as f:
            f.write("ignored_in_gitignore.txt\n.hidden_ignored_in_gitignore.txt")
        with open("test_dir/ignored_in_gitignore.txt", "w") as f:
            f.write("This file should be ignored by .gitignore")
        with open("test_dir/.hidden_ignored_in_gitignore.txt", "w") as f:
            f.write("This hidden file should be ignored by .gitignore")
        with open("test_dir/included.txt", "w") as f:
            f.write("This file should be included")
        with open("test_dir/.hidden_included.txt", "w") as f:
            f.write("This hidden file should be included")
        with open("single_file.txt", "w") as f:
            f.write("Contents of single file")

        result = runner.invoke(cli, ["test_dir", "single_file.txt"])
        assert result.exit_code == 0
        assert "test_dir/ignored_in_gitignore.txt" not in result.output
        assert "test_dir/.hidden_ignored_in_gitignore.txt" not in result.output
        assert "test_dir/included.txt" in result.output
        assert "test_dir/.hidden_included.txt" not in result.output
        assert "single_file.txt" in result.output
        assert "Contents of single file" in result.output

        result = runner.invoke(cli, ["test_dir", "single_file.txt", "--include-hidden"])
        assert result.exit_code == 0
        assert "test_dir/ignored_in_gitignore.txt" not in result.output
        assert "test_dir/.hidden_ignored_in_gitignore.txt" not in result.output
        assert "test_dir/included.txt" in result.output
        assert "test_dir/.hidden_included.txt" in result.output
        assert "single_file.txt" in result.output
        assert "Contents of single file" in result.output

        result = runner.invoke(
            cli, ["test_dir", "single_file.txt", "--ignore-gitignore"]
        )
        assert result.exit_code == 0
        assert "test_dir/ignored_in_gitignore.txt" in result.output
        assert "test_dir/.hidden_ignored_in_gitignore.txt" not in result.output
        assert "test_dir/included.txt" in result.output
        assert "test_dir/.hidden_included.txt" not in result.output
        assert "single_file.txt" in result.output
        assert "Contents of single file" in result.output

        result = runner.invoke(
            cli,
            ["test_dir", "single_file.txt", "--ignore-gitignore", "--include-hidden"],
        )
        assert result.exit_code == 0
        assert "test_dir/ignored_in_gitignore.txt" in result.output
        assert "test_dir/.hidden_ignored_in_gitignore.txt" in result.output
        assert "test_dir/included.txt" in result.output
        assert "test_dir/.hidden_included.txt" in result.output
        assert "single_file.txt" in result.output
        assert "Contents of single file" in result.output

System:

output one more test that can exercise the new code that writes warnings about binary files

Response:

Here's a test that can exercise the new code that writes warnings about binary files:

def test_binary_file_warning(tmpdir):
    runner = CliRunner()
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/binary_file.bin", "wb") as f:
            f.write(b"\x00\x01\x02\x03\x04\x05")
        with open("test_dir/text_file.txt", "w") as f:
            f.write("This is a text file")

        result = runner.invoke(cli, ["test_dir"])
        assert result.exit_code == 0
        assert "test_dir/text_file.txt" in result.output
        assert "This is a text file" in result.output
        assert "test_dir/binary_file.bin" not in result.output
        assert "Warning: Skipping file test_dir/binary_file.bin due to UnicodeDecodeError" in result.output

This test does the following:

  1. It creates a temporary directory using the tmpdir fixture.
  2. Inside the temporary directory, it creates a subdirectory called "test_dir".
  3. It creates a binary file named "binary_file.bin" within "test_dir" and writes some binary data to it.
  4. It also creates a text file named "text_file.txt" within "test_dir" and writes some text content to it.
  5. It invokes the CLI command with the "test_dir" path.
  6. It asserts that the exit code is 0, indicating successful execution.
  7. It checks that the text file "test_dir/text_file.txt" is present in the output and its contents are displayed.
  8. It verifies that the binary file "test_dir/binary_file.bin" is not included in the output.
  9. It asserts that the warning message "Warning: Skipping file test_dir/binary_file.bin due to UnicodeDecodeError" is present in the output, indicating that the binary file was skipped and a warning was displayed.

This test ensures that when a binary file is encountered, it is skipped and a warning message is displayed, while text files are processed normally.

2024-04-08T05:54:48 conversation: 01hty4b8jkh8t7bhsyn9557k3q

Model: claude-3-opus-20240229

Prompt:

modify that test to capture stdout and stderr separately and check for the message in stderr

Response:

Sure! Here's the modified test that captures stdout and stderr separately and checks for the warning message in stderr:

def test_binary_file_warning(tmpdir):
    runner = CliRunner()
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/binary_file.bin", "wb") as f:
            f.write(b"\x00\x01\x02\x03\x04\x05")
        with open("test_dir/text_file.txt", "w") as f:
            f.write("This is a text file")

        result = runner.invoke(cli, ["test_dir"], catch_exceptions=False)
        assert result.exit_code == 0

        stdout = result.stdout
        stderr = result.stderr

        assert "test_dir/text_file.txt" in stdout
        assert "This is a text file" in stdout
        assert "test_dir/binary_file.bin" not in stdout
        assert "Warning: Skipping file test_dir/binary_file.bin due to UnicodeDecodeError" in stderr

In this modified test:

  1. We use the catch_exceptions=False argument when invoking the CLI command using runner.invoke(). This ensures that any exceptions raised during the execution are not caught and will be visible in the test output.

  2. After invoking the CLI command, we capture the stdout and stderr separately using result.stdout and result.stderr, respectively.

  3. We update the assertions to check for the presence of the text file and its contents in stdout.

  4. We also update the assertion to check that the binary file is not included in stdout.

  5. Finally, we assert that the warning message "Warning: Skipping file test_dir/binary_file.bin due to UnicodeDecodeError" is present in stderr, indicating that the warning was written to stderr as expected.

By capturing stdout and stderr separately and checking for the warning message in stderr, we ensure that the warning is being written to the correct output stream (stderr) and can be distinguished from the regular output (stdout).

2024-04-08T05:55:24 conversation: 01hty4b8jkh8t7bhsyn9557k3q

Model: claude-3-opus-20240229

Prompt:

ValueError: stderr not separately captured

Response:

Apologies for the confusion. It seems that the CliRunner in Click testing doesn't capture stderr separately by default. To capture stderr, you need to pass the stderr parameter to the CliRunner constructor. Here's the updated test with the necessary changes:

def test_binary_file_warning(tmpdir):
    runner = CliRunner(stderr=True)
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/binary_file.bin", "wb") as f:
            f.write(b"\x00\x01\x02\x03\x04\x05")
        with open("test_dir/text_file.txt", "w") as f:
            f.write("This is a text file")

        result = runner.invoke(cli, ["test_dir"])
        assert result.exit_code == 0

        stdout = result.stdout
        stderr = result.stderr

        assert "test_dir/text_file.txt" in stdout
        assert "This is a text file" in stdout
        assert "test_dir/binary_file.bin" not in stdout
        assert "Warning: Skipping file test_dir/binary_file.bin due to UnicodeDecodeError" in stderr

The changes made:

  1. When creating the CliRunner instance, we pass stderr=True to enable capturing stderr separately.

  2. The catch_exceptions=False argument is removed since it's not necessary for capturing stderr.

Now, when you run this test, it will capture stdout and stderr separately, and you can assert the presence of the warning message in stderr as expected.

Apologies for any confusion caused earlier. Let me know if you have any further questions!

2024-04-08T05:55:51 conversation: 01hty4b8jkh8t7bhsyn9557k3q

Model: claude-3-opus-20240229

Prompt:

TypeError: CliRunner.init() got an unexpected keyword argument 'stderr'

Response:

I apologize for the confusion. It seems that the CliRunner in Click testing doesn't have a stderr parameter. Instead, you can use the result.stderr attribute directly to access the captured stderr output.

Here's the updated test without the stderr parameter:

def test_binary_file_warning(tmpdir):
    runner = CliRunner()
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/binary_file.bin", "wb") as f:
            f.write(b"\x00\x01\x02\x03\x04\x05")
        with open("test_dir/text_file.txt", "w") as f:
            f.write("This is a text file")

        result = runner.invoke(cli, ["test_dir"])
        assert result.exit_code == 0

        stdout = result.output
        stderr = result.stderr

        assert "test_dir/text_file.txt" in stdout
        assert "This is a text file" in stdout
        assert "test_dir/binary_file.bin" not in stdout
        assert "Warning: Skipping file test_dir/binary_file.bin due to UnicodeDecodeError" in stderr

In this updated version:

  1. We create the CliRunner instance without any additional parameters.

  2. After invoking the CLI command, we access the captured stdout using result.output and the captured stderr using result.stderr.

  3. The assertions remain the same, checking for the presence of the text file and its contents in stdout, verifying that the binary file is not included in stdout, and ensuring that the warning message is present in stderr.

This should resolve the TypeError and allow the test to run successfully, capturing stdout and stderr separately and checking for the warning message in stderr.

Let me know if you have any further questions!

2024-04-08T05:56:44 conversation: 01hty4b8jkh8t7bhsyn9557k3q

Model: claude-3-opus-20240229

Prompt:

Use CliRunner(mix_stderr=False)

Response:

You're right! Using CliRunner(mix_stderr=False) is the correct way to capture stderr separately in Click testing. Here's the updated test with the correct usage:

def test_binary_file_warning(tmpdir):
    runner = CliRunner(mix_stderr=False)
    with tmpdir.as_cwd():
        os.makedirs("test_dir")
        with open("test_dir/binary_file.bin", "wb") as f:
            f.write(b"\x00\x01\x02\x03\x04\x05")
        with open("test_dir/text_file.txt", "w") as f:
            f.write("This is a text file")

        result = runner.invoke(cli, ["test_dir"])
        assert result.exit_code == 0

        stdout = result.stdout
        stderr = result.stderr

        assert "test_dir/text_file.txt" in stdout
        assert "This is a text file" in stdout
        assert "test_dir/binary_file.bin" not in stdout
        assert "Warning: Skipping file test_dir/binary_file.bin due to UnicodeDecodeError" in stderr

In this updated version:

  1. We create the CliRunner instance with mix_stderr=False to ensure that stderr is captured separately from stdout.

  2. After invoking the CLI command, we access the captured stdout using result.stdout and the captured stderr using result.stderr.

  3. The assertions remain the same, checking for the presence of the text file and its contents in stdout, verifying that the binary file is not included in stdout, and ensuring that the warning message is present in stderr.

Thank you for pointing out the correct way to capture stderr separately in Click testing. This should work as expected and allow you to test the warning message in stderr.

Apologies for any confusion caused earlier. Let me know if you have any further questions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment