package actor import ( "context" "testing" cart_messages "git.k6n.net/go-cart-actor/proto/cart" control_plane_messages "git.k6n.net/go-cart-actor/proto/control" "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{ {Type: "AddItem", Mutation: &cart_messages.AddItem{ItemId: 1, Quantity: 2}, Error: nil}, {Type: "RemoveItem", Mutation: &cart_messages.RemoveItem{Id: 1}, Error: nil}, }, }, nil } func (m *mockGrainPool) Get(ctx context.Context, id uint64) (*mockGrain, error) { return &mockGrain{}, nil } func (m *mockGrainPool) OwnerHost(id uint64) (Host[mockGrain], 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 := control_plane_messages.NewControlPlaneClient(conn) // Prepare ApplyRequest with multiple Any messages addItemAny, _ := anypb.New(&cart_messages.AddItem{ItemId: 1, Quantity: 2}) removeItemAny, _ := anypb.New(&cart_messages.RemoveItem{Id: 1}) req := &control_plane_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.State == nil { t.Errorf("expected State to be non-nil") } if len(resp.Mutations) != 2 { t.Errorf("expected 2 mutation results, got %d", len(resp.Mutations)) } for i, mut := range resp.Mutations { if mut.Error != nil { t.Errorf("expected no error in mutation %d, got %s", i, *mut.Error) } } // 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].(*cart_messages.AddItem); !ok || addItem.ItemId != 1 { t.Errorf("expected AddItem with ItemId=1, got %v", pool.applied[0]) } if removeItem, ok := pool.applied[1].(*cart_messages.RemoveItem); !ok || removeItem.Id != 1 { t.Errorf("expected RemoveItem with Id=1, got %v", pool.applied[1]) } } func TestGetRequest(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 := control_plane_messages.NewControlPlaneClient(conn) // Prepare GetRequest req := &control_plane_messages.GetRequest{ Id: 123, } // Call Get resp, err := client.Get(context.Background(), req) if err != nil { t.Fatalf("Get failed: %v", err) } // Verify response if resp.Grain == nil { t.Errorf("expected Grain to be non-nil") } }