Tests for pyrefly snippet command

Basic snippet with type error

$ $PYREFLY snippet "x: int = 'hello'"
ERROR `Literal['hello']` is not assignable to `int` [bad-assignment]
 --> snippet:1:10
  |
1 | x: int = 'hello'
  |    ---   ^^^^^^^
  |    |
  |    declared type
  |
[1]

Valid snippet (no errors)

$ $PYREFLY snippet "x: int = 42"
 INFO 0 errors
[0]

Snippet with built-in module import

$ $PYREFLY snippet "import sys; print(sys.version)"
 INFO 0 errors
[0]

Snippet with local file import

$ echo "x: int = 5" > $TMPDIR/test.py && \
> touch $TMPDIR/pyrefly.toml && \
> $PYREFLY snippet "import test; reveal_type(test.x)" -c $TMPDIR/pyrefly.toml
ERROR `reveal_type` must be imported from `typing` for runtime usage [unimported-directive]
 --> snippet:1:14
  |
1 | import test; reveal_type(test.x)
  |              ^^^^^^^^^^^
  |
 INFO revealed type: int [reveal-type]
 --> snippet:1:25
  |
1 | import test; reveal_type(test.x)
  |                         --------
  |
[1]

Snippet with typing imports and error

$ $PYREFLY snippet "from typing import List; x: List[str] = [1, 2, 3]"
ERROR `list[int]` is not assignable to `list[str]` [bad-assignment]
 --> snippet:1:41
  |
1 | from typing import List; x: List[str] = [1, 2, 3]
  |                             ---------   ^^^^^^^^^
  |                             |
  |                             declared type
  |
[1]

Snippet with multiple errors

$ $PYREFLY snippet "def foo(x: str) -> int: return len(x); y: str = foo(42)"
ERROR Function declared to return `int`, but one or more paths are missing an explicit `return` [bad-return]
 --> snippet:1:20
  |
1 | def foo(x: str) -> int: return len(x); y: str = foo(42)
  |                    ^^^
  |
ERROR `int` is not assignable to `str` [bad-assignment]
 --> snippet:1:49
  |
1 | def foo(x: str) -> int: return len(x); y: str = foo(42)
  |                                           ---   ^^^^^^^
  |                                           |
  |                                           declared type
  |
ERROR Argument `Literal[42]` is not assignable to parameter `x` with type `str` in function `foo` [bad-argument-type]
 --> snippet:1:53
  |
1 | def foo(x: str) -> int: return len(x); y: str = foo(42)
  |                                                     ^^
  |
[1]

Snippet with JSON output format

$ $PYREFLY snippet "x: int = 'hello'" --output-format=json
{
  "errors": [
    {
      "line": 1,
      "column": 10,
      "stop_line": 1,
      "stop_column": 17,
      "path": "snippet",
      "code": -2,
      "name": "bad-assignment",
      "description": "`Literal['hello']` is not assignable to `int`",
      "concise_description": "`Literal['hello']` is not assignable to `int`",
      "severity": "error"
    }
  ]
} (no-eol)
[1]

Snippet with multiple output destinations

$ $PYREFLY snippet "x: int = 'hello'" --summary=none \
>     --output=min-text:- --output="json:$TMPDIR/snippet.json"; rc=$?; \
> $JQ -r '.errors[0].name' $TMPDIR/snippet.json; exit $rc
ERROR snippet:1:10-17: `Literal['hello']` is not assignable to `int` [bad-assignment]
bad-assignment
[1]

Snippet with CodeClimate output format

$ $PYREFLY snippet "x: int = 'hello'" --output-format=code-climate
[
  {
    "type": "issue",
    "check_name": "pyrefly/bad-assignment",
    "description": "`Literal['hello']` is not assignable to `int`",
    "categories": [
      "Bug Risk"
    ],
    "location": {
      "path": "snippet",
      "positions": {
        "begin": {
          "line": 1,
          "column": 10
        },
        "end": {
          "line": 1,
          "column": 17
        }
      }
    },
    "severity": "major",
    "fingerprint": "18e547e03ffcae99"
  }
] (no-eol)
[1]

Snippet with config file

$ echo "python_version = \"3.11\"" > pyrefly.toml && $PYREFLY snippet "x: int = 42" --config pyrefly.toml
 INFO 0 errors
[0]

Snippet picks up config from current directory

When a pyrefly.toml exists in the current directory and no --config flag is passed, pyrefly snippet should discover and use it — the same way pyrefly check does.

$ echo 'errors = { bad-assignment = false }' > $TMPDIR/pyrefly.toml && cd $TMPDIR && \
> $PYREFLY snippet "x: int = 'hello'"
 INFO 0 errors
[0]

Help text shows snippet command

$ $PYREFLY snippet --help | head -3
Check a Python code snippet

Usage: pyrefly snippet [OPTIONS] <CODE>
[0]

Main help shows snippet command

$ $PYREFLY --help | grep "snippet"
  snippet      Check a Python code snippet
[0]

Snippets can read from stdin

$ cat <<EOF | $PYREFLY snippet -
> x: int = 1
> def foo(x: float) -> None:
>   return x > 1.0
> foo(x)
ERROR Returned type `bool` is not assignable to declared return type `None` [bad-return]
 --> snippet:3:10
  |
2 | def foo(x: float) -> None:
  |                      ---- declared return type
3 |   return x > 1.0
  |          ^^^^^^^
  |
[1]