package actor import ( "context" "testing" "git.k6n.net/go-cart-actor/pkg/messages" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" ) // MockGrainPool for testing type mockGrainPool struct { applied []proto.Message } func (m *mockGrainPool) Apply(ctx context.Context, id uint64, mutations ...proto.Message) (*MutationResult[*mockGrain], error) { m.applied = mutations // Simulate successful application return &MutationResult[*mockGrain]{ Result: &mockGrain{}, Mutations: []ApplyResult{ {Error: nil}, // Assume success {Error: nil}, }, }, nil } func (m *mockGrainPool) Get(ctx context.Context, id uint64) (*mockGrain, error) { return &mockGrain{}, nil } func (m *mockGrainPool) OwnerHost(id uint64) (Host, bool) { return nil, false } func (m *mockGrainPool) TakeOwnership(id uint64) {} func (m *mockGrainPool) Hostname() string { return "test-host" } func (m *mockGrainPool) HandleOwnershipChange(host string, ids []uint64) error { return nil } func (m *mockGrainPool) HandleRemoteExpiry(host string, ids []uint64) error { return nil } func (m *mockGrainPool) Negotiate(hosts []string) {} func (m *mockGrainPool) GetLocalIds() []uint64 { return []uint64{} } func (m *mockGrainPool) RemoveHost(host string) {} func (m *mockGrainPool) AddRemoteHost(host string) {} func (m *mockGrainPool) IsHealthy() bool { return true } func (m *mockGrainPool) IsKnown(host string) bool { return false } func (m *mockGrainPool) Close() {} type mockGrain struct{} func TestApplyRequestWithMutations(t *testing.T) { // Setup mock pool pool := &mockGrainPool{} // Create gRPC server server, err := NewControlServer[*mockGrain](DefaultServerConfig(), pool) if err != nil { t.Fatalf("failed to create server: %v", err) } defer server.GracefulStop() // Create client connection conn, err := grpc.Dial("localhost:1337", grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { t.Fatalf("failed to dial: %v", err) } defer conn.Close() client := messages.NewControlPlaneClient(conn) // Prepare ApplyRequest with multiple Any messages addItemAny, _ := anypb.New(&messages.AddItem{ItemId: 1, Quantity: 2}) removeItemAny, _ := anypb.New(&messages.RemoveItem{Id: 1}) req := &messages.ApplyRequest{ Id: 123, Messages: []*anypb.Any{addItemAny, removeItemAny}, } // Call Apply resp, err := client.Apply(context.Background(), req) if err != nil { t.Fatalf("Apply failed: %v", err) } // Verify response if !resp.Accepted { t.Errorf("expected Accepted=true, got false") } // Verify mutations were extracted and applied if len(pool.applied) != 2 { t.Errorf("expected 2 mutations applied, got %d", len(pool.applied)) } if addItem, ok := pool.applied[0].(*messages.AddItem); !ok || addItem.ItemId != 1 { t.Errorf("expected AddItem with ItemId=1, got %v", pool.applied[0]) } if removeItem, ok := pool.applied[1].(*messages.RemoveItem); !ok || removeItem.Id != 1 { t.Errorf("expected RemoveItem with Id=1, got %v", pool.applied[1]) } }