Skip to content
Open
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
53 changes: 53 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,57 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Testing
.pytest_cache/
.coverage
htmlcov/
coverage.xml
*.cover
.hypothesis/
.tox/

# Virtual environments
.venv/
venv/
ENV/
env/
virtualenv/

# IDEs
.vscode/
.idea/
*.swp
*.swo
*~

# Claude
.claude/*

# OS
.DS_Store
Thumbs.db

# Project specific
*.log
*.pid
*.seed
*.pid.lock
Empty file added mamba_chat/__init__.py
Empty file.
277 changes: 277 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

94 changes: 94 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
[tool.poetry]
name = "mamba-chat"
version = "0.1.0"
description = "Mamba Chat - A conversational AI implementation using Mamba architecture"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
packages = [{include = "mamba_chat"}]

[tool.poetry.dependencies]
python = "^3.9"
# Production dependencies are managed in requirements.txt
# This pyproject.toml focuses on testing infrastructure

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
pytest-cov = "^4.1.0"
pytest-mock = "^3.12.0"

# Poetry scripts are not needed for pytest
# Use 'poetry run test' or 'poetry run tests' commands directly

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-v",
"--strict-markers",
"--tb=short",
"--cov=.",
"--cov-config=pyproject.toml",
"--cov-report=term-missing:skip-covered",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=80",
]
markers = [
"unit: marks tests as unit tests (fast, isolated)",
"integration: marks tests as integration tests (may require external resources)",
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
]
norecursedirs = [
".git",
".tox",
"dist",
"build",
"*.egg",
"__pycache__",
".pytest_cache",
"htmlcov",
]

[tool.coverage.run]
source = ["."]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/virtualenv/*",
"*/.venv/*",
"*/setup.py",
"*/conftest.py",
"*/site-packages/*",
".venv/*",
"tests/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"def __str__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
show_missing = true
skip_covered = true
fail_under = 80

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Empty file added tests/__init__.py
Empty file.
157 changes: 157 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""
Shared pytest fixtures and configuration for all tests.
"""
import os
import sys
import tempfile
from pathlib import Path
from unittest.mock import Mock, patch

import pytest

# Add the project root to the Python path
PROJECT_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(PROJECT_ROOT))


@pytest.fixture
def temp_dir():
"""Create a temporary directory for test files."""
with tempfile.TemporaryDirectory() as tmpdir:
yield Path(tmpdir)


@pytest.fixture
def temp_file(temp_dir):
"""Create a temporary file in the temp directory."""
def _create_temp_file(name="test_file.txt", content=""):
file_path = temp_dir / name
file_path.write_text(content)
return file_path
return _create_temp_file


@pytest.fixture
def mock_config():
"""Provide a mock configuration object."""
config = Mock()
config.model_name = "test-model"
config.batch_size = 32
config.learning_rate = 0.001
config.num_epochs = 10
config.device = "cpu"
return config


@pytest.fixture
def mock_model():
"""Provide a mock model object."""
model = Mock()
model.forward = Mock(return_value=Mock(loss=0.5))
model.generate = Mock(return_value=[1, 2, 3, 4, 5])
model.eval = Mock()
model.train = Mock()
return model


@pytest.fixture
def mock_tokenizer():
"""Provide a mock tokenizer object."""
tokenizer = Mock()
tokenizer.encode = Mock(return_value=[1, 2, 3, 4, 5])
tokenizer.decode = Mock(return_value="decoded text")
tokenizer.pad_token_id = 0
tokenizer.eos_token_id = 1
return tokenizer


@pytest.fixture
def mock_dataset():
"""Provide a mock dataset object."""
dataset = Mock()
dataset.__len__ = Mock(return_value=100)
dataset.__getitem__ = Mock(return_value={"input_ids": [1, 2, 3], "labels": [4, 5, 6]})
return dataset


@pytest.fixture
def mock_torch():
"""Mock torch module for tests that don't need actual PyTorch."""
with patch.dict(sys.modules, {"torch": Mock()}):
yield sys.modules["torch"]


@pytest.fixture
def isolated_filesystem(temp_dir, monkeypatch):
"""Change to a temporary directory for the test."""
original_dir = Path.cwd()
monkeypatch.chdir(temp_dir)
yield temp_dir
monkeypatch.chdir(original_dir)


@pytest.fixture(autouse=True)
def reset_environment(monkeypatch):
"""Reset environment variables that might affect tests."""
# Clear any CUDA-related environment variables
cuda_vars = ["CUDA_VISIBLE_DEVICES", "CUDA_DEVICE_ORDER"]
for var in cuda_vars:
monkeypatch.delenv(var, raising=False)

# Set deterministic behavior
monkeypatch.setenv("PYTHONHASHSEED", "0")


@pytest.fixture
def capture_logs():
"""Capture log messages during tests."""
import logging
from io import StringIO

log_capture = StringIO()
handler = logging.StreamHandler(log_capture)
handler.setLevel(logging.DEBUG)

# Add handler to root logger
root_logger = logging.getLogger()
root_logger.addHandler(handler)
original_level = root_logger.level
root_logger.setLevel(logging.DEBUG)

yield log_capture

# Cleanup
root_logger.removeHandler(handler)
root_logger.setLevel(original_level)


# Markers for slow tests
def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line(
"markers", "unit: mark test as a unit test"
)
config.addinivalue_line(
"markers", "integration: mark test as an integration test"
)
config.addinivalue_line(
"markers", "slow: mark test as slow running"
)


# Skip slow tests by default unless --runslow is provided
def pytest_addoption(parser):
"""Add custom command line options."""
parser.addoption(
"--runslow", action="store_true", default=False, help="run slow tests"
)


def pytest_collection_modifyitems(config, items):
"""Modify test collection to skip slow tests by default."""
if config.getoption("--runslow"):
return
skip_slow = pytest.mark.skip(reason="need --runslow option to run")
for item in items:
if "slow" in item.keywords:
item.add_marker(skip_slow)
Empty file added tests/integration/__init__.py
Empty file.
Loading