package control import ( "context" "crypto/sha256" "net" "path/filepath" "testing" "time" rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1" "github.com/rvbox/rvbox/internal/agentproto" "github.com/rvbox/rvbox/internal/domain" "github.com/rvbox/rvbox/internal/server/store" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/grpc/test/bufconn" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" ) func TestControlListAndGetViews_HP_CONTROL_01(t *testing.T) { service, persistence := newTestService(t) defer persistence.Close() ctx := context.Background() registerControlClient(t, persistence, "win-a", rvboxv1.Platform_PLATFORM_WINDOWS, rvboxv1.ShellType_SHELL_POWERSHELL, 1) registerControlClient(t, persistence, "win-b", rvboxv1.Platform_PLATFORM_WINDOWS, rvboxv1.ShellType_SHELL_CMD, 2) first, err := service.ListClients(ctx, &rvboxv1.ListClientsRequest{PageSize: 1}) if err != nil || len(first.GetClients()) != 1 || first.GetNextPageToken() == "" { t.Fatalf("first client page = %#v, %v", first, err) } second, err := service.ListClients(ctx, &rvboxv1.ListClientsRequest{PageSize: 1, PageToken: first.GetNextPageToken()}) if err != nil || len(second.GetClients()) != 1 || second.GetClients()[0].GetClientId() != "win-b" { t.Fatalf("second client page = %#v, %v", second, err) } if _, err := service.ListClients(ctx, &rvboxv1.ListClientsRequest{PageSize: 1, PageToken: "bad-token"}); status.Code(err) != codes.InvalidArgument { t.Fatalf("bad client cursor code = %v", status.Code(err)) } client, err := service.GetClient(ctx, &rvboxv1.GetClientRequest{ClientId: "win-a"}) if err != nil || client.GetClient().GetSupportedShells()[0] != rvboxv1.ShellType_SHELL_POWERSHELL { t.Fatalf("get client = %#v, %v", client, err) } issue := fixedIssue(0xa1) queued, err := service.RunCommand(ctx, &rvboxv1.RunCommandRequest{ TargetClientId: "win-a", RequestId: issue.String(), Spec: &rvboxv1.ExecutionSpec{Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "echo hi"}}, }) if err != nil || queued.GetIssueUuid() != issue.String() || queued.GetLifecycle() != rvboxv1.CommandLifecycle_COMMAND_QUEUED { t.Fatalf("run response = %#v, %v", queued, err) } commands, err := service.ListCommands(ctx, &rvboxv1.ListCommandsRequest{ClientId: "win-a", IncludeTerminal: true}) if err != nil || len(commands.GetCommands()) != 1 || commands.GetCommands()[0].GetSpec().GetShellType() != rvboxv1.ShellType_SHELL_POWERSHELL { t.Fatalf("command list = %#v, %v", commands, err) } got, err := service.GetCommand(ctx, &rvboxv1.GetCommandRequest{ClientId: "win-a", IssueUuid: issue.String()}) if err != nil || got.GetCommand().GetIssueUuid() != issue.String() { t.Fatalf("command get = %#v, %v", got, err) } if _, err := service.GetCommand(ctx, &rvboxv1.GetCommandRequest{IssueUuid: "019c46f1-1d02-6000-8000-0000000000a1"}); status.Code(err) != codes.InvalidArgument { t.Fatalf("non-v7 command ID code = %v", status.Code(err)) } } func TestRunCommandRequestIDIdempotencyAndValidation_BH_CONTROL_02(t *testing.T) { service, persistence := newTestService(t) defer persistence.Close() ctx := context.Background() registerControlClient(t, persistence, "win-a", rvboxv1.Platform_PLATFORM_WINDOWS, rvboxv1.ShellType_SHELL_POWERSHELL, 3) issue := fixedIssue(0xa2) request := &rvboxv1.RunCommandRequest{TargetClientId: "win-a", RequestId: issue.String(), Spec: &rvboxv1.ExecutionSpec{Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "echo one"}}} if _, err := service.RunCommand(ctx, request); err != nil { t.Fatal(err) } if replay, err := service.RunCommand(ctx, proto.Clone(request).(*rvboxv1.RunCommandRequest)); err != nil || replay.GetIssueUuid() != issue.String() { t.Fatalf("exact replay = %#v, %v", replay, err) } conflict := proto.Clone(request).(*rvboxv1.RunCommandRequest) conflict.Spec = &rvboxv1.ExecutionSpec{Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "echo two"}} if _, err := service.RunCommand(ctx, conflict); status.Code(err) != codes.AlreadyExists { t.Fatalf("conflicting replay code = %v", status.Code(err)) } badID := proto.Clone(request).(*rvboxv1.RunCommandRequest) badID.RequestId = "not-a-uuid" if _, err := service.RunCommand(ctx, badID); status.Code(err) != codes.InvalidArgument { t.Fatalf("bad request ID code = %v", status.Code(err)) } scriptBody := []byte("x") scriptDigest := sha256.Sum256(scriptBody) script := &rvboxv1.RunCommandRequest{TargetClientId: "win-a", RequestId: fixedIssue(0xa3).String(), Spec: &rvboxv1.ExecutionSpec{Source: &rvboxv1.ExecutionSpec_Script{Script: &rvboxv1.ScriptDescriptor{Filename: "x.ps1", SizeBytes: 1, Sha256: scriptDigest[:]}}}, ScriptContent: scriptBody} if queued, err := service.RunCommand(ctx, script); err != nil || queued.GetIssueUuid() != script.GetRequestId() { t.Fatalf("script admission = %#v, %v", queued, err) } badScript := proto.Clone(script).(*rvboxv1.RunCommandRequest) badScript.RequestId = fixedIssue(0xa7).String() badScript.ScriptContent = []byte("y") if _, err := service.RunCommand(ctx, badScript); status.Code(err) != codes.InvalidArgument { t.Fatalf("mismatched script code = %v", status.Code(err)) } badTTL := proto.Clone(request).(*rvboxv1.RunCommandRequest) badTTL.RequestId = fixedIssue(0xa4).String() badTTL.QueueTtl = durationpb.New(-time.Second) if _, err := service.RunCommand(ctx, badTTL); status.Code(err) != codes.InvalidArgument { t.Fatalf("negative TTL code = %v", status.Code(err)) } } func TestCommandPaginationBindsFilters_BH_CONTROL_03(t *testing.T) { service, persistence := newTestService(t) defer persistence.Close() ctx := context.Background() registerControlClient(t, persistence, "win-a", rvboxv1.Platform_PLATFORM_WINDOWS, rvboxv1.ShellType_SHELL_CMD, 4) for index := byte(0xb0); index < 0xb3; index++ { if _, err := service.RunCommand(ctx, &rvboxv1.RunCommandRequest{TargetClientId: "win-a", RequestId: fixedIssue(index).String(), Spec: &rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_CMD, Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "echo"}}}); err != nil { t.Fatal(err) } } first, err := service.ListCommands(ctx, &rvboxv1.ListCommandsRequest{ClientId: "win-a", PageSize: 1}) if err != nil || len(first.GetCommands()) != 1 || first.GetNextPageToken() == "" { t.Fatalf("first command page = %#v, %v", first, err) } changedFilter := &rvboxv1.ListCommandsRequest{ClientId: "win-a", IncludeTerminal: true, PageSize: 1, PageToken: first.GetNextPageToken()} if _, err := service.ListCommands(ctx, changedFilter); status.Code(err) != codes.InvalidArgument { t.Fatalf("changed-filter cursor code = %v", status.Code(err)) } second, err := service.ListCommands(ctx, &rvboxv1.ListCommandsRequest{ClientId: "win-a", PageSize: 1, PageToken: first.GetNextPageToken()}) if err != nil || len(second.GetCommands()) != 1 || second.GetCommands()[0].GetIssueUuid() == first.GetCommands()[0].GetIssueUuid() { t.Fatalf("second command page = %#v, %v", second, err) } } func TestControlGRPCRoundTrip_HP_CONTROL_06(t *testing.T) { service, persistence := newTestService(t) defer persistence.Close() registerControlClient(t, persistence, "win-a", rvboxv1.Platform_PLATFORM_WINDOWS, rvboxv1.ShellType_SHELL_CMD, 5) grpcServer := grpc.NewServer() rvboxv1.RegisterControlServer(grpcServer, service) listener := bufconn.Listen(1 << 20) go func() { _ = grpcServer.Serve(listener) }() defer grpcServer.Stop() ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() connection, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return listener.Dial() }), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) if err != nil { t.Fatal(err) } defer connection.Close() client := rvboxv1.NewControlClient(connection) response, err := client.RunCommand(ctx, &rvboxv1.RunCommandRequest{TargetClientId: "win-a", RequestId: fixedIssue(0xa5).String(), Spec: &rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_CMD, Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "echo wire"}}}) if err != nil || response.GetLifecycle() != rvboxv1.CommandLifecycle_COMMAND_QUEUED { t.Fatalf("gRPC run response = %#v, %v", response, err) } } func TestFollowCommandStreamsDurableEvents_HP_CONTROL_10(t *testing.T) { service, persistence := newTestService(t) defer persistence.Close() registerControlClient(t, persistence, "win-a", rvboxv1.Platform_PLATFORM_WINDOWS, rvboxv1.ShellType_SHELL_CMD, 6) issue := fixedIssue(0xa6) now := time.Now().UTC() specBytes, err := proto.Marshal(&rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_CMD, Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "echo"}}) if err != nil { t.Fatal(err) } if _, err := persistence.QueueCommand(context.Background(), store.QueueCommandInput{IssueUUID: issue, ClientID: "win-a", IssueTime: now, ReceiptTime: now, ImmutableSHA256: sha256.Sum256([]byte("follow")), ExecutionSpec: specBytes}); err != nil { t.Fatal(err) } if _, err := persistence.ClaimNextDispatch(context.Background(), "win-a", 1, now); err != nil { t.Fatal(err) } if _, err := persistence.RecordCommandAcceptance(context.Background(), issue, "win-a", 1, 1, true, now); err != nil { t.Fatal(err) } for index, lifecycle := range []rvboxv1.CommandLifecycle{rvboxv1.CommandLifecycle_COMMAND_RUNNING, rvboxv1.CommandLifecycle_COMMAND_SUCCEEDED} { event := &rvboxv1.CommandEvent{IssueUuid: issue.String(), EventSeq: uint64(index + 1), ObservedAt: timestamppb.New(now.Add(time.Duration(index) * time.Millisecond)), Payload: &rvboxv1.CommandEvent_Lifecycle{Lifecycle: &rvboxv1.LifecycleChange{Lifecycle: lifecycle, CommandRevision: 1}}} digest, digestErr := agentproto.CommandEventDigest(event) if digestErr != nil { t.Fatal(digestErr) } event.ImmutableEventSha256 = digest[:] payload, marshalErr := proto.MarshalOptions{Deterministic: true}.Marshal(event) if marshalErr != nil { t.Fatal(marshalErr) } if _, appendErr := persistence.AppendCommandEvent(context.Background(), store.EventAppend{IssueUUID: [16]byte(issue), ClientID: "win-a", SessionGeneration: 1, EventSeq: uint64(index + 1), ObservedUnixNano: event.GetObservedAt().AsTime().UnixNano(), ReceiptUnixNano: now.Add(time.Duration(index) * time.Millisecond).UnixNano(), EventType: 4, Compression: 1, RawLength: uint64(len(payload)), Payload: payload, ImmutableSHA256: digest, Lifecycle: &lifecycle, LifecycleRevision: 1}); appendErr != nil { t.Fatal(appendErr) } } stream := &testFollowStream{ctx: context.Background()} if err := service.FollowCommand(&rvboxv1.FollowCommandRequest{ClientId: "win-a", IssueUuid: issue.String(), IncludeExisting: true}, stream); err != nil { t.Fatal(err) } if len(stream.responses) != 2 || stream.responses[0].GetEvent().GetEventSeq() != 1 || stream.responses[1].GetEvent().GetLifecycle().GetLifecycle() != rvboxv1.CommandLifecycle_COMMAND_SUCCEEDED { t.Fatalf("follow responses = %#v", stream.responses) } } func TestGetOutputSlicesWithAuthenticatedCursor_HP_CONTROL_12(t *testing.T) { service, persistence := newTestService(t) defer persistence.Close() registerControlClient(t, persistence, "win-a", rvboxv1.Platform_PLATFORM_WINDOWS, rvboxv1.ShellType_SHELL_CMD, 7) issue := fixedIssue(0xa7) now := time.Now().UTC() specBytes, _ := proto.Marshal(&rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_CMD, Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "echo"}}) if _, err := persistence.QueueCommand(context.Background(), store.QueueCommandInput{IssueUUID: issue, ClientID: "win-a", IssueTime: now, ReceiptTime: now, ImmutableSHA256: sha256.Sum256([]byte("output")), ExecutionSpec: specBytes}); err != nil { t.Fatal(err) } if _, err := persistence.ClaimNextDispatch(context.Background(), "win-a", 1, now); err != nil { t.Fatal(err) } if _, err := persistence.RecordCommandAcceptance(context.Background(), issue, "win-a", 1, 1, true, now); err != nil { t.Fatal(err) } event := &rvboxv1.CommandEvent{IssueUuid: issue.String(), EventSeq: 1, ObservedAt: timestamppb.New(now), Payload: &rvboxv1.CommandEvent_Output{Output: &rvboxv1.OutputChunk{Stream: rvboxv1.StreamKind_STREAM_STDOUT, Compression: rvboxv1.Compression_COMPRESSION_NONE, Data: []byte("abcdef"), UncompressedSize: 6, CompressedSize: 6}}} digest, err := agentproto.CommandEventDigest(event) if err != nil { t.Fatal(err) } event.ImmutableEventSha256 = digest[:] payload, err := proto.MarshalOptions{Deterministic: true}.Marshal(event) if err != nil { t.Fatal(err) } if _, err := persistence.AppendCommandEvent(context.Background(), store.EventAppend{IssueUUID: [16]byte(issue), ClientID: "win-a", SessionGeneration: 1, EventSeq: 1, ObservedUnixNano: now.UnixNano(), ReceiptUnixNano: now.UnixNano(), EventType: 5, Stream: 1, Compression: 1, RawLength: uint64(len(payload)), Payload: payload, ImmutableSHA256: digest, Output: true}); err != nil { t.Fatal(err) } first, err := service.GetOutput(context.Background(), &rvboxv1.GetOutputRequest{ClientId: "win-a", IssueUuid: issue.String(), Streams: []rvboxv1.StreamKind{rvboxv1.StreamKind_STREAM_STDOUT}, MaxBytes: 3}) if err != nil || len(first.GetOutput()) != 1 || string(first.GetOutput()[0].GetData()) != "abc" || first.GetNextPageToken() == "" { t.Fatalf("first output page = %#v, %v", first, err) } second, err := service.GetOutput(context.Background(), &rvboxv1.GetOutputRequest{ClientId: "win-a", IssueUuid: issue.String(), Streams: []rvboxv1.StreamKind{rvboxv1.StreamKind_STREAM_STDOUT}, MaxBytes: 3, PageToken: first.GetNextPageToken()}) if err != nil || len(second.GetOutput()) != 1 || string(second.GetOutput()[0].GetData()) != "def" || !second.GetOutput()[0].GetEndOfEvent() { t.Fatalf("second output page = %#v, %v", second, err) } if _, err := service.GetOutput(context.Background(), &rvboxv1.GetOutputRequest{ClientId: "win-a", IssueUuid: issue.String(), Streams: []rvboxv1.StreamKind{rvboxv1.StreamKind_STREAM_STDERR}, MaxBytes: 3, PageToken: first.GetNextPageToken()}); status.Code(err) != codes.InvalidArgument { t.Fatalf("changed output filter code = %v", status.Code(err)) } } func TestControlMutationIdempotencyAndQueuedCancellation_BH_CONTROL_13(t *testing.T) { service, persistence := newTestService(t) defer persistence.Close() registerControlClient(t, persistence, "win-a", rvboxv1.Platform_PLATFORM_WINDOWS, rvboxv1.ShellType_SHELL_CMD, 8) issue := fixedIssue(0xa8) if _, err := service.RunCommand(context.Background(), &rvboxv1.RunCommandRequest{TargetClientId: "win-a", RequestId: issue.String(), Spec: &rvboxv1.ExecutionSpec{ShellType: rvboxv1.ShellType_SHELL_CMD, Source: &rvboxv1.ExecutionSpec_CommandText{CommandText: "wait"}}}); err != nil { t.Fatal(err) } stdinID := fixedIssue(0xa9) appendRequest := &rvboxv1.AppendStdinRequest{ClientId: "win-a", IssueUuid: issue.String(), RequestId: stdinID.String(), Data: []byte("hello"), AppendNewline: true} first, err := service.AppendStdin(context.Background(), appendRequest) if err != nil || first.GetWriteSeq() != 1 { t.Fatalf("append stdin = %#v, %v", first, err) } second, err := service.AppendStdin(context.Background(), proto.Clone(appendRequest).(*rvboxv1.AppendStdinRequest)) if err != nil || second.GetWriteSeq() != first.GetWriteSeq() { t.Fatalf("append replay = %#v, %v", second, err) } conflict := proto.Clone(appendRequest).(*rvboxv1.AppendStdinRequest) conflict.Data = []byte("different") if _, err := service.AppendStdin(context.Background(), conflict); status.Code(err) != codes.AlreadyExists { t.Fatalf("append conflict code = %v", status.Code(err)) } closeID := fixedIssue(0xaa) closed, err := service.CloseStdin(context.Background(), &rvboxv1.CloseStdinRequest{ClientId: "win-a", IssueUuid: issue.String(), RequestId: closeID.String()}) if err != nil || closed.GetWriteSeq() != 2 { t.Fatalf("close stdin = %#v, %v", closed, err) } signalID := fixedIssue(0xab) cancelled, err := service.SignalCommand(context.Background(), &rvboxv1.ControlSignalCommandRequest{ClientId: "win-a", IssueUuid: issue.String(), RequestId: signalID.String(), Signal: rvboxv1.SignalKind_SIGNAL_TERM}) if err != nil || cancelled.GetCommandRevision() != 2 { t.Fatalf("queued cancellation = %#v, %v", cancelled, err) } replay, err := service.SignalCommand(context.Background(), &rvboxv1.ControlSignalCommandRequest{ClientId: "win-a", IssueUuid: issue.String(), RequestId: signalID.String(), Signal: rvboxv1.SignalKind_SIGNAL_TERM}) if err != nil || replay.GetCommandRevision() != cancelled.GetCommandRevision() { t.Fatalf("cancel replay = %#v, %v", replay, err) } command, err := service.GetCommand(context.Background(), &rvboxv1.GetCommandRequest{ClientId: "win-a", IssueUuid: issue.String()}) if err != nil || command.GetCommand().GetLifecycle() != rvboxv1.CommandLifecycle_COMMAND_CANCELLED { t.Fatalf("cancelled command = %#v, %v", command, err) } } func TestStorageIncidentControlLifecycle_HP_CONTROL_14(t *testing.T) { service, persistence := newTestService(t) defer persistence.Close() incidentID := fixedIssue(0xac) if _, err := persistence.RecordIncident(context.Background(), store.IncidentInput{IncidentUUID: [16]byte(incidentID), DetectedAt: time.Date(2026, time.September, 6, 11, 59, 0, 0, time.UTC), Kind: store.IncidentChecksumMismatch, Scope: store.IncidentScopeGlobal, ScopeKey: "segments", Summary: "checksum mismatch", Evidence: []byte("evidence"), AutomaticallyRepairable: true}); err != nil { t.Fatal(err) } listed, err := service.ListStorageIncidents(context.Background(), &rvboxv1.ListStorageIncidentsRequest{PageSize: 1}) if err != nil || len(listed.GetIncidents()) != 1 || listed.GetIncidents()[0].GetState() != rvboxv1.StorageIncidentState_STORAGE_INCIDENT_STATE_OPEN { t.Fatalf("incident list = %#v, %v", listed, err) } repaired, err := service.RepairStorageIncident(context.Background(), &rvboxv1.RepairStorageIncidentRequest{IncidentId: incidentID.String(), RequestId: fixedIssue(0xad).String()}) if err != nil || repaired.GetIncident().GetState() != rvboxv1.StorageIncidentState_STORAGE_INCIDENT_STATE_REPAIRED { t.Fatalf("incident repair = %#v, %v", repaired, err) } if repeat, err := service.RepairStorageIncident(context.Background(), &rvboxv1.RepairStorageIncidentRequest{IncidentId: incidentID.String(), RequestId: fixedIssue(0xae).String()}); err != nil || repeat.GetIncident().GetState() != rvboxv1.StorageIncidentState_STORAGE_INCIDENT_STATE_REPAIRED { t.Fatalf("second repair = %#v, %v", repeat, err) } resolved, err := service.ListStorageIncidents(context.Background(), &rvboxv1.ListStorageIncidentsRequest{IncludeResolved: true}) if err != nil || len(resolved.GetIncidents()) != 1 || resolved.GetIncidents()[0].GetResolvedAt() == nil { t.Fatalf("resolved incident list = %#v, %v", resolved, err) } } type testFollowStream struct { ctx context.Context responses []*rvboxv1.FollowCommandResponse } func (stream *testFollowStream) Send(response *rvboxv1.FollowCommandResponse) error { stream.responses = append(stream.responses, response) return nil } func (stream *testFollowStream) SetHeader(metadata.MD) error { return nil } func (stream *testFollowStream) SendHeader(metadata.MD) error { return nil } func (stream *testFollowStream) SetTrailer(metadata.MD) {} func (stream *testFollowStream) Context() context.Context { return stream.ctx } func (stream *testFollowStream) SendMsg(any) error { return nil } func (stream *testFollowStream) RecvMsg(any) error { return nil } func newTestService(t *testing.T) (*Service, *store.Store) { t.Helper() persistence, err := store.Open(context.Background(), store.Options{DataDir: filepath.Join(t.TempDir(), "state"), BusyTimeout: time.Second}) if err != nil { t.Fatal(err) } service, err := NewService(Options{Store: persistence, CursorKey: bytesKey(), Now: func() time.Time { return time.Date(2026, time.September, 6, 12, 0, 0, 0, time.UTC) }, Limits: agentproto.DefaultLimits()}) if err != nil { persistence.Close() t.Fatal(err) } return service, persistence } func bytesKey() []byte { return []byte("0123456789abcdef0123456789abcdef") } func registerControlClient(t *testing.T, persistence *store.Store, clientID string, platform rvboxv1.Platform, shell rvboxv1.ShellType, seed byte) { t.Helper() shells, err := proto.Marshal(&rvboxv1.ClientHello{SupportedShells: []rvboxv1.ShellType{shell}}) if err != nil { t.Fatal(err) } if _, err := persistence.RegisterClientSession(context.Background(), store.ClientRegistration{ClientID: clientID, Platform: uint32(platform), Architecture: "amd64", DaemonVersion: "test", DaemonCWD: `C:\\ProgramData\\RVBox`, SupportedShells: shells, ClientInstanceID: [16]byte{seed}, SessionID: [16]byte{seed + 10}, ConnectedAt: time.Date(2026, time.September, 6, 11, 0, 0, 0, time.UTC)}); err != nil { t.Fatal(err) } } func fixedIssue(last byte) domain.UUID { value, _ := domain.ParseUUIDv7("019c46f1-1d02-7000-8000-000000000001") value[15] = last return value }