Phase3
Some checks failed
CI / Node — lint, typecheck, test, build (push) Failing after 9s
CI / Go — node-agent tests (push) Failing after 9s
CI / Go — edge-gateway build (push) Successful in 13s

This commit is contained in:
smueller
2026-06-26 12:17:26 +02:00
parent c4077d4673
commit 9b061c3ee7
92 changed files with 4128 additions and 146 deletions

View File

@@ -137,12 +137,76 @@ type ServerStopPayload struct {
OperationMeta
}
// ServerCommandPayload instructs the agent to run a console command via RCON.
type ServerCommandPayload struct {
OperationMeta
Command string `json:"command"`
}
// ServerLogsSubscribePayload instructs the agent to stream container logs.
type ServerLogsSubscribePayload struct {
OperationMeta
TailLines int `json:"tailLines"`
Follow bool `json:"follow"`
}
// ServerLogPayload carries a single log line from a game server.
type ServerLogPayload struct {
OperationMeta
Line string `json:"line"`
Stream string `json:"stream"`
}
// ServerFilesListPayload lists files in a server data directory.
type ServerFilesListPayload struct {
OperationMeta
Path string `json:"path"`
}
// ServerFilesReadPayload reads a file from the server data directory.
type ServerFilesReadPayload struct {
OperationMeta
Path string `json:"path"`
MaxBytes int `json:"maxBytes"`
}
// ServerFilesWritePayload writes content to a file in the server data directory.
type ServerFilesWritePayload struct {
OperationMeta
Path string `json:"path"`
Content string `json:"content"`
Create bool `json:"create"`
}
// FileEntry describes a file or directory in a list response.
type FileEntry struct {
Name string `json:"name"`
Path string `json:"path"`
IsDir bool `json:"isDir"`
Size int64 `json:"size"`
ModifiedAt string `json:"modifiedAt"`
}
// ServerFilesListResultPayload is the result of a files.list operation.
type ServerFilesListResultPayload struct {
Entries []FileEntry `json:"entries"`
}
// ServerFilesReadResultPayload is the result of a files.read operation.
type ServerFilesReadResultPayload struct {
Path string `json:"path"`
Content string `json:"content"`
Truncated bool `json:"truncated"`
}
// ServerOperationResultPayload reports the outcome of a lifecycle operation.
type ServerOperationResultPayload struct {
OperationMeta
ResultCode string `json:"resultCode"`
ErrorCode string `json:"errorCode,omitempty"`
ErrorMessage string `json:"errorMessage,omitempty"`
ResultCode string `json:"resultCode"`
Operation string `json:"operation,omitempty"`
Result json.RawMessage `json:"result,omitempty"`
ErrorCode string `json:"errorCode,omitempty"`
ErrorMessage string `json:"errorMessage,omitempty"`
}
// ServerStatePayload reports the observed server state.

View File

@@ -212,3 +212,168 @@ func TestNewEnvelope(t *testing.T) {
t.Fatalf("protocol version %d", env.ProtocolVersion)
}
}
func TestPhase3Payloads_JSONRoundtrip(t *testing.T) {
meta := OperationMeta{
ServerID: "srv-01",
Generation: 3,
}
command := ServerCommandPayload{
OperationMeta: meta,
Command: "say hello",
}
commandRaw, err := json.Marshal(command)
if err != nil {
t.Fatal(err)
}
var decodedCommand ServerCommandPayload
if err := json.Unmarshal(commandRaw, &decodedCommand); err != nil {
t.Fatal(err)
}
if decodedCommand.Command != command.Command {
t.Fatalf("command: got %q want %q", decodedCommand.Command, command.Command)
}
logsSub := ServerLogsSubscribePayload{
OperationMeta: meta,
TailLines: 100,
Follow: true,
}
logsSubRaw, err := json.Marshal(logsSub)
if err != nil {
t.Fatal(err)
}
var decodedLogsSub ServerLogsSubscribePayload
if err := json.Unmarshal(logsSubRaw, &decodedLogsSub); err != nil {
t.Fatal(err)
}
if decodedLogsSub.TailLines != 100 || !decodedLogsSub.Follow {
t.Fatalf("logs subscribe mismatch: %+v", decodedLogsSub)
}
filesList := ServerFilesListPayload{
OperationMeta: meta,
Path: "world",
}
filesListRaw, err := json.Marshal(filesList)
if err != nil {
t.Fatal(err)
}
env := &Envelope{
ProtocolVersion: CurrentProtocolVersion,
MessageID: "msg-files-list",
Type: TypeServerFilesList,
Timestamp: time.Now().UTC(),
Payload: filesListRaw,
}
if err := env.Validate(); err != nil {
t.Fatalf("files list envelope: %v", err)
}
filesRead := ServerFilesReadPayload{
OperationMeta: meta,
Path: "server.properties",
MaxBytes: 4096,
}
filesReadRaw, err := json.Marshal(filesRead)
if err != nil {
t.Fatal(err)
}
env = &Envelope{
ProtocolVersion: CurrentProtocolVersion,
MessageID: "msg-files-read",
Type: TypeServerFilesRead,
Timestamp: time.Now().UTC(),
Payload: filesReadRaw,
}
if err := env.Validate(); err != nil {
t.Fatalf("files read envelope: %v", err)
}
filesWrite := ServerFilesWritePayload{
OperationMeta: meta,
Path: "ops.txt",
Content: "test",
Create: true,
}
filesWriteRaw, err := json.Marshal(filesWrite)
if err != nil {
t.Fatal(err)
}
env = &Envelope{
ProtocolVersion: CurrentProtocolVersion,
MessageID: "msg-files-write",
Type: TypeServerFilesWrite,
Timestamp: time.Now().UTC(),
Payload: filesWriteRaw,
}
if err := env.Validate(); err != nil {
t.Fatalf("files write envelope: %v", err)
}
logLine := ServerLogPayload{
OperationMeta: meta,
Line: "[Server] Done",
Stream: "stdout",
}
logLineRaw, err := json.Marshal(logLine)
if err != nil {
t.Fatal(err)
}
env = &Envelope{
ProtocolVersion: CurrentProtocolVersion,
MessageID: "msg-log",
Type: TypeServerLog,
Timestamp: time.Now().UTC(),
Payload: logLineRaw,
}
if err := env.Validate(); err != nil {
t.Fatalf("server log envelope: %v", err)
}
}
func TestServerOperationResultPayload_WithResult(t *testing.T) {
listResult, err := json.Marshal(ServerFilesListResultPayload{
Entries: []FileEntry{{
Name: "server.properties",
Path: "server.properties",
IsDir: false,
Size: 128,
ModifiedAt: "2026-06-26T12:00:00Z",
}},
})
if err != nil {
t.Fatal(err)
}
payload := ServerOperationResultPayload{
OperationMeta: OperationMeta{ServerID: "srv-01", Generation: 1},
ResultCode: "ok",
Operation: "files.list",
Result: listResult,
}
raw, err := json.Marshal(payload)
if err != nil {
t.Fatal(err)
}
var decoded ServerOperationResultPayload
if err := json.Unmarshal(raw, &decoded); err != nil {
t.Fatal(err)
}
if decoded.Operation != "files.list" {
t.Fatalf("operation: got %q", decoded.Operation)
}
if len(decoded.Result) == 0 {
t.Fatal("expected result payload")
}
var result ServerFilesListResultPayload
if err := json.Unmarshal(decoded.Result, &result); err != nil {
t.Fatal(err)
}
if len(result.Entries) != 1 || result.Entries[0].Name != "server.properties" {
t.Fatalf("unexpected list result: %+v", result)
}
}