add: test cases for rcon
This commit is contained in:
@@ -44,17 +44,8 @@ The package expects these environment variables to be set:
|
||||
- `RCON_ADDRESS` - The address of the Minecraft server (e.g., "localhost:25575")
|
||||
- `RCON_PASSWORD` - The RCON password for authentication
|
||||
|
||||
## Methods
|
||||
## Testing
|
||||
|
||||
- `New(address, password)` - Create a new RCON client
|
||||
- `Execute(command)` - Execute a command on the server
|
||||
- `SetWeather(weather)` - Set the weather (clear, rain, thunder)
|
||||
- `SetTime(timeValue)` - Set the time (day, night, noon, midnight, or numeric)
|
||||
- `SetDifficulty(difficulty)` - Set the difficulty level (peaceful, easy, normal, hard)
|
||||
- `GetServerInfo()` - Get server version information
|
||||
- `Close()` - Close the RCON connection
|
||||
- `HealthCheck()` - Verify the connection is working
|
||||
- `ExecuteWithTimeout()` - Execute command with timeout
|
||||
- `ConnectWithTimeout()` - Connect with timeout
|
||||
- `GetEnvCredentials()` - Get credentials from environment
|
||||
- `NewFromEnv()` - Create client from environment variables
|
||||
`rconmock.go` contains a mock rcon server that accepts and logs requests from the user. It can be configured to return errors, or success (empty body strings). It logs the recieved message bodies.
|
||||
|
||||
The protocol is described at https://developer.valvesoftware.com/wiki/Source_RCON_Protocol.
|
||||
|
||||
109
internal/pkg/rcon/mockrcon.go
Normal file
109
internal/pkg/rcon/mockrcon.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package rcon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/gorcon/rcon"
|
||||
)
|
||||
|
||||
// MockRCONServer simulates an RCON server for testing purposes
|
||||
type MockRCONServer struct {
|
||||
listener net.Listener
|
||||
Commands []string
|
||||
Errors []error
|
||||
Messages []string
|
||||
}
|
||||
|
||||
// Start starts the mock RCON server on a random available port
|
||||
func (m *MockRCONServer) Start() (func() *RCONResults, error) {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to start mock server: %w", err)
|
||||
}
|
||||
m.listener = listener
|
||||
|
||||
results := &RCONResults{}
|
||||
done := sync.WaitGroup{}
|
||||
|
||||
done.Go(func() {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go m.handleConnection(conn, results)
|
||||
}
|
||||
})
|
||||
|
||||
return func() *RCONResults {
|
||||
m.stop()
|
||||
done.Wait()
|
||||
return results
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Stop stops the mock RCON server
|
||||
func (m *MockRCONServer) stop() error {
|
||||
if m.listener != nil {
|
||||
return m.listener.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Address returns the address the mock server is listening on
|
||||
func (m *MockRCONServer) Address() string {
|
||||
if m.listener != nil {
|
||||
return m.listener.Addr().String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// handleConnection handles individual client connections
|
||||
func (m *MockRCONServer) handleConnection(conn net.Conn, results *RCONResults) {
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
packet := &rcon.Packet{}
|
||||
if _, err := packet.ReadFrom(conn); err != nil {
|
||||
results.AppendError(err)
|
||||
return
|
||||
}
|
||||
|
||||
results.AppendMessage(packet.Body())
|
||||
|
||||
var respPacket *rcon.Packet
|
||||
switch packet.Type {
|
||||
case rcon.SERVERDATA_AUTH:
|
||||
respPacket = rcon.NewPacket(rcon.SERVERDATA_AUTH_RESPONSE, packet.ID, "")
|
||||
case rcon.SERVERDATA_EXECCOMMAND:
|
||||
respPacket = rcon.NewPacket(rcon.SERVERDATA_RESPONSE_VALUE, packet.ID, "")
|
||||
default:
|
||||
results.AppendError(fmt.Errorf("unknown packet: %v", packet.Type))
|
||||
return
|
||||
}
|
||||
if _, err := respPacket.WriteTo(conn); err != nil {
|
||||
results.AppendError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type RCONResults struct {
|
||||
mu sync.Mutex
|
||||
Messages []string
|
||||
Errors []error
|
||||
}
|
||||
|
||||
func (r *RCONResults) AppendError(err error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.Errors = append(r.Errors, err)
|
||||
}
|
||||
|
||||
func (r *RCONResults) AppendMessage(msg string) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.Messages = append(r.Messages, msg)
|
||||
}
|
||||
@@ -56,61 +56,17 @@ func (c *Client) Ping() error {
|
||||
|
||||
// SetWeather sets the weather in the Minecraft world
|
||||
func (c *Client) SetWeather(weather string) error {
|
||||
var command string
|
||||
switch weather {
|
||||
case "clear":
|
||||
command = "weather clear"
|
||||
case "rain":
|
||||
command = "weather rain"
|
||||
case "thunder":
|
||||
command = "weather thunder"
|
||||
default:
|
||||
return fmt.Errorf("invalid weather type: %s", weather)
|
||||
}
|
||||
|
||||
_, err := c.Execute(command)
|
||||
return err
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// SetTime sets the time in the Minecraft world
|
||||
func (c *Client) SetTime(timeValue string) error {
|
||||
var command string
|
||||
switch timeValue {
|
||||
case "day":
|
||||
command = "time set day"
|
||||
case "night":
|
||||
command = "time set night"
|
||||
case "noon":
|
||||
command = "time set noon"
|
||||
case "midnight":
|
||||
command = "time set midnight"
|
||||
default:
|
||||
// Assume it's a numeric value
|
||||
command = fmt.Sprintf("time set %s", timeValue)
|
||||
}
|
||||
|
||||
_, err := c.Execute(command)
|
||||
return err
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// SetDifficulty sets the difficulty level
|
||||
func (c *Client) SetDifficulty(difficulty string) error {
|
||||
var command string
|
||||
switch difficulty {
|
||||
case "peaceful":
|
||||
command = "difficulty peaceful"
|
||||
case "easy":
|
||||
command = "difficulty easy"
|
||||
case "normal":
|
||||
command = "difficulty normal"
|
||||
case "hard":
|
||||
command = "difficulty hard"
|
||||
default:
|
||||
return fmt.Errorf("invalid difficulty level: %s", difficulty)
|
||||
}
|
||||
|
||||
_, err := c.Execute(command)
|
||||
return err
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// GetServerInfo returns basic server information
|
||||
@@ -118,14 +74,6 @@ func (c *Client) GetServerInfo() (string, error) {
|
||||
return c.Execute("version")
|
||||
}
|
||||
|
||||
// TailLogs starts tailing the server logs
|
||||
func (c *Client) TailLogs(ctx context.Context, handler func(string)) error {
|
||||
// This is a placeholder implementation
|
||||
// In a real implementation, this would need to be connected to actual log tailing
|
||||
// For now, we'll just return an error to indicate this is not implemented
|
||||
return fmt.Errorf("log tailing not implemented in this version")
|
||||
}
|
||||
|
||||
// ConnectWithTimeout attempts to connect with a timeout
|
||||
func ConnectWithTimeout(address, password string, timeout time.Duration) (*Client, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
|
||||
@@ -2,13 +2,34 @@ package rcon
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
// This is a placeholder test since we can't actually connect to a server
|
||||
// in a test environment without a real Minecraft server
|
||||
// The actual functionality will be tested with integration tests
|
||||
t.Log("RCON package tests - placeholder for actual tests")
|
||||
serv := MockRCONServer{}
|
||||
done, err := serv.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start server: %v", err)
|
||||
}
|
||||
|
||||
password := "abc123"
|
||||
client, err := New(serv.Address(), password)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect: %v", err)
|
||||
}
|
||||
if _, err = client.Execute("Hello world!"); err != nil {
|
||||
t.Fatalf("failed to run command: %v", err)
|
||||
}
|
||||
|
||||
results := done()
|
||||
want := []string{
|
||||
"abc123",
|
||||
"Hello world!",
|
||||
}
|
||||
if diff := cmp.Diff(want, results.Messages); diff != "" {
|
||||
t.Errorf("Got diff (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEnvCredentials(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user