README
¶
Mock ocserv Unix Socket Server
Mock implementation of ocserv's occtl Unix socket interface for integration testing.
Overview
This mock server simulates the ocserv Unix socket that occtl uses to communicate with the ocserv daemon. It's designed for integration testing of ocserv-agent without requiring a real ocserv installation.
Key Features:
- ✅ Listens on Unix domain socket (like real ocserv)
- ✅ Supports all 13 occtl commands with
-j(JSON) flag - ✅ Returns realistic responses from production fixtures
- ✅ Handles multiple concurrent connections
- ✅ Graceful shutdown on SIGINT/SIGTERM
- ✅ Configurable socket path and fixtures directory
Quick Start
Run Mock Server (Recommended: Podman Compose)
# From repository root
make compose-mock-ocserv
# View logs
podman logs -f mock-ocserv
# Stop
cd deploy/compose && podman-compose -f mock-ocserv.yml down
Container Details:
- Image:
ocserv-agent-mock-ocserv:latest - Socket:
/var/run/occtl.socket(inside container) - Fixtures:
/fixtures/ocserv/occtl(mounted read-only) - Network:
ocserv-agent-test - Health check: Every 5s
Run Locally (Development Only)
# From repository root
cd test/mock-ocserv
go run . -socket /tmp/occtl-test.socket
# Or with custom fixtures path
go run . -socket /tmp/occtl-test.socket -fixtures ../fixtures/ocserv/occtl
# Enable verbose logging
go run . -verbose
⚠️ Note: Local runs are for development only. All testing should use podman-compose.
Test with nc (netcat)
# In another terminal
echo '{"command": ["show", "-j", "users"]}' | nc -U /tmp/occtl-test.socket
# Or plain text format
echo 'show -j users' | nc -U /tmp/occtl-test.socket
Test with ocserv-agent (in Compose)
# Start mock server
make compose-mock-ocserv
# In another terminal, run tests that connect to mock socket
# Tests should use the shared volume or network to access socket
# Integration tests will use mock-ocserv via shared volume
# See docker-compose.test.yml for configuration
Test Locally (Development)
# Configure ocserv-agent to use mock socket
export OCSERV_SOCKET=/tmp/occtl-test.socket
# Run integration tests
go test -v ./internal/ocserv/...
Supported Commands
The mock server supports all 13 occtl commands found in production ocserv 1.3:
User Management
show -j users- List all connected usersshow -j user <username>- Show details for specific usershow -j id <id>- Show connection by ID
Session Management
show -j sessions all- All sessionsshow -j sessions valid- Valid sessions (reconnectable)show -j session <session-id>- Specific session detailsshow -j cookies all- All session cookies (alias for sessions)show -j cookies valid- Valid cookies
Server Status
show -j status- Server statistics and uptimeshow -j iroutes- User-provided routesshow -j events- Real-time connection events (streaming)show -j ip ban points- IP ban list and scores
Plain Text Commands
show id <id>- Connection details (plain text format)
Input Format
The mock server accepts two input formats:
1. JSON Format (Real ocserv Protocol)
{"command": ["show", "-j", "users"]}
2. Plain Text Format (For Testing)
show -j users
Both formats are equivalent and produce the same output.
Output Format
Responses are returned as newline-terminated JSON, matching real ocserv behavior:
[{
"ID": 836873,
"Username": "lpa",
"State": "connected",
"Remote IP": "90.156.164.225",
"IPv4": "10.0.16.23",
...
}]
Fixtures
Fixtures are loaded from ../fixtures/ocserv/occtl/ directory:
fixtures/ocserv/occtl/
├── occtl -j show users # User list
├── occtl -j show status # Server stats
├── occtl -j show user # User details (single)
├── occtl -j show id # Connection by ID
├── occtl -j show sessions all # All sessions
├── occtl -j show sessions valid # Valid sessions
├── occtl -j show iroutes # User routes
├── occtl -j show events # Events stream
├── occtl -j show ip ban points # IP bans
├── occtl -j show cookies all # All cookies
├── occtl -j show cookies valid # Valid cookies
├── occtl -j show session # Session details
└── occtl show id # Plain text ID lookup
Note: Fixtures are from production ocserv 1.3.0 server (Oracle Linux 10) with sanitized data.
Command-Line Flags
-socket string
Unix socket path (default "/tmp/occtl-test.socket")
-fixtures string
Path to fixtures directory (default "../fixtures/ocserv/occtl")
-verbose
Enable verbose logging (default false)
Architecture
┌─────────────┐ Unix Socket ┌──────────────┐
│ │◄──────────────────────►│ │
│ Client │ JSON commands │ Mock Server │
│ (ocserv- │ JSON responses │ │
│ agent) │ │ │
└─────────────┘ └──────┬───────┘
│
│ Loads
▼
┌──────────────┐
│ Fixtures │
│ (JSON files)│
└──────────────┘
Components
- main.go - Server setup, signal handling, socket lifecycle
- handler.go - Connection handling, command execution
- command.go - Command parsing (JSON and plain text)
- fixtures.go - Fixture loading and lookup
Integration Testing
Example Test
func TestOcctlIntegration(t *testing.T) {
// Start mock server
socketPath := "/tmp/occtl-test.socket"
fixturesPath := "../../test/fixtures/ocserv/occtl"
// ... start mock server in background
// Create ocserv manager
mgr := ocserv.NewOcctlManager(socketPath)
// Test commands
users, err := mgr.ShowUsers(context.Background())
require.NoError(t, err)
assert.Len(t, users, 2)
assert.Equal(t, "lpa", users[0].Username)
}
Usage in CI/CD
# .github/workflows/test.yml
- name: Run Integration Tests
run: |
# Start mock server in background
cd test/mock-ocserv
go run . -socket /tmp/occtl-test.socket &
MOCK_PID=$!
# Wait for socket
sleep 1
# Run tests
cd ../..
go test -v -tags=integration ./...
# Cleanup
kill $MOCK_PID
Troubleshooting
Socket Permission Denied
# Check socket exists and has correct permissions
ls -l /tmp/occtl-test.socket
# Should show: srw-rw-rw-
# If needed, set permissions manually
chmod 666 /tmp/occtl-test.socket
Fixtures Not Found
# Check fixtures directory
ls ../fixtures/ocserv/occtl/
# Should list 13 files starting with "occtl"
# Use absolute path
go run . -fixtures /full/path/to/fixtures/ocserv/occtl
Connection Refused
# Check if socket is listening
lsof | grep occtl-test.socket
# Check if another process is using the socket
fuser /tmp/occtl-test.socket
# Remove old socket
rm -f /tmp/occtl-test.socket
Differences from Real ocserv
- No Authentication - Mock server does not verify client permissions
- Static Responses - Returns pre-recorded fixtures, not live data
- No State Changes - Commands like
disconnectreturn success but don't affect state - Fixed Data - User list, status, etc. never change during runtime
- No Events Streaming -
show eventsreturns static data, not real-time stream
These differences are intentional for testing purposes.
Performance
- Startup time: <100ms (including fixture loading)
- Memory usage: ~5MB (with all fixtures loaded)
- Latency: <1ms per command
- Concurrency: Handles 100+ concurrent connections
Development
Adding New Fixtures
- Run real
occtlcommand on production server:
occtl -j show users > fixtures/ocserv/occtl/"occtl -j show users"
- Sanitize data if needed (remove sensitive info)
- Place file in
fixtures/ocserv/occtl/directory - Restart mock server (it will auto-load new fixture)
Testing Changes
# Run with verbose logging
go run . -verbose
# Test specific command
echo 'show -j users' | nc -U /tmp/occtl-test.socket
Future Enhancements
- Support for non-JSON commands (plain text)
- State management (track "connected" users)
- Event streaming support
- Config file support
- Metrics/statistics endpoint
References
Last Updated: 2025-10-23 Status: Production-ready for integration testing
Documentation
¶
Overview ¶
mock-ocserv: Mock ocserv Unix socket server for integration testing
This server simulates occtl Unix socket interface by: - Listening on a Unix socket (default: /tmp/occtl-test.socket) - Accepting JSON-formatted occtl commands - Returning realistic responses from test fixtures
Usage:
go run main.go -socket /tmp/occtl-test.socket go run main.go -fixtures ./test/fixtures/ocserv/occtl