refactor/checkout (#8)
All checks were successful
Build and Publish / BuildAndDeployAmd64 (push) Successful in 59s
Build and Publish / BuildAndDeployArm64 (push) Successful in 5m40s

Co-authored-by: matst80 <mats.tornberg@gmail.com>
Reviewed-on: #8
Co-authored-by: Mats Törnberg <mats@tornberg.me>
Co-committed-by: Mats Törnberg <mats@tornberg.me>
This commit was merged in pull request #8.
This commit is contained in:
2025-12-03 09:45:48 +01:00
committed by mats
parent ebd1508294
commit ee5f54f0dd
77 changed files with 5190 additions and 5795 deletions

View File

@@ -8,7 +8,7 @@ import (
"net"
"time"
messages "git.k6n.net/go-cart-actor/pkg/messages"
messages "git.k6n.net/go-cart-actor/proto/control"
"go.opentelemetry.io/contrib/bridges/otelslog"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
@@ -145,7 +145,7 @@ func (s *ControlServer[V]) AnnounceExpiry(ctx context.Context, req *messages.Exp
}
// ControlPlane: Ping
func (s *ControlServer[V]) Ping(ctx context.Context, _ *messages.Empty) (*messages.PingReply, error) {
func (s *ControlServer[V]) Ping(ctx context.Context, req *messages.Empty) (*messages.PingReply, error) {
host := s.pool.Hostname()
@@ -191,7 +191,7 @@ func (s *ControlServer[V]) Negotiate(ctx context.Context, req *messages.Negotiat
}
// ControlPlane: GetCartIds (locally owned carts only)
func (s *ControlServer[V]) GetLocalActorIds(ctx context.Context, _ *messages.Empty) (*messages.ActorIdsReply, error) {
func (s *ControlServer[V]) GetLocalActorIds(ctx context.Context, req *messages.Empty) (*messages.ActorIdsReply, error) {
ctx, span := tracer.Start(ctx, "grpc_get_local_actor_ids")
defer span.End()
ids := s.pool.GetLocalIds()

View File

@@ -4,7 +4,8 @@ import (
"context"
"testing"
"git.k6n.net/go-cart-actor/pkg/messages"
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"
@@ -70,12 +71,12 @@ func TestApplyRequestWithMutations(t *testing.T) {
}
defer conn.Close()
client := messages.NewControlPlaneClient(conn)
client := control_plane_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{
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},
}
@@ -95,10 +96,10 @@ func TestApplyRequestWithMutations(t *testing.T) {
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 {
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].(*messages.RemoveItem); !ok || removeItem.Id != 1 {
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])
}
}

View File

@@ -95,14 +95,18 @@ type MutationHandler interface {
type RegisteredMutation[V any, T proto.Message] struct {
name string
handler func(*V, T) error
create func() T
create func() proto.Message
msgType reflect.Type
}
func NewMutation[V any, T proto.Message](handler func(*V, T) error, create func() T) *RegisteredMutation[V, T] {
func NewMutation[V any, T proto.Message](handler func(*V, T) error) *RegisteredMutation[V, T] {
// Derive the name and message type from a concrete instance produced by create().
// This avoids relying on reflect.TypeFor (which can yield unexpected results in some toolchains)
// and ensures we always peel off the pointer layer for proto messages.
create := func() proto.Message {
m := new(T)
return *m
}
instance := create()
rt := reflect.TypeOf(instance)
if rt.Kind() == reflect.Ptr {

View File

@@ -6,33 +6,32 @@ import (
"slices"
"testing"
"git.k6n.net/go-cart-actor/pkg/messages"
cart_messages "git.k6n.net/go-cart-actor/proto/cart"
)
type cartState struct {
calls int
lastAdded *messages.AddItem
lastAdded *cart_messages.AddItem
}
func TestRegisteredMutationBasics(t *testing.T) {
reg := NewMutationRegistry().(*ProtoMutationRegistry)
addItemMutation := NewMutation(
func(state *cartState, msg *messages.AddItem) error {
func(state *cartState, msg *cart_messages.AddItem) error {
state.calls++
// copy to avoid external mutation side-effects (not strictly necessary for the test)
cp := msg
state.lastAdded = cp
return nil
},
func() *messages.AddItem { return &messages.AddItem{} },
)
// Sanity check on mutation metadata
if addItemMutation.Name() != "AddItem" {
t.Fatalf("expected mutation Name() == AddItem, got %s", addItemMutation.Name())
}
if got, want := addItemMutation.Type(), reflect.TypeOf(messages.AddItem{}); got != want {
if got, want := addItemMutation.Type(), reflect.TypeOf(cart_messages.AddItem{}); got != want {
t.Fatalf("expected Type() == %v, got %v", want, got)
}
@@ -46,18 +45,18 @@ func TestRegisteredMutationBasics(t *testing.T) {
// RegisteredMutationTypes: membership (order not guaranteed)
types := reg.RegisteredMutationTypes()
if !slices.Contains(types, reflect.TypeOf(messages.AddItem{})) {
if !slices.Contains(types, reflect.TypeOf(cart_messages.AddItem{})) {
t.Fatalf("RegisteredMutationTypes missing AddItem type, got %v", types)
}
// GetTypeName should resolve for a pointer instance
name, ok := reg.GetTypeName(&messages.AddItem{})
name, ok := reg.GetTypeName(&cart_messages.AddItem{})
if !ok || name != "AddItem" {
t.Fatalf("GetTypeName returned (%q,%v), expected (AddItem,true)", name, ok)
}
// GetTypeName should fail for unregistered type
if name, ok := reg.GetTypeName(&messages.Noop{}); ok || name != "" {
if name, ok := reg.GetTypeName(&cart_messages.RemoveItem{}); ok || name != "" {
t.Fatalf("expected GetTypeName to fail for unregistered message, got (%q,%v)", name, ok)
}
@@ -66,7 +65,7 @@ func TestRegisteredMutationBasics(t *testing.T) {
if !ok {
t.Fatalf("Create failed for registered mutation")
}
if _, isAddItem := msg.(*messages.AddItem); !isAddItem {
if _, isAddItem := msg.(*cart_messages.AddItem); !isAddItem {
t.Fatalf("Create returned wrong concrete type: %T", msg)
}
@@ -77,7 +76,7 @@ func TestRegisteredMutationBasics(t *testing.T) {
// Apply happy path
state := &cartState{}
add := &messages.AddItem{ItemId: 42, Quantity: 3, Sku: "ABC"}
add := &cart_messages.AddItem{ItemId: 42, Quantity: 3, Sku: "ABC"}
if _, err := reg.Apply(context.Background(), state, add); err != nil {
t.Fatalf("Apply returned error: %v", err)
}
@@ -99,7 +98,7 @@ func TestRegisteredMutationBasics(t *testing.T) {
}
// Apply unregistered message
_, err := reg.Apply(context.Background(), state, &messages.Noop{})
_, err := reg.Apply(context.Background(), state, &cart_messages.RemoveItem{})
if err != ErrMutationNotRegistered {
t.Fatalf("expected ErrMutationNotRegistered, got %v", err)
}