mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-13 21:57:29 +08:00
fix package
This commit is contained in:
497
cmd/cmd_node_test.go
Normal file
497
cmd/cmd_node_test.go
Normal file
@@ -0,0 +1,497 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/YspCoder/clawgo/pkg/agent"
|
||||
"github.com/YspCoder/clawgo/pkg/config"
|
||||
"github.com/YspCoder/clawgo/pkg/nodes"
|
||||
"github.com/YspCoder/clawgo/pkg/providers"
|
||||
)
|
||||
|
||||
type stubNodeProvider struct {
|
||||
content string
|
||||
}
|
||||
|
||||
func (p stubNodeProvider) Chat(ctx context.Context, messages []providers.Message, tools []providers.ToolDefinition, model string, options map[string]interface{}) (*providers.LLMResponse, error) {
|
||||
return &providers.LLMResponse{Content: p.content, FinishReason: "stop"}, nil
|
||||
}
|
||||
|
||||
func (p stubNodeProvider) GetDefaultModel() string {
|
||||
return "stub-model"
|
||||
}
|
||||
|
||||
func TestParseNodeRegisterArgsDefaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Gateway.Host = "gateway.example"
|
||||
cfg.Gateway.Port = 7788
|
||||
cfg.Gateway.Token = "cfg-token"
|
||||
|
||||
opts, err := parseNodeRegisterArgs([]string{"--id", "edge-dev"}, cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("parseNodeRegisterArgs failed: %v", err)
|
||||
}
|
||||
if opts.GatewayBase != "http://gateway.example:7788" {
|
||||
t.Fatalf("unexpected gateway base: %s", opts.GatewayBase)
|
||||
}
|
||||
if opts.Token != "cfg-token" {
|
||||
t.Fatalf("unexpected token: %s", opts.Token)
|
||||
}
|
||||
if opts.ID != "edge-dev" {
|
||||
t.Fatalf("unexpected id: %s", opts.ID)
|
||||
}
|
||||
if !opts.Capabilities.Run || !opts.Capabilities.Invoke || !opts.Capabilities.Model {
|
||||
t.Fatalf("expected default run/invoke/model capabilities, got %+v", opts.Capabilities)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNodeRegisterArgsTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
opts, err := parseNodeRegisterArgs([]string{"--id", "edge-dev", "--tags", "vision,gpu"}, cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("parseNodeRegisterArgs failed: %v", err)
|
||||
}
|
||||
if len(opts.Tags) != 2 || opts.Tags[0] != "vision" || opts.Tags[1] != "gpu" {
|
||||
t.Fatalf("unexpected tags: %+v", opts.Tags)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostNodeRegisterSendsNodeInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var gotAuth string
|
||||
var got nodes.NodeInfo
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/nodes/register" {
|
||||
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
gotAuth = r.Header.Get("Authorization")
|
||||
if err := json.NewDecoder(r.Body).Decode(&got); err != nil {
|
||||
t.Fatalf("decode body: %v", err)
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(`{"ok":true}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
info := nodes.NodeInfo{
|
||||
ID: "edge-dev",
|
||||
Name: "Edge Dev",
|
||||
Endpoint: "http://edge.example:18790",
|
||||
Capabilities: nodes.Capabilities{
|
||||
Run: true, Invoke: true, Model: true,
|
||||
},
|
||||
Actions: []string{"run", "agent_task"},
|
||||
Models: []string{"gpt-4o-mini"},
|
||||
}
|
||||
client := &http.Client{Timeout: 2 * time.Second}
|
||||
if err := postNodeRegister(context.Background(), client, srv.URL, "secret", info); err != nil {
|
||||
t.Fatalf("postNodeRegister failed: %v", err)
|
||||
}
|
||||
if gotAuth != "Bearer secret" {
|
||||
t.Fatalf("unexpected auth header: %s", gotAuth)
|
||||
}
|
||||
if got.ID != "edge-dev" || got.Endpoint != "http://edge.example:18790" {
|
||||
t.Fatalf("unexpected node payload: %+v", got)
|
||||
}
|
||||
if len(got.Actions) != 2 || got.Actions[1] != "agent_task" {
|
||||
t.Fatalf("unexpected actions: %+v", got.Actions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostNodeHeartbeatSendsNodeID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var body map[string]string
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/nodes/heartbeat" {
|
||||
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
t.Fatalf("decode body: %v", err)
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
client := &http.Client{Timeout: 2 * time.Second}
|
||||
if err := postNodeHeartbeat(context.Background(), client, srv.URL, "", "edge-dev"); err != nil {
|
||||
t.Fatalf("postNodeHeartbeat failed: %v", err)
|
||||
}
|
||||
if strings.TrimSpace(body["id"]) != "edge-dev" {
|
||||
t.Fatalf("unexpected heartbeat body: %+v", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeAgentsFromConfigCollectsEnabledAgents(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Agents.Subagents["main"] = config.SubagentConfig{
|
||||
Enabled: true,
|
||||
Type: "router",
|
||||
DisplayName: "Main Agent",
|
||||
Role: "orchestrator",
|
||||
}
|
||||
cfg.Agents.Subagents["coder"] = config.SubagentConfig{
|
||||
Enabled: true,
|
||||
Type: "worker",
|
||||
DisplayName: "Code Agent",
|
||||
Role: "code",
|
||||
}
|
||||
cfg.Agents.Subagents["tester"] = config.SubagentConfig{
|
||||
Enabled: false,
|
||||
Type: "worker",
|
||||
DisplayName: "Test Agent",
|
||||
Role: "test",
|
||||
}
|
||||
items := nodeAgentsFromConfig(cfg)
|
||||
if len(items) != 2 {
|
||||
t.Fatalf("expected 2 enabled agents, got %+v", items)
|
||||
}
|
||||
if items[0].ID != "coder" || items[1].ID != "main" {
|
||||
t.Fatalf("unexpected agent export order: %+v", items)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeWebsocketURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := nodeWebsocketURL("http://gateway.example:18790"); got != "ws://gateway.example:18790/nodes/connect" {
|
||||
t.Fatalf("unexpected ws url: %s", got)
|
||||
}
|
||||
if got := nodeWebsocketURL("https://gateway.example"); got != "wss://gateway.example/nodes/connect" {
|
||||
t.Fatalf("unexpected wss url: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeSocketPingInterval(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := nodeSocketPingInterval(120); got != 25*time.Second {
|
||||
t.Fatalf("expected 25s cap, got %s", got)
|
||||
}
|
||||
if got := nodeSocketPingInterval(20); got != 10*time.Second {
|
||||
t.Fatalf("expected 10s floor, got %s", got)
|
||||
}
|
||||
if got := nodeSocketPingInterval(30); got != 15*time.Second {
|
||||
t.Fatalf("expected half heartbeat, got %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteNodeRequestRunsLocalMainAgentTask(t *testing.T) {
|
||||
prevCfg := globalConfigPathOverride
|
||||
prevProviderFactory := nodeProviderFactory
|
||||
prevLoopFactory := nodeAgentLoopFactory
|
||||
prevExecutors := nodeLocalExecutors
|
||||
globalConfigPathOverride = filepath.Join(t.TempDir(), "config.json")
|
||||
nodeLocalExecutors = map[string]*nodeLocalExecutor{}
|
||||
nodeProviderFactory = func(cfg *config.Config) (providers.LLMProvider, error) {
|
||||
return stubNodeProvider{content: "main-local-ok"}, nil
|
||||
}
|
||||
nodeAgentLoopFactory = agent.NewAgentLoop
|
||||
defer func() {
|
||||
globalConfigPathOverride = prevCfg
|
||||
nodeProviderFactory = prevProviderFactory
|
||||
nodeAgentLoopFactory = prevLoopFactory
|
||||
nodeLocalExecutors = prevExecutors
|
||||
}()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Agents.Defaults.Workspace = filepath.Join(t.TempDir(), "workspace")
|
||||
cfg.Agents.Subagents["main"] = config.SubagentConfig{
|
||||
Enabled: true,
|
||||
Type: "router",
|
||||
Role: "orchestrator",
|
||||
}
|
||||
if err := config.SaveConfig(globalConfigPathOverride, cfg); err != nil {
|
||||
t.Fatalf("save config: %v", err)
|
||||
}
|
||||
|
||||
info := nodes.NodeInfo{ID: "edge-a", Name: "Edge A"}
|
||||
resp := executeNodeRequest(context.Background(), &http.Client{Timeout: time.Second}, info, nodeRegisterOptions{}, &nodes.Request{
|
||||
Action: "agent_task",
|
||||
Task: "say ok",
|
||||
})
|
||||
if !resp.OK {
|
||||
t.Fatalf("expected ok response, got %+v", resp)
|
||||
}
|
||||
if got := strings.TrimSpace(resp.Payload["result"].(string)); got != "main-local-ok" {
|
||||
t.Fatalf("unexpected result: %+v", resp.Payload)
|
||||
}
|
||||
if got := strings.TrimSpace(resp.Payload["agent_id"].(string)); got != "main" {
|
||||
t.Fatalf("unexpected agent id: %+v", resp.Payload)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteNodeRequestRunsLocalSubagentTask(t *testing.T) {
|
||||
prevCfg := globalConfigPathOverride
|
||||
prevProviderFactory := nodeProviderFactory
|
||||
prevLoopFactory := nodeAgentLoopFactory
|
||||
prevExecutors := nodeLocalExecutors
|
||||
globalConfigPathOverride = filepath.Join(t.TempDir(), "config.json")
|
||||
nodeLocalExecutors = map[string]*nodeLocalExecutor{}
|
||||
nodeProviderFactory = func(cfg *config.Config) (providers.LLMProvider, error) {
|
||||
return stubNodeProvider{content: "coder-local-ok"}, nil
|
||||
}
|
||||
nodeAgentLoopFactory = agent.NewAgentLoop
|
||||
defer func() {
|
||||
globalConfigPathOverride = prevCfg
|
||||
nodeProviderFactory = prevProviderFactory
|
||||
nodeAgentLoopFactory = prevLoopFactory
|
||||
nodeLocalExecutors = prevExecutors
|
||||
}()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Agents.Defaults.Workspace = filepath.Join(t.TempDir(), "workspace")
|
||||
cfg.Agents.Subagents["main"] = config.SubagentConfig{
|
||||
Enabled: true,
|
||||
Type: "router",
|
||||
Role: "orchestrator",
|
||||
}
|
||||
cfg.Agents.Subagents["coder"] = config.SubagentConfig{
|
||||
Enabled: true,
|
||||
Type: "worker",
|
||||
Role: "code",
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(cfg.Agents.Defaults.Workspace, "out"), 0755); err != nil {
|
||||
t.Fatalf("mkdir artifact dir: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(cfg.Agents.Defaults.Workspace, "out", "result.txt"), []byte("artifact-body"), 0644); err != nil {
|
||||
t.Fatalf("write artifact: %v", err)
|
||||
}
|
||||
if err := config.SaveConfig(globalConfigPathOverride, cfg); err != nil {
|
||||
t.Fatalf("save config: %v", err)
|
||||
}
|
||||
|
||||
info := nodes.NodeInfo{ID: "edge-b", Name: "Edge B"}
|
||||
resp := executeNodeRequest(context.Background(), &http.Client{Timeout: time.Second}, info, nodeRegisterOptions{}, &nodes.Request{
|
||||
Action: "agent_task",
|
||||
Task: "write tests",
|
||||
Args: map[string]interface{}{"remote_agent_id": "coder", "artifact_paths": []interface{}{"out/result.txt"}},
|
||||
})
|
||||
if !resp.OK {
|
||||
t.Fatalf("expected ok response, got %+v", resp)
|
||||
}
|
||||
if got := strings.TrimSpace(resp.Payload["result"].(string)); !strings.Contains(got, "coder-local-ok") {
|
||||
t.Fatalf("unexpected result: %+v", resp.Payload)
|
||||
}
|
||||
if got := strings.TrimSpace(resp.Payload["agent_id"].(string)); got != "coder" {
|
||||
t.Fatalf("unexpected agent id: %+v", resp.Payload)
|
||||
}
|
||||
artifacts, ok := resp.Payload["artifacts"].([]map[string]interface{})
|
||||
if !ok || len(artifacts) != 1 {
|
||||
t.Fatalf("expected one artifact, got %+v", resp.Payload["artifacts"])
|
||||
}
|
||||
if artifacts[0]["content_text"] != "artifact-body" {
|
||||
t.Fatalf("unexpected artifact payload: %+v", artifacts[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectNodeArtifactsRejectsPathEscape(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := collectNodeArtifacts(t.TempDir(), map[string]interface{}{
|
||||
"artifact_paths": []interface{}{"../secret.txt"},
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "escapes workspace") {
|
||||
t.Fatalf("expected workspace escape error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteNodeRequestRunsLocalCameraSnap(t *testing.T) {
|
||||
prevCfg := globalConfigPathOverride
|
||||
prevExecutors := nodeLocalExecutors
|
||||
prevCamera := nodeCameraSnapFunc
|
||||
globalConfigPathOverride = filepath.Join(t.TempDir(), "config.json")
|
||||
nodeLocalExecutors = map[string]*nodeLocalExecutor{}
|
||||
defer func() {
|
||||
globalConfigPathOverride = prevCfg
|
||||
nodeLocalExecutors = prevExecutors
|
||||
nodeCameraSnapFunc = prevCamera
|
||||
}()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Agents.Defaults.Workspace = filepath.Join(t.TempDir(), "workspace")
|
||||
if err := config.SaveConfig(globalConfigPathOverride, cfg); err != nil {
|
||||
t.Fatalf("save config: %v", err)
|
||||
}
|
||||
nodeCameraSnapFunc = func(ctx context.Context, workspace string, args map[string]interface{}) (string, error) {
|
||||
out := filepath.Join(workspace, "artifacts", "node", "camera-test.jpg")
|
||||
if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.WriteFile(out, []byte("camera-bytes"), 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
info := nodes.NodeInfo{ID: "edge-cam", Name: "Edge Cam"}
|
||||
resp := executeNodeRequest(context.Background(), &http.Client{Timeout: time.Second}, info, nodeRegisterOptions{}, &nodes.Request{
|
||||
Action: "camera_snap",
|
||||
Args: map[string]interface{}{"facing": "front"},
|
||||
})
|
||||
if !resp.OK {
|
||||
t.Fatalf("expected ok response, got %+v", resp)
|
||||
}
|
||||
artifacts, ok := resp.Payload["artifacts"].([]map[string]interface{})
|
||||
if !ok || len(artifacts) != 1 {
|
||||
t.Fatalf("expected one artifact, got %+v", resp.Payload["artifacts"])
|
||||
}
|
||||
if artifacts[0]["name"] != "camera-test.jpg" {
|
||||
t.Fatalf("unexpected artifact: %+v", artifacts[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteNodeRequestRunsLocalScreenSnapshot(t *testing.T) {
|
||||
prevCfg := globalConfigPathOverride
|
||||
prevExecutors := nodeLocalExecutors
|
||||
prevScreen := nodeScreenSnapFunc
|
||||
globalConfigPathOverride = filepath.Join(t.TempDir(), "config.json")
|
||||
nodeLocalExecutors = map[string]*nodeLocalExecutor{}
|
||||
defer func() {
|
||||
globalConfigPathOverride = prevCfg
|
||||
nodeLocalExecutors = prevExecutors
|
||||
nodeScreenSnapFunc = prevScreen
|
||||
}()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Agents.Defaults.Workspace = filepath.Join(t.TempDir(), "workspace")
|
||||
if err := config.SaveConfig(globalConfigPathOverride, cfg); err != nil {
|
||||
t.Fatalf("save config: %v", err)
|
||||
}
|
||||
nodeScreenSnapFunc = func(ctx context.Context, workspace string, args map[string]interface{}) (string, error) {
|
||||
out := filepath.Join(workspace, "artifacts", "node", "screen-test.png")
|
||||
if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.WriteFile(out, []byte{0x89, 0x50, 0x4e, 0x47}, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
info := nodes.NodeInfo{ID: "edge-screen", Name: "Edge Screen"}
|
||||
resp := executeNodeRequest(context.Background(), &http.Client{Timeout: time.Second}, info, nodeRegisterOptions{}, &nodes.Request{
|
||||
Action: "screen_snapshot",
|
||||
})
|
||||
if !resp.OK {
|
||||
t.Fatalf("expected ok response, got %+v", resp)
|
||||
}
|
||||
artifacts, ok := resp.Payload["artifacts"].([]map[string]interface{})
|
||||
if !ok || len(artifacts) != 1 {
|
||||
t.Fatalf("expected one artifact, got %+v", resp.Payload["artifacts"])
|
||||
}
|
||||
if artifacts[0]["name"] != "screen-test.png" {
|
||||
t.Fatalf("unexpected artifact: %+v", artifacts[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteNodeRequestRunsLocalCameraClip(t *testing.T) {
|
||||
prevCfg := globalConfigPathOverride
|
||||
prevExecutors := nodeLocalExecutors
|
||||
prevClip := nodeCameraClipFunc
|
||||
globalConfigPathOverride = filepath.Join(t.TempDir(), "config.json")
|
||||
nodeLocalExecutors = map[string]*nodeLocalExecutor{}
|
||||
defer func() {
|
||||
globalConfigPathOverride = prevCfg
|
||||
nodeLocalExecutors = prevExecutors
|
||||
nodeCameraClipFunc = prevClip
|
||||
}()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Agents.Defaults.Workspace = filepath.Join(t.TempDir(), "workspace")
|
||||
if err := config.SaveConfig(globalConfigPathOverride, cfg); err != nil {
|
||||
t.Fatalf("save config: %v", err)
|
||||
}
|
||||
nodeCameraClipFunc = func(ctx context.Context, workspace string, args map[string]interface{}) (string, error) {
|
||||
out := filepath.Join(workspace, "artifacts", "node", "camera-test.mp4")
|
||||
if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.WriteFile(out, []byte("video-bytes"), 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
info := nodes.NodeInfo{ID: "edge-clip", Name: "Edge Clip"}
|
||||
resp := executeNodeRequest(context.Background(), &http.Client{Timeout: time.Second}, info, nodeRegisterOptions{}, &nodes.Request{
|
||||
Action: "camera_clip",
|
||||
Args: map[string]interface{}{"duration_ms": 2500},
|
||||
})
|
||||
if !resp.OK {
|
||||
t.Fatalf("expected ok response, got %+v", resp)
|
||||
}
|
||||
if got, _ := resp.Payload["duration_ms"].(int); got != 2500 {
|
||||
t.Fatalf("unexpected duration payload: %+v", resp.Payload)
|
||||
}
|
||||
artifacts, ok := resp.Payload["artifacts"].([]map[string]interface{})
|
||||
if !ok || len(artifacts) != 1 {
|
||||
t.Fatalf("expected one artifact, got %+v", resp.Payload["artifacts"])
|
||||
}
|
||||
if artifacts[0]["name"] != "camera-test.mp4" {
|
||||
t.Fatalf("unexpected artifact: %+v", artifacts[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteNodeRequestRunsLocalScreenRecord(t *testing.T) {
|
||||
prevCfg := globalConfigPathOverride
|
||||
prevExecutors := nodeLocalExecutors
|
||||
prevRecord := nodeScreenRecordFunc
|
||||
globalConfigPathOverride = filepath.Join(t.TempDir(), "config.json")
|
||||
nodeLocalExecutors = map[string]*nodeLocalExecutor{}
|
||||
defer func() {
|
||||
globalConfigPathOverride = prevCfg
|
||||
nodeLocalExecutors = prevExecutors
|
||||
nodeScreenRecordFunc = prevRecord
|
||||
}()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Agents.Defaults.Workspace = filepath.Join(t.TempDir(), "workspace")
|
||||
if err := config.SaveConfig(globalConfigPathOverride, cfg); err != nil {
|
||||
t.Fatalf("save config: %v", err)
|
||||
}
|
||||
nodeScreenRecordFunc = func(ctx context.Context, workspace string, args map[string]interface{}) (string, error) {
|
||||
out := filepath.Join(workspace, "artifacts", "node", "screen-test.mp4")
|
||||
if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.WriteFile(out, []byte("screen-video"), 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
info := nodes.NodeInfo{ID: "edge-record", Name: "Edge Record"}
|
||||
resp := executeNodeRequest(context.Background(), &http.Client{Timeout: time.Second}, info, nodeRegisterOptions{}, &nodes.Request{
|
||||
Action: "screen_record",
|
||||
Args: map[string]interface{}{"duration_ms": 1800},
|
||||
})
|
||||
if !resp.OK {
|
||||
t.Fatalf("expected ok response, got %+v", resp)
|
||||
}
|
||||
if got, _ := resp.Payload["duration_ms"].(int); got != 1800 {
|
||||
t.Fatalf("unexpected duration payload: %+v", resp.Payload)
|
||||
}
|
||||
artifacts, ok := resp.Payload["artifacts"].([]map[string]interface{})
|
||||
if !ok || len(artifacts) != 1 {
|
||||
t.Fatalf("expected one artifact, got %+v", resp.Payload["artifacts"])
|
||||
}
|
||||
if artifacts[0]["name"] != "screen-test.mp4" {
|
||||
t.Fatalf("unexpected artifact: %+v", artifacts[0])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user