Files
beads/internal/rpc/server_test.go
Steve Yegge 872f203c57 Add RPC infrastructure and updated database
- RPC Phase 1: Protocol, server, client implementation
- Updated renumber.go with proper text reference updates (3-phase approach)
- Clean database exported: 344 issues (bd-1 to bd-344)
- Added DAEMON_DESIGN.md documentation
- Updated go.mod/go.sum for RPC dependencies

Amp-Thread-ID: https://ampcode.com/threads/T-456af77c-8b7f-4004-9027-c37b95e10ea5
Co-authored-by: Amp <amp@ampcode.com>
2025-10-16 20:36:23 -07:00

215 lines
6.4 KiB
Go

package rpc
import (
"context"
"encoding/json"
"net"
"os"
"path/filepath"
"testing"
"time"
"github.com/steveyegge/beads/internal/types"
)
type mockStorage struct{}
func (m *mockStorage) CreateIssue(ctx context.Context, issue *types.Issue, actor string) error {
return nil
}
func (m *mockStorage) CreateIssues(ctx context.Context, issues []*types.Issue, actor string) error {
return nil
}
func (m *mockStorage) GetIssue(ctx context.Context, id string) (*types.Issue, error) { return nil, nil }
func (m *mockStorage) UpdateIssue(ctx context.Context, id string, updates map[string]interface{}, actor string) error {
return nil
}
func (m *mockStorage) CloseIssue(ctx context.Context, id, reason, actor string) error { return nil }
func (m *mockStorage) SearchIssues(ctx context.Context, query string, filter types.IssueFilter) ([]*types.Issue, error) {
return nil, nil
}
func (m *mockStorage) AddDependency(ctx context.Context, dep *types.Dependency, actor string) error {
return nil
}
func (m *mockStorage) RemoveDependency(ctx context.Context, issueID, dependsOnID, actor string) error {
return nil
}
func (m *mockStorage) GetDependencies(ctx context.Context, issueID string) ([]*types.Issue, error) {
return nil, nil
}
func (m *mockStorage) GetDependents(ctx context.Context, issueID string) ([]*types.Issue, error) {
return nil, nil
}
func (m *mockStorage) GetDependencyRecords(ctx context.Context, issueID string) ([]*types.Dependency, error) {
return nil, nil
}
func (m *mockStorage) GetAllDependencyRecords(ctx context.Context) (map[string][]*types.Dependency, error) {
return nil, nil
}
func (m *mockStorage) GetDependencyTree(ctx context.Context, issueID string, maxDepth int) ([]*types.TreeNode, error) {
return nil, nil
}
func (m *mockStorage) DetectCycles(ctx context.Context) ([][]*types.Issue, error) { return nil, nil }
func (m *mockStorage) AddLabel(ctx context.Context, issueID, label, actor string) error {
return nil
}
func (m *mockStorage) RemoveLabel(ctx context.Context, issueID, label, actor string) error {
return nil
}
func (m *mockStorage) GetLabels(ctx context.Context, issueID string) ([]string, error) {
return nil, nil
}
func (m *mockStorage) GetIssuesByLabel(ctx context.Context, label string) ([]*types.Issue, error) {
return nil, nil
}
func (m *mockStorage) GetReadyWork(ctx context.Context, filter types.WorkFilter) ([]*types.Issue, error) {
return nil, nil
}
func (m *mockStorage) GetBlockedIssues(ctx context.Context) ([]*types.BlockedIssue, error) {
return nil, nil
}
func (m *mockStorage) AddComment(ctx context.Context, issueID, actor, comment string) error {
return nil
}
func (m *mockStorage) GetEvents(ctx context.Context, issueID string, limit int) ([]*types.Event, error) {
return nil, nil
}
func (m *mockStorage) GetStatistics(ctx context.Context) (*types.Statistics, error) {
return nil, nil
}
func (m *mockStorage) GetDirtyIssues(ctx context.Context) ([]string, error) { return nil, nil }
func (m *mockStorage) ClearDirtyIssues(ctx context.Context) error { return nil }
func (m *mockStorage) ClearDirtyIssuesByID(ctx context.Context, issueIDs []string) error {
return nil
}
func (m *mockStorage) SetConfig(ctx context.Context, key, value string) error { return nil }
func (m *mockStorage) GetConfig(ctx context.Context, key string) (string, error) {
return "", nil
}
func (m *mockStorage) SetMetadata(ctx context.Context, key, value string) error { return nil }
func (m *mockStorage) GetMetadata(ctx context.Context, key string) (string, error) {
return "", nil
}
func (m *mockStorage) UpdateIssueID(ctx context.Context, oldID, newID string, issue *types.Issue, actor string) error {
return nil
}
func (m *mockStorage) RenameDependencyPrefix(ctx context.Context, oldPrefix, newPrefix string) error {
return nil
}
func (m *mockStorage) RenameCounterPrefix(ctx context.Context, oldPrefix, newPrefix string) error {
return nil
}
func (m *mockStorage) Close() error { return nil }
func TestServerStartStop(t *testing.T) {
tmpDir := t.TempDir()
sockPath := filepath.Join(tmpDir, "test.sock")
store := &mockStorage{}
server := NewServer(store, sockPath)
if err := server.Start(); err != nil {
t.Fatalf("Failed to start server: %v", err)
}
if _, err := os.Stat(sockPath); os.IsNotExist(err) {
t.Fatal("Socket file was not created")
}
if err := server.Stop(); err != nil {
t.Fatalf("Failed to stop server: %v", err)
}
if _, err := os.Stat(sockPath); !os.IsNotExist(err) {
t.Fatal("Socket file was not removed")
}
}
func TestServerHandlesRequest(t *testing.T) {
tmpDir := t.TempDir()
sockPath := filepath.Join(tmpDir, "test.sock")
store := &mockStorage{}
server := NewServer(store, sockPath)
if err := server.Start(); err != nil {
t.Fatalf("Failed to start server: %v", err)
}
defer server.Stop()
time.Sleep(100 * time.Millisecond)
conn, err := net.Dial("unix", sockPath)
if err != nil {
t.Fatalf("Failed to connect to server: %v", err)
}
defer conn.Close()
req, _ := NewRequest(OpStats, nil)
reqJSON, _ := json.Marshal(req)
reqJSON = append(reqJSON, '\n')
if _, err := conn.Write(reqJSON); err != nil {
t.Fatalf("Failed to write request: %v", err)
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
t.Fatalf("Failed to read response: %v", err)
}
var resp Response
if err := json.Unmarshal(buf[:n], &resp); err != nil {
t.Fatalf("Failed to unmarshal response: %v", err)
}
if resp.Success {
t.Error("Expected error response for unimplemented operation")
}
}
func TestServerRejectsInvalidJSON(t *testing.T) {
tmpDir := t.TempDir()
sockPath := filepath.Join(tmpDir, "test.sock")
store := &mockStorage{}
server := NewServer(store, sockPath)
if err := server.Start(); err != nil {
t.Fatalf("Failed to start server: %v", err)
}
defer server.Stop()
time.Sleep(100 * time.Millisecond)
conn, err := net.Dial("unix", sockPath)
if err != nil {
t.Fatalf("Failed to connect to server: %v", err)
}
defer conn.Close()
if _, err := conn.Write([]byte("invalid json\n")); err != nil {
t.Fatalf("Failed to write request: %v", err)
}
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
t.Fatalf("Failed to read response: %v", err)
}
var resp Response
if err := json.Unmarshal(buf[:n], &resp); err != nil {
t.Fatalf("Failed to unmarshal response: %v", err)
}
if resp.Success {
t.Error("Expected error response for invalid JSON")
}
if resp.Error == "" {
t.Error("Expected error message")
}
}