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
39 changes: 39 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,42 @@ package-lock.json
node_modules
dist
nohup.out

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

# Claude settings
.claude/*

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

# IDE files
.idea/
*.swp
*.swo
*~
.DS_Store

# Build artifacts
build/
dist/
*.egg-info/
.eggs/
*.egg

# MyPy
.mypy_cache/
.dmypy.json
dmypy.json
406 changes: 406 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
[tool.poetry]
name = "awspx"
version = "0.1.0"
description = "A graph-based tool for visualizing effective access and resource relationships within AWS"
authors = ["Your Name <[email protected]>"]
readme = "README.md"
license = "MIT"
packages = [{include = "lib"}, {include = "scripts"}]

[tool.poetry.dependencies]
python = "^3.11"
boto3 = "*"
botocore = "*"
gitpython = "*"
neo4j = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
awspx = "cli:main"
test = "scripts.test_runner:main"
tests = "scripts.test_runner:main"

[tool.pytest.ini_options]
minversion = "8.0"
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*", "*Tests"]
python_functions = ["test_*"]
addopts = [
"-v",
"--strict-markers",
"--tb=short",
"--cov=lib",
"--cov-report=html",
"--cov-report=xml",
"--cov-report=term-missing:skip-covered",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Tests that take a long time to run",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["lib"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/virtualenv/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self\\.debug:",
"if __name__ == .__main__.:",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if False:",
"pass",
]
show_missing = true
precision = 2
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 scripts/__init__.py
Empty file.
14 changes: 14 additions & 0 deletions scripts/test_runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env python3
"""Simple test runner script to enable poetry run test/tests commands."""
import subprocess
import sys


def main():
"""Run pytest with all configured options."""
result = subprocess.run([sys.executable, "-m", "pytest"] + sys.argv[1:])
sys.exit(result.returncode)


if __name__ == "__main__":
main()
Empty file added tests/__init__.py
Empty file.
108 changes: 108 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Shared pytest fixtures and configuration for all tests.
"""
import os
import sys
import tempfile
from pathlib import Path
from unittest.mock import Mock, MagicMock

import pytest

# Add the project root to 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 mock_boto3_client():
"""Mock boto3 client for AWS service testing."""
client = MagicMock()
client.describe_instances.return_value = {
'Reservations': []
}
client.list_buckets.return_value = {
'Buckets': []
}
return client


@pytest.fixture
def mock_boto3_session():
"""Mock boto3 session."""
session = MagicMock()
session.client = MagicMock(return_value=mock_boto3_client())
session.get_available_regions = MagicMock(return_value=['us-east-1', 'us-west-2'])
return session


@pytest.fixture
def mock_neo4j_driver():
"""Mock Neo4j driver for graph database testing."""
driver = MagicMock()
session = MagicMock()
driver.session.return_value = session
return driver


@pytest.fixture
def sample_aws_config():
"""Sample AWS configuration for testing."""
return {
'aws_access_key_id': 'test_access_key',
'aws_secret_access_key': 'test_secret_key',
'region': 'us-east-1'
}


@pytest.fixture
def sample_aws_resource():
"""Sample AWS resource data for testing."""
return {
'ResourceType': 'EC2Instance',
'ResourceId': 'i-1234567890abcdef0',
'Properties': {
'InstanceType': 't2.micro',
'State': 'running',
'Tags': [
{'Key': 'Name', 'Value': 'TestInstance'}
]
}
}


@pytest.fixture(autouse=True)
def reset_environment():
"""Reset environment variables before each test."""
original_env = os.environ.copy()
yield
os.environ.clear()
os.environ.update(original_env)


@pytest.fixture
def mock_console(mocker):
"""Mock console for output testing."""
console = mocker.MagicMock()
console.notice = mocker.MagicMock()
console.task = mocker.MagicMock()
console.error = mocker.MagicMock()
console.info = mocker.MagicMock()
return console


@pytest.fixture
def mock_git_repo():
"""Mock git repository for update testing."""
repo = MagicMock()
repo.head.commit = "abc123"
repo.remotes.origin = MagicMock()
repo.git = MagicMock()
return repo
Empty file added tests/integration/__init__.py
Empty file.
102 changes: 102 additions & 0 deletions tests/test_setup_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""
Validation tests to ensure the testing infrastructure is properly set up.
"""
import sys
from pathlib import Path

import pytest


class TestSetupValidation:
"""Validate that the testing infrastructure is properly configured."""

def test_project_imports(self):
"""Test that project modules can be imported."""
# These imports should work if the project is properly configured
import lib
import lib.aws
import lib.graph
import lib.util

assert lib is not None
assert lib.aws is not None
assert lib.graph is not None
assert lib.util is not None

def test_fixtures_available(self, temp_dir, mock_boto3_client, sample_aws_config):
"""Test that pytest fixtures are available and working."""
assert temp_dir.exists()
assert temp_dir.is_dir()

assert mock_boto3_client is not None
assert hasattr(mock_boto3_client, 'describe_instances')

assert sample_aws_config is not None
assert 'aws_access_key_id' in sample_aws_config
assert 'region' in sample_aws_config

def test_markers_configured(self, request):
"""Test that custom markers are properly configured."""
markers = request.config.getini("markers")
marker_names = [m.split(":")[0].strip() for m in markers]

assert "unit" in marker_names
assert "integration" in marker_names
assert "slow" in marker_names

@pytest.mark.unit
def test_unit_marker(self):
"""Test that unit marker works."""
assert True

@pytest.mark.integration
def test_integration_marker(self):
"""Test that integration marker works."""
assert True

@pytest.mark.slow
def test_slow_marker(self):
"""Test that slow marker works."""
assert True

def test_coverage_configured(self):
"""Test that coverage is properly configured."""
# This test will pass if coverage is running
# The actual coverage configuration is tested by running pytest with coverage
assert True

def test_python_version(self):
"""Test that we're running on the expected Python version."""
assert sys.version_info >= (3, 11)

def test_test_directories_exist(self):
"""Test that test directories are properly created."""
test_root = Path(__file__).parent

assert test_root.exists()
assert (test_root / "unit").exists()
assert (test_root / "integration").exists()
assert (test_root / "conftest.py").exists()

def test_mock_fixtures(self, mock_console, mock_neo4j_driver, mock_git_repo):
"""Test that mock fixtures work properly."""
# Test mock_console
mock_console.notice("test")
mock_console.notice.assert_called_once_with("test")

# Test mock_neo4j_driver
session = mock_neo4j_driver.session()
assert session is not None

# Test mock_git_repo
assert mock_git_repo.head.commit == "abc123"

def test_environment_reset(self):
"""Test that environment is properly reset between tests."""
import os

# Set a test variable
os.environ['TEST_VAR'] = 'test_value'
assert os.environ.get('TEST_VAR') == 'test_value'

# The reset_environment fixture should clean this up after the test
Empty file added tests/unit/__init__.py
Empty file.