Compare commits

..

2 Commits

Author SHA1 Message Date
roof 0773179962 Gitignore .coverage files
Unit Tests / test (push) Successful in 8m55s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:28:40 -04:00
roof 3a35cf72d3 Fix CI failures on root — mock OSError instead of relying on filesystem
Tests assumed write to /nonexistent/... fails, but CI runs as root where
Linux allows creating any path. Use unittest.mock.patch on builtins.open
with OSError side_effect so the test is environment-independent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:19:24 -04:00
8 changed files with 10 additions and 1069 deletions
+4
View File
@@ -87,3 +87,7 @@ backups/
# Temporary files
*.tmp
*.temp
# Coverage data
.coverage
htmlcov/
-36
View File
@@ -1,36 +0,0 @@
{
"cell_name": "modified",
"domain": "cell.local",
"ip_range": "10.0.0.0/24",
"network": {
"dns_port": 53,
"dhcp_range": "10.0.0.100-10.0.0.200",
"ntp_servers": ["pool.ntp.org"]
},
"wireguard": {
"port": 51820,
"private_key": "test_key",
"address": "10.0.0.1/24"
},
"email": {
"domain": "cell.local",
"smtp_port": 25,
"imap_port": 143
},
"calendar": {
"port": 5232,
"data_dir": "/app/data/calendar"
},
"files": {
"port": 8080,
"data_dir": "/app/data/files"
},
"routing": {
"nat_enabled": true,
"firewall_enabled": true
},
"vault": {
"ca_configured": true,
"fernet_configured": true
}
}
-389
View File
@@ -1,389 +0,0 @@
# Personal Internet Cell - Network Configuration Guide
This guide explains how to configure networking for the Personal Internet Cell to provide internet access to WireGuard VPN clients.
## Table of Contents
1. [Overview](#overview)
2. [Network Architecture](#network-architecture)
3. [Quick Setup](#quick-setup)
4. [Detailed Configuration](#detailed-configuration)
5. [Troubleshooting](#troubleshooting)
6. [Advanced Configuration](#advanced-configuration)
7. [Security Considerations](#security-considerations)
## Overview
The Personal Internet Cell provides a complete VPN solution with internet access. This requires proper configuration of:
- **IP Forwarding**: Allow traffic to pass through the server
- **NAT (Network Address Translation)**: Translate private IPs to public IPs
- **Routing**: Direct traffic from VPN clients to the internet
- **Firewall Rules**: Control traffic flow and security
## Network Architecture
```
Internet
[Host Server] (195.178.106.244)
├── [Docker Network] (172.20.0.0/16)
│ └── [WireGuard Container] (cell-wireguard)
│ └── [WireGuard Interface] (wg0: 10.0.0.1/24)
└── [VPN Clients] (10.0.0.2-10.0.0.254/24)
└── [Internet Access via NAT]
```
### Key Components
- **Host Interface**: `eth0` (or main network interface)
- **WireGuard Interface**: `wg0` (10.0.0.1/24)
- **Client Network**: `10.0.0.0/24`
- **NAT Translation**: Client IPs → Host IP
## Quick Setup
### 1. Run the Network Configuration Script
```bash
# Make the script executable (if not already done)
chmod +x /opt/pic/scripts/setup-network.sh
# Run the configuration
sudo /opt/pic/scripts/setup-network.sh setup
```
### 2. Verify Configuration
```bash
# Check status
sudo /opt/pic/scripts/setup-network.sh status
# Test configuration
sudo /opt/pic/scripts/setup-network.sh test
```
### 3. Connect a VPN Client
Use the generated WireGuard configuration to connect a client. The client should now have internet access.
## Detailed Configuration
### IP Forwarding
IP forwarding allows the server to route packets between different network interfaces.
**Enable on Host:**
```bash
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
sysctl -p
```
**Enable in Container:**
```bash
docker exec cell-wireguard sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
```
### NAT Configuration
NAT (Network Address Translation) allows VPN clients to access the internet using the server's public IP.
**Container NAT Rules:**
```bash
# Allow forwarding for WireGuard traffic
iptables -A FORWARD -i wg0 -j ACCEPT
iptables -A FORWARD -o wg0 -j ACCEPT
# NAT rule for internet access
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
```
**Host NAT Rules:**
```bash
# Allow traffic from WireGuard network
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
iptables -A FORWARD -i wg0 -j ACCEPT
iptables -A FORWARD -o wg0 -j ACCEPT
```
### Routing Configuration
**WireGuard Interface Setup:**
```bash
# Create WireGuard interface
ip link add dev wg0 type wireguard
# Set private key
wg set wg0 private-key /path/to/private-key
# Set listen port
wg set wg0 listen-port 51820
# Add IP address
ip addr add 10.0.0.1/24 dev wg0
# Bring interface up
ip link set wg0 up
# Add peers
wg set wg0 peer <public-key> allowed-ips 10.0.0.2/32
```
## Troubleshooting
### Common Issues
#### 1. VPN Connected but No Internet
**Symptoms:**
- WireGuard shows connected
- Can ping server (10.0.0.1)
- Cannot access internet
**Solutions:**
```bash
# Check IP forwarding
cat /proc/sys/net/ipv4/ip_forward
# Should return 1
# Check NAT rules
iptables -t nat -L POSTROUTING -n
# Should show MASQUERADE rule for 10.0.0.0/24
# Check forwarding rules
iptables -L FORWARD -n
# Should show ACCEPT rules for wg0
# Restart network configuration
sudo /opt/pic/scripts/setup-network.sh reset
sudo /opt/pic/scripts/setup-network.sh setup
```
#### 2. Cannot Connect to VPN
**Symptoms:**
- WireGuard client cannot connect
- No handshake in server logs
**Solutions:**
```bash
# Check WireGuard interface
docker exec cell-wireguard wg show
# Check if port 51820 is open
netstat -ulnp | grep 51820
# Check firewall rules
ufw status
iptables -L INPUT -n
# Check Docker port mapping
docker port cell-wireguard
```
#### 3. DNS Issues
**Symptoms:**
- Can ping IP addresses
- Cannot resolve domain names
**Solutions:**
```bash
# Check DNS configuration in client config
# Should include: DNS = 8.8.8.8, 1.1.1.1
# Test DNS from container
docker exec cell-wireguard nslookup google.com
# Check if DNS is being blocked
docker exec cell-wireguard iptables -L -n | grep 53
```
### Diagnostic Commands
```bash
# Check network status
sudo /opt/pic/scripts/setup-network.sh status
# Test connectivity from container
docker exec cell-wireguard ping -c 3 8.8.8.8
# Check routing table
docker exec cell-wireguard ip route show
# Check interface status
docker exec cell-wireguard ip addr show wg0
# Check NAT rules
docker exec cell-wireguard iptables -t nat -L -n
# Check forwarding rules
docker exec cell-wireguard iptables -L FORWARD -n
```
## Advanced Configuration
### Custom DNS Servers
To use custom DNS servers, modify the WireGuard client configuration:
```ini
[Interface]
PrivateKey = <private-key>
Address = 10.0.0.2/32
DNS = 1.1.1.1, 1.0.0.1, 8.8.8.8, 8.8.4.4
[Peer]
PublicKey = <server-public-key>
Endpoint = 195.178.106.244:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
```
### Split Tunneling
To allow only specific traffic through the VPN:
```ini
[Peer]
AllowedIPs = 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
# Only route private networks through VPN
```
### Port Forwarding
To forward specific ports to VPN clients:
```bash
# Forward port 8080 to client 10.0.0.2
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 10.0.0.2:8080
iptables -A FORWARD -p tcp -d 10.0.0.2 --dport 8080 -j ACCEPT
```
### Bandwidth Limiting
To limit bandwidth for VPN clients:
```bash
# Install tc (traffic control)
apt-get install iproute2
# Limit client 10.0.0.2 to 1Mbps
tc qdisc add dev wg0 root handle 1: htb default 30
tc class add dev wg0 parent 1: classid 1:1 htb rate 1mbit
tc class add dev wg0 parent 1:1 classid 1:10 htb rate 1mbit ceil 1mbit
tc filter add dev wg0 protocol ip parent 1:0 prio 1 u32 match ip dst 10.0.0.2 flowid 1:10
```
## Security Considerations
### Firewall Rules
**Basic Security Rules:**
```bash
# Drop invalid packets
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# Allow established connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow WireGuard traffic
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
# Allow SSH (if needed)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Drop everything else
iptables -A INPUT -j DROP
```
### Client Isolation
To prevent clients from communicating with each other:
```bash
# Block inter-client communication
iptables -A FORWARD -i wg0 -o wg0 -j DROP
```
### Logging
To log VPN traffic:
```bash
# Log all WireGuard traffic
iptables -A FORWARD -i wg0 -j LOG --log-prefix "WG-FORWARD: "
iptables -A FORWARD -o wg0 -j LOG --log-prefix "WG-FORWARD: "
# Log NAT traffic
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j LOG --log-prefix "WG-NAT: "
```
## Monitoring
### Real-time Monitoring
```bash
# Monitor WireGuard connections
watch -n 1 "docker exec cell-wireguard wg show"
# Monitor traffic
watch -n 1 "docker exec cell-wireguard wg show wg0 transfer"
# Monitor NAT rules
watch -n 1 "iptables -t nat -L POSTROUTING -n -v"
```
### Log Analysis
```bash
# Check system logs
journalctl -u pic-network.service -f
# Check iptables logs
tail -f /var/log/kern.log | grep WG-
# Check Docker logs
docker logs cell-wireguard -f
```
## Backup and Recovery
### Backup Configuration
```bash
# Backup iptables rules
iptables-save > /opt/pic/backups/iptables-backup-$(date +%Y%m%d).rules
# Backup WireGuard configuration
cp /opt/pic/config/wireguard/wg_confs/wg0.conf /opt/pic/backups/wg0-backup-$(date +%Y%m%d).conf
# Backup network script
cp /opt/pic/scripts/setup-network.sh /opt/pic/backups/setup-network-backup-$(date +%Y%m%d).sh
```
### Restore Configuration
```bash
# Restore iptables rules
iptables-restore < /opt/pic/backups/iptables-backup-YYYYMMDD.rules
# Restore WireGuard configuration
cp /opt/pic/backups/wg0-backup-YYYYMMDD.conf /opt/pic/config/wireguard/wg_confs/wg0.conf
docker restart cell-wireguard
```
## Support
If you encounter issues:
1. Check the troubleshooting section above
2. Run the diagnostic commands
3. Check the logs for error messages
4. Verify your network configuration
5. Test with a simple client configuration
For additional help, check the main Personal Internet Cell documentation or create an issue in the project repository.
-51
View File
@@ -1,51 +0,0 @@
#!/usr/bin/env python3
"""
Script to fix import statements in test files
"""
import os
import re
from pathlib import Path
def fix_imports_in_file(file_path):
"""Fix import statements in a test file"""
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Fix relative imports to absolute imports from api package
content = re.sub(r'from \.(\w+) import', r'from \1 import', content)
content = re.sub(r'import \.(\w+)', r'import \1', content)
# Add path setup if not present
if 'sys.path.insert' not in content and 'api_dir' not in content:
path_setup = '''import sys
from pathlib import Path
# Add api directory to path
api_dir = Path(__file__).parent.parent / 'api'
sys.path.insert(0, str(api_dir))
'''
# Insert after the first import line
lines = content.split('\n')
for i, line in enumerate(lines):
if line.startswith('import ') or line.startswith('from '):
lines.insert(i, path_setup.rstrip())
break
content = '\n'.join(lines)
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
print(f"Fixed imports in {file_path}")
def main():
"""Fix all test files"""
tests_dir = Path('tests')
for test_file in tests_dir.glob('test_*.py'):
if test_file.name not in ['test_cli_tool.py', 'test_peer_registry.py']: # Already fixed
fix_imports_in_file(test_file)
if __name__ == '__main__':
main()
-31
View File
@@ -1,31 +0,0 @@
#!/usr/bin/env python3
"""
Fix import statements in test files
"""
import os
import re
from pathlib import Path
def fix_imports_in_file(file_path):
"""Fix import statements in a test file"""
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Replace 'from api.' with 'from .'
content = re.sub(r'from api\.', 'from .', content)
content = re.sub(r'import api\.', 'import .', content)
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
print(f"Fixed imports in {file_path}")
def main():
tests_dir = Path('tests')
for test_file in tests_dir.glob('test_*.py'):
fix_imports_in_file(test_file)
if __name__ == '__main__':
main()
-559
View File
@@ -1,559 +0,0 @@
#!/usr/bin/env python3
"""
Comprehensive tests for Flask app endpoints
"""
import unittest
import sys
import os
import tempfile
import shutil
import json
from pathlib import Path
from unittest.mock import patch, MagicMock
# Add api directory to path
api_dir = Path(__file__).parent / 'api'
sys.path.insert(0, str(api_dir))
class TestFlaskAppEndpoints(unittest.TestCase):
def setUp(self):
"""Set up test environment"""
# Create temporary directories
self.test_dir = tempfile.mkdtemp()
self.data_dir = os.path.join(self.test_dir, 'data')
self.config_dir = os.path.join(self.test_dir, 'config')
os.makedirs(self.data_dir, exist_ok=True)
os.makedirs(self.config_dir, exist_ok=True)
# Set environment variables
os.environ['TESTING'] = 'true'
os.environ['LOG_LEVEL'] = 'ERROR'
# Import and create app
from app import app
self.app = app
self.client = app.test_client()
# Mock external dependencies
self.patchers = []
# Mock subprocess.run
subprocess_patcher = patch('subprocess.run')
self.mock_subprocess = subprocess_patcher.start()
self.mock_subprocess.return_value.returncode = 0
self.mock_subprocess.return_value.stdout = b"test output"
self.patchers.append(subprocess_patcher)
# Mock docker
docker_patcher = patch('docker.from_env')
self.mock_docker = docker_patcher.start()
self.mock_docker_client = MagicMock()
self.mock_docker.return_value = self.mock_docker_client
self.patchers.append(docker_patcher)
# Mock file operations
file_patcher = patch('builtins.open', create=True)
self.mock_file = file_patcher.start()
self.mock_file.return_value.__enter__.return_value.read.return_value = '{}'
self.patchers.append(file_patcher)
def tearDown(self):
"""Clean up test environment"""
shutil.rmtree(self.test_dir)
for patcher in self.patchers:
patcher.stop()
def test_health_endpoint(self):
"""Test /health endpoint"""
response = self.client.get('/health')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
def test_api_status_endpoint(self):
"""Test /api/status endpoint"""
response = self.client.get('/api/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
def test_api_config_get_endpoint(self):
"""Test GET /api/config endpoint"""
response = self.client.get('/api/config')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, dict)
def test_api_config_put_endpoint(self):
"""Test PUT /api/config endpoint"""
test_config = {'test': 'value'}
response = self.client.put('/api/config',
data=json.dumps(test_config),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_config_backup_endpoint(self):
"""Test POST /api/config/backup endpoint"""
response = self.client.post('/api/config/backup')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('backup_id', data)
def test_api_config_backups_endpoint(self):
"""Test GET /api/config/backups endpoint"""
response = self.client.get('/api/config/backups')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
def test_api_config_restore_endpoint(self):
"""Test POST /api/config/restore/<backup_id> endpoint"""
response = self.client.post('/api/config/restore/test_backup')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_config_export_endpoint(self):
"""Test GET /api/config/export endpoint"""
response = self.client.get('/api/config/export')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, dict)
def test_api_config_import_endpoint(self):
"""Test POST /api/config/import endpoint"""
test_config = {'test': 'value'}
response = self.client.post('/api/config/import',
data=json.dumps(test_config),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_services_bus_status_endpoint(self):
"""Test GET /api/services/bus/status endpoint"""
response = self.client.get('/api/services/bus/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('services', data)
def test_api_services_bus_events_endpoint(self):
"""Test GET /api/services/bus/events endpoint"""
response = self.client.get('/api/services/bus/events')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
def test_api_services_bus_start_endpoint(self):
"""Test POST /api/services/bus/services/<service_name>/start endpoint"""
response = self.client.post('/api/services/bus/services/test/start')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_services_bus_stop_endpoint(self):
"""Test POST /api/services/bus/services/<service_name>/stop endpoint"""
response = self.client.post('/api/services/bus/services/test/stop')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_services_bus_restart_endpoint(self):
"""Test POST /api/services/bus/services/<service_name>/restart endpoint"""
response = self.client.post('/api/services/bus/services/test/restart')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_logs_services_endpoint(self):
"""Test GET /api/logs/services/<service> endpoint"""
response = self.client.get('/api/logs/services/test')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
def test_api_logs_search_endpoint(self):
"""Test POST /api/logs/search endpoint"""
search_data = {'query': 'test', 'level': 'INFO'}
response = self.client.post('/api/logs/search',
data=json.dumps(search_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
def test_api_logs_export_endpoint(self):
"""Test POST /api/logs/export endpoint"""
export_data = {'format': 'json', 'filters': {}}
response = self.client.post('/api/logs/export',
data=json.dumps(export_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('export_path', data)
def test_api_logs_statistics_endpoint(self):
"""Test GET /api/logs/statistics endpoint"""
response = self.client.get('/api/logs/statistics')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('total_entries', data)
def test_api_logs_rotate_endpoint(self):
"""Test POST /api/logs/rotate endpoint"""
response = self.client.post('/api/logs/rotate')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_dns_records_endpoints(self):
"""Test DNS records endpoints"""
# GET
response = self.client.get('/api/dns/records')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
# POST
record_data = {'name': 'test.example.com', 'type': 'A', 'value': '192.168.1.1'}
response = self.client.post('/api/dns/records',
data=json.dumps(record_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# DELETE
response = self.client.delete('/api/dns/records',
data=json.dumps(record_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_dhcp_endpoints(self):
"""Test DHCP endpoints"""
# GET leases
response = self.client.get('/api/dhcp/leases')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
# POST reservation
reservation_data = {'mac': '00:11:22:33:44:55', 'ip': '192.168.1.100'}
response = self.client.post('/api/dhcp/reservations',
data=json.dumps(reservation_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# DELETE reservation
response = self.client.delete('/api/dhcp/reservations',
data=json.dumps(reservation_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_ntp_status_endpoint(self):
"""Test GET /api/ntp/status endpoint"""
response = self.client.get('/api/ntp/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
def test_api_network_info_endpoint(self):
"""Test GET /api/network/info endpoint"""
response = self.client.get('/api/network/info')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('interfaces', data)
def test_api_dns_status_endpoint(self):
"""Test GET /api/dns/status endpoint"""
response = self.client.get('/api/dns/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
def test_api_network_test_endpoint(self):
"""Test POST /api/network/test endpoint"""
test_data = {'target': '8.8.8.8', 'type': 'ping'}
response = self.client.post('/api/network/test',
data=json.dumps(test_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_wireguard_endpoints(self):
"""Test WireGuard endpoints"""
# GET keys
response = self.client.get('/api/wireguard/keys')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('public_key', data)
# POST generate peer keys
response = self.client.post('/api/wireguard/keys/peer')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('public_key', data)
# GET config
response = self.client.get('/api/wireguard/config')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('config', data)
# GET peers
response = self.client.get('/api/wireguard/peers')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
# POST add peer
peer_data = {'peer': 'test_peer', 'ip': '10.0.0.1', 'public_key': 'test_key'}
response = self.client.post('/api/wireguard/peers',
data=json.dumps(peer_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# DELETE remove peer
response = self.client.delete('/api/wireguard/peers',
data=json.dumps({'peer': 'test_peer'}),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# GET status
response = self.client.get('/api/wireguard/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
def test_api_peers_endpoints(self):
"""Test peers endpoints"""
# GET peers
response = self.client.get('/api/peers')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
# POST add peer
peer_data = {'peer': 'test_peer', 'ip': '10.0.0.1'}
response = self.client.post('/api/peers',
data=json.dumps(peer_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# DELETE remove peer
response = self.client.delete('/api/peers/test_peer')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
def test_api_email_endpoints(self):
"""Test email endpoints"""
# GET users
response = self.client.get('/api/email/users')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
# POST create user
user_data = {'username': 'test_user', 'email': 'test@example.com'}
response = self.client.post('/api/email/users',
data=json.dumps(user_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# DELETE user
response = self.client.delete('/api/email/users/test_user')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# GET status
response = self.client.get('/api/email/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
def test_api_calendar_endpoints(self):
"""Test calendar endpoints"""
# GET users
response = self.client.get('/api/calendar/users')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
# POST create user
user_data = {'username': 'test_user', 'email': 'test@example.com'}
response = self.client.post('/api/calendar/users',
data=json.dumps(user_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# DELETE user
response = self.client.delete('/api/calendar/users/test_user')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# GET status
response = self.client.get('/api/calendar/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
def test_api_files_endpoints(self):
"""Test files endpoints"""
# GET users
response = self.client.get('/api/files/users')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
# POST create user
user_data = {'username': 'test_user'}
response = self.client.post('/api/files/users',
data=json.dumps(user_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# DELETE user
response = self.client.delete('/api/files/users/test_user')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# GET status
response = self.client.get('/api/files/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
def test_api_routing_endpoints(self):
"""Test routing endpoints"""
# GET status
response = self.client.get('/api/routing/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
# POST NAT rule
nat_data = {'type': 'masquerade', 'interface': 'eth0'}
response = self.client.post('/api/routing/nat',
data=json.dumps(nat_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('rule_id', data)
# GET NAT rules
response = self.client.get('/api/routing/nat')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
def test_api_vault_endpoints(self):
"""Test vault endpoints"""
# GET status
response = self.client.get('/api/vault/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('status', data)
# GET certificates
response = self.client.get('/api/vault/certificates')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
# POST generate certificate
cert_data = {'common_name': 'test.example.com'}
response = self.client.post('/api/vault/certificates',
data=json.dumps(cert_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('certificate', data)
# GET CA certificate
response = self.client.get('/api/vault/ca/certificate')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('certificate', data)
def test_api_containers_endpoints(self):
"""Test containers endpoints"""
# GET containers
response = self.client.get('/api/containers')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
# POST start container
response = self.client.post('/api/containers/test/start')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# POST stop container
response = self.client.post('/api/containers/test/stop')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('success', data)
# GET container logs
response = self.client.get('/api/containers/test/logs')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
def test_api_services_status_endpoint(self):
"""Test GET /api/services/status endpoint"""
response = self.client.get('/api/services/status')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('services', data)
def test_api_services_connectivity_endpoint(self):
"""Test GET /api/services/connectivity endpoint"""
response = self.client.get('/api/services/connectivity')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIn('results', data)
def test_api_health_history_endpoint(self):
"""Test GET /api/health/history endpoint"""
response = self.client.get('/api/health/history')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
def test_api_logs_endpoint(self):
"""Test GET /api/logs endpoint"""
response = self.client.get('/api/logs')
self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertIsInstance(data, list)
if __name__ == '__main__':
unittest.main()
+2 -1
View File
@@ -133,7 +133,8 @@ class TestGenerateCorefile(unittest.TestCase):
self.assertIn('reload', content)
def test_returns_false_on_write_error(self):
result = firewall_manager.generate_corefile([], '/nonexistent/path/Corefile')
with unittest.mock.patch('builtins.open', side_effect=OSError('Permission denied')):
result = firewall_manager.generate_corefile([], '/any/path/Corefile')
self.assertFalse(result)
+3 -1
View File
@@ -5,6 +5,7 @@ import sys
import os
import tempfile
import unittest
import unittest.mock
from pathlib import Path
api_dir = Path(__file__).parent.parent / 'api'
@@ -98,7 +99,8 @@ class TestWriteEnvFile(unittest.TestCase):
self.assertTrue(result)
def test_returns_false_on_unwritable_path(self):
result = ip_utils.write_env_file('172.20.0.0/16', '/nonexistent/deep/path/.env')
with unittest.mock.patch('builtins.open', side_effect=OSError('Permission denied')):
result = ip_utils.write_env_file('172.20.0.0/16', '/any/path/.env')
self.assertFalse(result)
def test_contains_cell_network(self):