restructure of dirs, huge docs update
This commit is contained in:
384
app/tests/conftest.py
Normal file
384
app/tests/conftest.py
Normal file
@@ -0,0 +1,384 @@
|
||||
"""
|
||||
Pytest configuration and fixtures for SneakyScanner tests.
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from web.app import create_app
|
||||
from web.models import Base, Scan
|
||||
from web.utils.settings import PasswordManager, SettingsManager
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def test_db():
|
||||
"""
|
||||
Create a temporary test database.
|
||||
|
||||
Yields a SQLAlchemy session for testing, then cleans up.
|
||||
"""
|
||||
# Create temporary database file
|
||||
db_fd, db_path = tempfile.mkstemp(suffix='.db')
|
||||
|
||||
# Create engine and session
|
||||
engine = create_engine(f'sqlite:///{db_path}', echo=False)
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
yield session
|
||||
|
||||
# Cleanup
|
||||
session.close()
|
||||
os.close(db_fd)
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_scan_report():
|
||||
"""
|
||||
Sample scan report matching the structure from scanner.py.
|
||||
|
||||
Returns a dictionary representing a typical scan output.
|
||||
"""
|
||||
return {
|
||||
'title': 'Test Scan',
|
||||
'scan_time': '2025-11-14T10:30:00Z',
|
||||
'scan_duration': 125.5,
|
||||
'config_file': '/app/configs/test.yaml',
|
||||
'sites': [
|
||||
{
|
||||
'name': 'Test Site',
|
||||
'ips': [
|
||||
{
|
||||
'address': '192.168.1.10',
|
||||
'expected': {
|
||||
'ping': True,
|
||||
'tcp_ports': [22, 80, 443],
|
||||
'udp_ports': [53],
|
||||
'services': []
|
||||
},
|
||||
'actual': {
|
||||
'ping': True,
|
||||
'tcp_ports': [22, 80, 443, 8080],
|
||||
'udp_ports': [53],
|
||||
'services': [
|
||||
{
|
||||
'port': 22,
|
||||
'service': 'ssh',
|
||||
'product': 'OpenSSH',
|
||||
'version': '8.9p1',
|
||||
'extrainfo': 'Ubuntu',
|
||||
'ostype': 'Linux'
|
||||
},
|
||||
{
|
||||
'port': 443,
|
||||
'service': 'https',
|
||||
'product': 'nginx',
|
||||
'version': '1.24.0',
|
||||
'extrainfo': '',
|
||||
'ostype': '',
|
||||
'http_info': {
|
||||
'protocol': 'https',
|
||||
'screenshot': 'screenshots/192_168_1_10_443.png',
|
||||
'certificate': {
|
||||
'subject': 'CN=example.com',
|
||||
'issuer': 'CN=Let\'s Encrypt Authority',
|
||||
'serial_number': '123456789',
|
||||
'not_valid_before': '2025-01-01T00:00:00Z',
|
||||
'not_valid_after': '2025-12-31T23:59:59Z',
|
||||
'days_until_expiry': 365,
|
||||
'sans': ['example.com', 'www.example.com'],
|
||||
'is_self_signed': False,
|
||||
'tls_versions': {
|
||||
'TLS 1.2': {
|
||||
'supported': True,
|
||||
'cipher_suites': [
|
||||
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
|
||||
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
|
||||
]
|
||||
},
|
||||
'TLS 1.3': {
|
||||
'supported': True,
|
||||
'cipher_suites': [
|
||||
'TLS_AES_256_GCM_SHA384',
|
||||
'TLS_AES_128_GCM_SHA256'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'port': 80,
|
||||
'service': 'http',
|
||||
'product': 'nginx',
|
||||
'version': '1.24.0',
|
||||
'extrainfo': '',
|
||||
'ostype': '',
|
||||
'http_info': {
|
||||
'protocol': 'http',
|
||||
'screenshot': 'screenshots/192_168_1_10_80.png'
|
||||
}
|
||||
},
|
||||
{
|
||||
'port': 8080,
|
||||
'service': 'http',
|
||||
'product': 'Jetty',
|
||||
'version': '9.4.48',
|
||||
'extrainfo': '',
|
||||
'ostype': ''
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_config_file(tmp_path):
|
||||
"""
|
||||
Create a sample YAML config file for testing.
|
||||
|
||||
Args:
|
||||
tmp_path: pytest temporary directory fixture
|
||||
|
||||
Returns:
|
||||
Path to created config file
|
||||
"""
|
||||
config_data = {
|
||||
'title': 'Test Scan',
|
||||
'sites': [
|
||||
{
|
||||
'name': 'Test Site',
|
||||
'ips': [
|
||||
{
|
||||
'address': '192.168.1.10',
|
||||
'expected': {
|
||||
'ping': True,
|
||||
'tcp_ports': [22, 80, 443],
|
||||
'udp_ports': [53],
|
||||
'services': ['ssh', 'http', 'https']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
config_file = tmp_path / 'test_config.yaml'
|
||||
with open(config_file, 'w') as f:
|
||||
yaml.dump(config_data, f)
|
||||
|
||||
return str(config_file)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_invalid_config_file(tmp_path):
|
||||
"""
|
||||
Create an invalid config file for testing validation.
|
||||
|
||||
Returns:
|
||||
Path to invalid config file
|
||||
"""
|
||||
config_file = tmp_path / 'invalid_config.yaml'
|
||||
with open(config_file, 'w') as f:
|
||||
f.write("invalid: yaml: content: [missing closing bracket")
|
||||
|
||||
return str(config_file)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def app():
|
||||
"""
|
||||
Create Flask application for testing.
|
||||
|
||||
Returns:
|
||||
Configured Flask app instance with test database
|
||||
"""
|
||||
# Create temporary database
|
||||
db_fd, db_path = tempfile.mkstemp(suffix='.db')
|
||||
|
||||
# Create app with test config
|
||||
test_config = {
|
||||
'TESTING': True,
|
||||
'SQLALCHEMY_DATABASE_URI': f'sqlite:///{db_path}',
|
||||
'SECRET_KEY': 'test-secret-key'
|
||||
}
|
||||
|
||||
app = create_app(test_config)
|
||||
|
||||
yield app
|
||||
|
||||
# Cleanup
|
||||
os.close(db_fd)
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def client(app):
|
||||
"""
|
||||
Create Flask test client.
|
||||
|
||||
Args:
|
||||
app: Flask application fixture
|
||||
|
||||
Returns:
|
||||
Flask test client for making API requests
|
||||
"""
|
||||
return app.test_client()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def db(app):
|
||||
"""
|
||||
Alias for database session that works with Flask app context.
|
||||
|
||||
Args:
|
||||
app: Flask application fixture
|
||||
|
||||
Returns:
|
||||
SQLAlchemy session
|
||||
"""
|
||||
with app.app_context():
|
||||
yield app.db_session
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_scan(db):
|
||||
"""
|
||||
Create a sample scan in the database for testing.
|
||||
|
||||
Args:
|
||||
db: Database session fixture
|
||||
|
||||
Returns:
|
||||
Scan model instance
|
||||
"""
|
||||
scan = Scan(
|
||||
timestamp=datetime.utcnow(),
|
||||
status='completed',
|
||||
config_file='/app/configs/test.yaml',
|
||||
title='Test Scan',
|
||||
duration=125.5,
|
||||
triggered_by='test',
|
||||
json_path='/app/output/scan_report_20251114_103000.json',
|
||||
html_path='/app/output/scan_report_20251114_103000.html',
|
||||
zip_path='/app/output/scan_report_20251114_103000.zip',
|
||||
screenshot_dir='/app/output/scan_report_20251114_103000_screenshots'
|
||||
)
|
||||
|
||||
db.add(scan)
|
||||
db.commit()
|
||||
db.refresh(scan)
|
||||
|
||||
return scan
|
||||
|
||||
|
||||
# Authentication Fixtures
|
||||
|
||||
@pytest.fixture
|
||||
def app_password():
|
||||
"""
|
||||
Test password for authentication tests.
|
||||
|
||||
Returns:
|
||||
Test password string
|
||||
"""
|
||||
return 'testpassword123'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db_with_password(db, app_password):
|
||||
"""
|
||||
Database session with application password set.
|
||||
|
||||
Args:
|
||||
db: Database session fixture
|
||||
app_password: Test password fixture
|
||||
|
||||
Returns:
|
||||
Database session with password configured
|
||||
"""
|
||||
settings_manager = SettingsManager(db)
|
||||
PasswordManager.set_app_password(settings_manager, app_password)
|
||||
return db
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db_no_password(app):
|
||||
"""
|
||||
Database session without application password set.
|
||||
|
||||
Args:
|
||||
app: Flask application fixture
|
||||
|
||||
Returns:
|
||||
Database session without password
|
||||
"""
|
||||
with app.app_context():
|
||||
# Clear any password that might be set
|
||||
settings_manager = SettingsManager(app.db_session)
|
||||
settings_manager.delete('app_password')
|
||||
yield app.db_session
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def authenticated_client(client, db_with_password, app_password):
|
||||
"""
|
||||
Flask test client with authenticated session.
|
||||
|
||||
Args:
|
||||
client: Flask test client fixture
|
||||
db_with_password: Database with password set
|
||||
app_password: Test password fixture
|
||||
|
||||
Returns:
|
||||
Test client with active session
|
||||
"""
|
||||
# Log in
|
||||
client.post('/auth/login', data={
|
||||
'password': app_password
|
||||
})
|
||||
return client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client_no_password(app):
|
||||
"""
|
||||
Flask test client with no password set (for setup testing).
|
||||
|
||||
Args:
|
||||
app: Flask application fixture
|
||||
|
||||
Returns:
|
||||
Test client for testing setup flow
|
||||
"""
|
||||
# Create temporary database without password
|
||||
db_fd, db_path = tempfile.mkstemp(suffix='.db')
|
||||
|
||||
test_config = {
|
||||
'TESTING': True,
|
||||
'SQLALCHEMY_DATABASE_URI': f'sqlite:///{db_path}',
|
||||
'SECRET_KEY': 'test-secret-key'
|
||||
}
|
||||
|
||||
test_app = create_app(test_config)
|
||||
test_client = test_app.test_client()
|
||||
|
||||
yield test_client
|
||||
|
||||
# Cleanup
|
||||
os.close(db_fd)
|
||||
os.unlink(db_path)
|
||||
Reference in New Issue
Block a user