Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Tests

on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- main

jobs:
run-pytest:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up python
uses: actions/setup-python@v6

- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Install dependencies
run: |
uv sync --all-extras
uv pip install -e .

- name: Run tests
run: uv run pytest
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ dependencies = [

[project.urls]
repository = "https://github.com/RektPunk/colablinter"

[dependency-groups]
dev = [
"pytest>=9.0.3",
]
8 changes: 8 additions & 0 deletions src/colablinter/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,45 @@
CELL_CHECK_COMMAND = [
"ruff",
"check",
"--quiet",
"--select",
"B,E,F,I,UP,SIM",
"--ignore",
"F401,E501",
"--stdin-filename=tmp.py",
"-",
]
CELL_CHECK_FIX_COMMAND = [
"ruff",
"check",
"--fix",
"--quiet",
"--select",
"B,E,F,I,UP,SIM",
"--ignore",
"F401,E501",
"--stdin-filename=tmp.py",
"-",
]
CELL_CHECK_UNSAFE_FIX_COMMAND = [
"ruff",
"check",
"--fix",
"--unsafe-fixes",
"--quiet",
"--select",
"B,E,F,I,UP,SIM",
"--ignore",
"F401,E501",
"--stdin-filename=tmp.py",
"-",
]
CELL_FORMAT_COMMAND = [
"ruff",
"format",
"--quiet",
"--stdin-filename=tmp.py",
"-",
]


Expand Down
118 changes: 118 additions & 0 deletions tests/test_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import inspect

from colablinter.command import (
cell_check,
cell_check_fix,
cell_check_unsafe_fix,
cell_format,
)


def test_cell_check_real(monkeypatch):
logs = []
monkeypatch.setattr("colablinter.command.logger.info", lambda msg: logs.append(msg))

# Code with a real linting error (F541: f-string without placeholders)
bad_code = "f'test'"
cell_check(bad_code)

assert any("F541" in log for log in logs)


def test_cell_check_clean_real(monkeypatch):
logs = []
monkeypatch.setattr("colablinter.command.logger.info", lambda msg: logs.append(msg))

clean_code = "x = 1"
cell_check(clean_code)

assert "No issues found. Code is clean." in logs


def test_cell_check_fix_real():
# Code that can be fixed automatically (F541)
code = "f'test'"
fixed = cell_check_fix(code)
assert fixed == "'test'"


def test_cell_check_unsafe_fix_real():
# Unsafe fix: F632 (is comparison to literal)
code = "x is 'a'"
fixed = cell_check_unsafe_fix(code)
assert fixed == "x == 'a'"


def test_cell_format_real():
# Formatting: messy spacing
code = "x=1+2"
formatted = cell_format(code)
assert formatted == "x = 1 + 2"


def test_cell_check_ignore_f401(monkeypatch):
# F401 should be ignored even in complex cases
# We must sort imports as Ruff expects to avoid I001 error
code = inspect.cleandoc("""
import os
import sys
from datetime import datetime

x = 1
""")
logs = []
monkeypatch.setattr("colablinter.command.logger.info", lambda msg: logs.append(msg))

cell_check(code)
assert "No issues found. Code is clean." in logs


def test_cell_check_unsafe_fix_multiline():
# SIM105 (Use contextlib.suppress)
code = inspect.cleandoc("""
try:
os.remove("file.txt")
except OSError:
pass
""")
fixed = cell_check_unsafe_fix(code)
assert fixed is not None
assert "contextlib.suppress" in fixed


def test_cell_format_complex():
# Complex formatting: nested lists, dicts, and long lines
code = "d={'a':1,'b':[1,2,3],'c':{'d':4}} #comment"
formatted = cell_format(code)
expected = """d = {"a": 1, "b": [1, 2, 3], "c": {"d": 4}} # comment"""
assert formatted == expected


def test_preserve_strings_and_comments(monkeypatch):

# Ensure that linting/formatting doesn't mess up strings or comments
code = inspect.cleandoc("""
def greet():
\"\"\"This is a docstring with == None inside.\"\"\"
s = "x == None" # literal string
# real comment: x == None
return s
""")
# cell_check should find NO issues because E711 shouldn't trigger inside strings/comments
logs = []
monkeypatch.setattr("colablinter.command.logger.info", lambda msg: logs.append(msg))

cell_check(code)
assert "No issues found. Code is clean." in logs


def test_cell_check_unsafe_fix_complex():
# Complex unsafe fix: SIM102 (Nested if statements)
code = inspect.cleandoc("""
if a:
if b:
pass
""")
fixed = cell_check_unsafe_fix(code)
assert fixed is not None
assert "if a and b:" in fixed
25 changes: 25 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from IPython.core.interactiveshell import InteractiveShell

from colablinter import load_ipython_extension


def test_load_ipython_extension(monkeypatch):
shell = InteractiveShell()
registered = False
clautofix_called = False

def mock_register_magics(magics_class):
nonlocal registered
registered = True

def mock_run_line_magic(name, value):
nonlocal clautofix_called
if name == "clautofix" and value == "on":
clautofix_called = True

monkeypatch.setattr(shell, "register_magics", mock_register_magics)
monkeypatch.setattr(shell, "run_line_magic", mock_run_line_magic)

load_ipython_extension(shell)
assert registered
assert clautofix_called
69 changes: 69 additions & 0 deletions tests/test_magics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import pytest
from IPython.core.interactiveshell import ExecutionInfo, InteractiveShell

from colablinter.magics import ColabLinterMagics


@pytest.fixture
def shell():
return InteractiveShell()


@pytest.fixture
def magics(shell):
return ColabLinterMagics(shell=shell)


def test_clcheck_integration(magics, monkeypatch):
cells_run = []
monkeypatch.setattr(
magics.shell, "run_cell", lambda cell, **kwargs: cells_run.append(cell)
)

logs = []
monkeypatch.setattr("colablinter.magics.logger.info", lambda msg: logs.append(msg))

magics.clcheck("", "f'test'")

assert any("F541" in log for log in logs)
assert "f'test'" in cells_run


def test_clunsafefix_integration(magics, monkeypatch):
cells_run = []
monkeypatch.setattr(
magics.shell, "run_cell", lambda cell, **kwargs: cells_run.append(cell)
)

next_inputs = []
monkeypatch.setattr(
magics.shell, "set_next_input", lambda code, replace: next_inputs.append(code)
)

magics.clunsafefix("", "x is 'a'")

assert len(cells_run) == 1
assert "x == 'a'" in cells_run[0]
assert "x == 'a'" in next_inputs[0]


def test_clautofix_toggle(magics, monkeypatch):
magics.clautofix("off")
assert magics._is_autofix_active is False

magics.clautofix("on")
assert magics._is_autofix_active is True


def test_autofix_event_integration(magics, monkeypatch):
next_inputs = []
monkeypatch.setattr(
magics.shell, "set_next_input", lambda code, replace: next_inputs.append(code)
)

magics.clautofix("on")
info = ExecutionInfo("x=1+2", False, False, True, "cell_id")
magics._ColabLinterMagics__autofix(info)

assert len(next_inputs) == 1
assert "x = 1 + 2" in next_inputs[0]
Loading
Loading