634 lines
18 KiB
Go
634 lines
18 KiB
Go
package profile
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.k6n.net/mats/go-cart-actor/pkg/actor"
|
|
messages "git.k6n.net/mats/go-cart-actor/proto/profile"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// --- helpers (mirroring order/order_test.go) ---
|
|
|
|
func apply(t *testing.T, reg actor.MutationRegistry, g *ProfileGrain, msg proto.Message) error {
|
|
t.Helper()
|
|
results, err := reg.Apply(context.Background(), g, msg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(results) != 1 {
|
|
t.Fatalf("expected 1 result, got %d", len(results))
|
|
}
|
|
return results[0].Error
|
|
}
|
|
|
|
func mustApply(t *testing.T, reg actor.MutationRegistry, g *ProfileGrain, msg proto.Message) {
|
|
t.Helper()
|
|
if err := apply(t, reg, g, msg); err != nil {
|
|
t.Fatalf("apply %T: %v", msg, err)
|
|
}
|
|
}
|
|
|
|
func newRegistry() actor.MutationRegistry {
|
|
return NewProfileMutationRegistry()
|
|
}
|
|
|
|
// strPtr is a helper for proto optional string fields.
|
|
func strPtr(s string) *string { return &s }
|
|
func boolPtr(b bool) *bool { return &b }
|
|
|
|
// --- SetProfile ---
|
|
|
|
func TestSetProfileAllFields(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(1, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.SetProfile{
|
|
Name: strPtr("Jane Doe"),
|
|
Email: strPtr("jane@example.com"),
|
|
Phone: strPtr("+46 70 123 45 67"),
|
|
Language: strPtr("sv"),
|
|
Currency: strPtr("SEK"),
|
|
AvatarUrl: strPtr("https://cdn.example.com/avatar/jane.jpg"),
|
|
})
|
|
|
|
if g.Name != "Jane Doe" {
|
|
t.Fatalf("Name = %q, want %q", g.Name, "Jane Doe")
|
|
}
|
|
if g.Email != "jane@example.com" {
|
|
t.Fatalf("Email = %q, want %q", g.Email, "jane@example.com")
|
|
}
|
|
if g.Phone != "+46 70 123 45 67" {
|
|
t.Fatalf("Phone = %q, want %q", g.Phone, "+46 70 123 45 67")
|
|
}
|
|
if g.Language != "sv" {
|
|
t.Fatalf("Language = %q, want %q", g.Language, "sv")
|
|
}
|
|
if g.Currency != "SEK" {
|
|
t.Fatalf("Currency = %q, want %q", g.Currency, "SEK")
|
|
}
|
|
if g.AvatarUrl != "https://cdn.example.com/avatar/jane.jpg" {
|
|
t.Fatalf("AvatarUrl = %q, want %q", g.AvatarUrl, "https://cdn.example.com/avatar/jane.jpg")
|
|
}
|
|
}
|
|
|
|
func TestSetProfilePartialUpdate(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(2, time.Now())
|
|
g.Name = "Old Name"
|
|
g.Email = "old@example.com"
|
|
|
|
mustApply(t, reg, g, &messages.SetProfile{
|
|
Email: strPtr("updated@example.com"),
|
|
})
|
|
|
|
if g.Name != "Old Name" {
|
|
t.Fatalf("Name should be unchanged, got %q", g.Name)
|
|
}
|
|
if g.Email != "updated@example.com" {
|
|
t.Fatalf("Email = %q, want %q", g.Email, "updated@example.com")
|
|
}
|
|
}
|
|
|
|
func TestSetProfileNilMessage(t *testing.T) {
|
|
g := NewProfileGrain(3, time.Now())
|
|
if err := HandleSetProfile(g, nil); err == nil {
|
|
t.Fatal("expected error for nil SetProfile, got nil")
|
|
}
|
|
}
|
|
|
|
// --- AddAddress ---
|
|
|
|
func addrProto(id *uint32) *messages.Address {
|
|
return &messages.Address{
|
|
Label: "Home",
|
|
FullName: "Jane Doe",
|
|
AddressLine1: "Storgatan 1",
|
|
City: "Stockholm",
|
|
State: "Stockholm",
|
|
Zip: "111 22",
|
|
Country: "SE",
|
|
Phone: strPtr("+46 70 123 45 67"),
|
|
}
|
|
}
|
|
|
|
func TestAddAddress(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(10, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
|
|
if len(g.Addresses) != 1 {
|
|
t.Fatalf("expected 1 address, got %d", len(g.Addresses))
|
|
}
|
|
a := g.Addresses[0]
|
|
if a.Id != 1 {
|
|
t.Fatalf("expected address id 1, got %d", a.Id)
|
|
}
|
|
if a.Label != "Home" {
|
|
t.Fatalf("Label = %q, want %q", a.Label, "Home")
|
|
}
|
|
if a.City != "Stockholm" {
|
|
t.Fatalf("City = %q, want %q", a.City, "Stockholm")
|
|
}
|
|
if a.Phone != "+46 70 123 45 67" {
|
|
t.Fatalf("Phone = %q, want %q", a.Phone, "+46 70 123 45 67")
|
|
}
|
|
}
|
|
|
|
func TestAddAddressIncrementsId(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(11, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
|
|
if len(g.Addresses) != 3 {
|
|
t.Fatalf("expected 3 addresses, got %d", len(g.Addresses))
|
|
}
|
|
for i, a := range g.Addresses {
|
|
if want := uint32(i + 1); a.Id != want {
|
|
t.Fatalf("address %d id = %d, want %d", i, a.Id, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddAddressDefaultShippingClearsOthers(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(12, time.Now())
|
|
|
|
// Add two non-default addresses
|
|
addr1 := addrProto(nil)
|
|
addr1.IsDefaultShipping = false
|
|
addr1.IsDefaultBilling = false
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addr1})
|
|
|
|
addr2 := addrProto(nil)
|
|
addr2.IsDefaultShipping = false
|
|
addr2.IsDefaultBilling = false
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addr2})
|
|
|
|
// Add a default-shipping address — should clear the previous ones
|
|
addr3 := addrProto(nil)
|
|
addr3.IsDefaultShipping = true
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addr3})
|
|
|
|
if len(g.Addresses) != 3 {
|
|
t.Fatalf("expected 3 addresses, got %d", len(g.Addresses))
|
|
}
|
|
if !g.Addresses[2].IsDefaultShipping {
|
|
t.Fatal("expected address 3 to be default shipping")
|
|
}
|
|
for i := 0; i < 2; i++ {
|
|
if g.Addresses[i].IsDefaultShipping {
|
|
t.Fatalf("address %d should not be default shipping", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddAddressNil(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(13, time.Now())
|
|
|
|
if err := apply(t, reg, g, &messages.AddAddress{Address: nil}); err == nil {
|
|
t.Fatal("expected error for nil address, got nil")
|
|
}
|
|
}
|
|
|
|
// --- UpdateAddress ---
|
|
|
|
func TestUpdateAddressAllFields(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(20, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
|
|
mustApply(t, reg, g, &messages.UpdateAddress{
|
|
Id: 1,
|
|
Label: strPtr("Work"),
|
|
FullName: strPtr("Jane Doe Updated"),
|
|
AddressLine1: strPtr("Drottninggatan 10"),
|
|
City: strPtr("Göteborg"),
|
|
State: strPtr("Västra Götaland"),
|
|
Zip: strPtr("411 03"),
|
|
Country: strPtr("SE"),
|
|
Phone: strPtr("+46 70 987 65 43"),
|
|
})
|
|
|
|
a := g.Addresses[0]
|
|
if a.Label != "Work" {
|
|
t.Fatalf("Label = %q, want %q", a.Label, "Work")
|
|
}
|
|
if a.City != "Göteborg" {
|
|
t.Fatalf("City = %q, want %q", a.City, "Göteborg")
|
|
}
|
|
if a.Phone != "+46 70 987 65 43" {
|
|
t.Fatalf("Phone = %q, want %q", a.Phone, "+46 70 987 65 43")
|
|
}
|
|
}
|
|
|
|
func TestUpdateAddressPartialFields(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(21, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
|
|
// Update only the label
|
|
mustApply(t, reg, g, &messages.UpdateAddress{
|
|
Id: 1,
|
|
Label: strPtr("Summer Home"),
|
|
})
|
|
|
|
a := g.Addresses[0]
|
|
if a.Label != "Summer Home" {
|
|
t.Fatalf("Label = %q, want %q", a.Label, "Summer Home")
|
|
}
|
|
if a.City != "Stockholm" {
|
|
t.Fatalf("City should be unchanged, got %q", a.City)
|
|
}
|
|
}
|
|
|
|
func TestUpdateAddressNotFound(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(22, time.Now())
|
|
|
|
if err := apply(t, reg, g, &messages.UpdateAddress{Id: 999, Label: strPtr("Nowhere")}); err == nil {
|
|
t.Fatal("expected error for non-existent address, got nil")
|
|
}
|
|
}
|
|
|
|
func TestUpdateAddressDefaultShippingClearsOthers(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(23, time.Now())
|
|
|
|
addr1 := addrProto(nil)
|
|
addr1.IsDefaultShipping = true
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addr1})
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
|
|
// Update address 2 to be default shipping — should clear address 1
|
|
mustApply(t, reg, g, &messages.UpdateAddress{
|
|
Id: 2,
|
|
IsDefaultShipping: boolPtr(true),
|
|
})
|
|
|
|
if !g.Addresses[1].IsDefaultShipping {
|
|
t.Fatal("expected address 2 to be default shipping")
|
|
}
|
|
if g.Addresses[0].IsDefaultShipping {
|
|
t.Fatal("address 1 should no longer be default shipping")
|
|
}
|
|
}
|
|
|
|
// --- RemoveAddress ---
|
|
|
|
func TestRemoveAddress(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(30, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
|
|
mustApply(t, reg, g, &messages.RemoveAddress{Id: 2})
|
|
|
|
if len(g.Addresses) != 2 {
|
|
t.Fatalf("expected 2 addresses after removal, got %d", len(g.Addresses))
|
|
}
|
|
if g.Addresses[0].Id != 1 {
|
|
t.Fatalf("expected address id 1 at index 0, got %d", g.Addresses[0].Id)
|
|
}
|
|
if g.Addresses[1].Id != 3 {
|
|
t.Fatalf("expected address id 3 at index 1, got %d", g.Addresses[1].Id)
|
|
}
|
|
}
|
|
|
|
func TestRemoveAddressNotFound(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(31, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
|
|
if err := apply(t, reg, g, &messages.RemoveAddress{Id: 99}); err == nil {
|
|
t.Fatal("expected error for removing non-existent address, got nil")
|
|
}
|
|
}
|
|
|
|
func TestRemoveAddressLastElement(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(32, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.AddAddress{Address: addrProto(nil)})
|
|
mustApply(t, reg, g, &messages.RemoveAddress{Id: 1})
|
|
|
|
if len(g.Addresses) != 0 {
|
|
t.Fatalf("expected 0 addresses, got %d", len(g.Addresses))
|
|
}
|
|
}
|
|
|
|
// --- LinkCart ---
|
|
|
|
func TestLinkCart(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(40, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.LinkCart{
|
|
CartId: 1001,
|
|
Label: "current cart",
|
|
})
|
|
|
|
if len(g.Carts) != 1 {
|
|
t.Fatalf("expected 1 linked cart, got %d", len(g.Carts))
|
|
}
|
|
if g.Carts[0].CartId != 1001 {
|
|
t.Fatalf("CartId = %d, want %d", g.Carts[0].CartId, 1001)
|
|
}
|
|
if g.Carts[0].Label != "current cart" {
|
|
t.Fatalf("Label = %q, want %q", g.Carts[0].Label, "current cart")
|
|
}
|
|
}
|
|
|
|
func TestLinkCartMultiple(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(41, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.LinkCart{CartId: 1, Label: "first"})
|
|
mustApply(t, reg, g, &messages.LinkCart{CartId: 2, Label: "second"})
|
|
|
|
if len(g.Carts) != 2 {
|
|
t.Fatalf("expected 2 linked carts, got %d", len(g.Carts))
|
|
}
|
|
}
|
|
|
|
func TestLinkCartDuplicateIdempotent(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(42, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.LinkCart{CartId: 1001})
|
|
mustApply(t, reg, g, &messages.LinkCart{CartId: 1001}) // same cart again
|
|
|
|
if len(g.Carts) != 1 {
|
|
t.Fatalf("expected 1 linked cart (idempotent), got %d", len(g.Carts))
|
|
}
|
|
}
|
|
|
|
func TestLinkCartMissingId(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(43, time.Now())
|
|
|
|
if err := apply(t, reg, g, &messages.LinkCart{CartId: 0}); err == nil {
|
|
t.Fatal("expected error for zero cartId, got nil")
|
|
}
|
|
}
|
|
|
|
// --- LinkCheckout ---
|
|
|
|
func TestLinkCheckout(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(50, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.LinkCheckout{
|
|
CheckoutId: 2001,
|
|
CartId: 1001,
|
|
})
|
|
|
|
if len(g.Checkouts) != 1 {
|
|
t.Fatalf("expected 1 linked checkout, got %d", len(g.Checkouts))
|
|
}
|
|
if g.Checkouts[0].CheckoutId != 2001 {
|
|
t.Fatalf("CheckoutId = %d, want %d", g.Checkouts[0].CheckoutId, 2001)
|
|
}
|
|
if g.Checkouts[0].CartId != 1001 {
|
|
t.Fatalf("CartId = %d, want %d", g.Checkouts[0].CartId, 1001)
|
|
}
|
|
}
|
|
|
|
func TestLinkCheckoutMultiple(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(51, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.LinkCheckout{CheckoutId: 1, CartId: 10})
|
|
mustApply(t, reg, g, &messages.LinkCheckout{CheckoutId: 2, CartId: 20})
|
|
|
|
if len(g.Checkouts) != 2 {
|
|
t.Fatalf("expected 2 linked checkouts, got %d", len(g.Checkouts))
|
|
}
|
|
}
|
|
|
|
func TestLinkCheckoutDuplicateIdempotent(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(52, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.LinkCheckout{CheckoutId: 2001, CartId: 1001})
|
|
mustApply(t, reg, g, &messages.LinkCheckout{CheckoutId: 2001, CartId: 1001})
|
|
|
|
if len(g.Checkouts) != 1 {
|
|
t.Fatalf("expected 1 linked checkout (idempotent), got %d", len(g.Checkouts))
|
|
}
|
|
}
|
|
|
|
func TestLinkCheckoutMissingId(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(53, time.Now())
|
|
|
|
if err := apply(t, reg, g, &messages.LinkCheckout{CheckoutId: 0}); err == nil {
|
|
t.Fatal("expected error for zero checkoutId, got nil")
|
|
}
|
|
}
|
|
|
|
// --- LinkOrder ---
|
|
|
|
func TestLinkOrder(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(60, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.LinkOrder{
|
|
OrderReference: "ORD-001",
|
|
CartId: 1001,
|
|
Status: "pending",
|
|
})
|
|
|
|
if len(g.Orders) != 1 {
|
|
t.Fatalf("expected 1 linked order, got %d", len(g.Orders))
|
|
}
|
|
if g.Orders[0].OrderReference != "ORD-001" {
|
|
t.Fatalf("OrderReference = %q, want %q", g.Orders[0].OrderReference, "ORD-001")
|
|
}
|
|
if g.Orders[0].CartId != 1001 {
|
|
t.Fatalf("CartId = %d, want %d", g.Orders[0].CartId, 1001)
|
|
}
|
|
if g.Orders[0].Status != "pending" {
|
|
t.Fatalf("Status = %q, want %q", g.Orders[0].Status, "pending")
|
|
}
|
|
}
|
|
|
|
func TestLinkOrderUpdatesStatusOnDuplicate(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(61, time.Now())
|
|
|
|
mustApply(t, reg, g, &messages.LinkOrder{
|
|
OrderReference: "ORD-001",
|
|
CartId: 1001,
|
|
Status: "pending",
|
|
})
|
|
mustApply(t, reg, g, &messages.LinkOrder{
|
|
OrderReference: "ORD-001",
|
|
CartId: 1001,
|
|
Status: "captured",
|
|
})
|
|
|
|
if len(g.Orders) != 1 {
|
|
t.Fatalf("expected 1 linked order (idempotent), got %d", len(g.Orders))
|
|
}
|
|
if g.Orders[0].Status != "captured" {
|
|
t.Fatalf("Status = %q, want %q", g.Orders[0].Status, "captured")
|
|
}
|
|
}
|
|
|
|
func TestLinkOrderMissingReference(t *testing.T) {
|
|
reg := newRegistry()
|
|
g := NewProfileGrain(62, time.Now())
|
|
|
|
if err := apply(t, reg, g, &messages.LinkOrder{OrderReference: "", CartId: 1001}); err == nil {
|
|
t.Fatal("expected error for empty orderReference, got nil")
|
|
}
|
|
}
|
|
|
|
// --- Nil message edge cases (call handlers directly with typed nil pointers;
|
|
// the registry silently skips typed-nil messages, so Apply does not reach
|
|
// the handler and we must test the handler in isolation) ---
|
|
|
|
func TestNilMessages(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
err error
|
|
}{
|
|
{"nil SetProfile", HandleSetProfile(NewProfileGrain(70, time.Now()), (*messages.SetProfile)(nil))},
|
|
{"nil AddAddress", HandleAddAddress(NewProfileGrain(70, time.Now()), (*messages.AddAddress)(nil))},
|
|
{"nil UpdateAddress", HandleUpdateAddress(NewProfileGrain(70, time.Now()), (*messages.UpdateAddress)(nil))},
|
|
{"nil RemoveAddress", HandleRemoveAddress(NewProfileGrain(70, time.Now()), (*messages.RemoveAddress)(nil))},
|
|
{"nil LinkCart", HandleLinkCart(NewProfileGrain(70, time.Now()), (*messages.LinkCart)(nil))},
|
|
{"nil LinkCheckout", HandleLinkCheckout(NewProfileGrain(70, time.Now()), (*messages.LinkCheckout)(nil))},
|
|
{"nil LinkOrder", HandleLinkOrder(NewProfileGrain(70, time.Now()), (*messages.LinkOrder)(nil))},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.err == nil {
|
|
t.Fatal("expected error for nil message, got nil")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// --- Event-sourced replay test (mirroring order/order_test.go) ---
|
|
|
|
func TestEventSourcedReplay(t *testing.T) {
|
|
reg := newRegistry()
|
|
storage := actor.NewDiskStorage[ProfileGrain](t.TempDir(), reg)
|
|
|
|
const id uint64 = 100
|
|
g := NewProfileGrain(id, time.Now())
|
|
|
|
events := []proto.Message{
|
|
&messages.SetProfile{
|
|
Name: strPtr("Jane Doe"),
|
|
Email: strPtr("jane@example.com"),
|
|
},
|
|
&messages.AddAddress{Address: addrProto(nil)},
|
|
&messages.LinkCart{CartId: 1001, Label: "current"},
|
|
&messages.LinkCheckout{CheckoutId: 2001, CartId: 1001},
|
|
&messages.LinkOrder{OrderReference: "ORD-001", CartId: 1001, Status: "pending"},
|
|
&messages.AddAddress{
|
|
Address: &messages.Address{
|
|
Label: "Work",
|
|
FullName: "Jane Doe",
|
|
AddressLine1: "Kungsgatan 5",
|
|
City: "Malmö",
|
|
State: "Skåne",
|
|
Zip: "211 11",
|
|
Country: "SE",
|
|
},
|
|
},
|
|
&messages.UpdateAddress{
|
|
Id: 1,
|
|
Label: strPtr("Home (Updated)"),
|
|
},
|
|
&messages.LinkOrder{OrderReference: "ORD-002", CartId: 1002, Status: "captured"},
|
|
}
|
|
|
|
for _, e := range events {
|
|
mustApply(t, reg, g, e)
|
|
if err := storage.AppendMutations(id, e); err != nil {
|
|
t.Fatalf("append: %v", err)
|
|
}
|
|
}
|
|
|
|
// Verify initial grain state before replay.
|
|
if g.Name != "Jane Doe" {
|
|
t.Fatalf("Name = %q, want %q", g.Name, "Jane Doe")
|
|
}
|
|
if len(g.Addresses) != 2 {
|
|
t.Fatalf("expected 2 addresses, got %d", len(g.Addresses))
|
|
}
|
|
if g.Addresses[0].Label != "Home (Updated)" {
|
|
t.Fatalf("first address label = %q, want %q", g.Addresses[0].Label, "Home (Updated)")
|
|
}
|
|
if len(g.Carts) != 1 {
|
|
t.Fatalf("expected 1 cart, got %d", len(g.Carts))
|
|
}
|
|
if len(g.Checkouts) != 1 {
|
|
t.Fatalf("expected 1 checkout, got %d", len(g.Checkouts))
|
|
}
|
|
if len(g.Orders) != 2 {
|
|
t.Fatalf("expected 2 orders, got %d", len(g.Orders))
|
|
}
|
|
if g.Orders[1].Status != "captured" {
|
|
t.Fatalf("second order status = %q, want %q", g.Orders[1].Status, "captured")
|
|
}
|
|
|
|
// Replay into a brand-new grain and compare the projection.
|
|
replayed := NewProfileGrain(id, time.Now())
|
|
if err := storage.LoadEvents(context.Background(), id, replayed); err != nil {
|
|
t.Fatalf("replay: %v", err)
|
|
}
|
|
|
|
if replayed.Name != g.Name {
|
|
t.Fatalf("replayed Name = %q, want %q", replayed.Name, g.Name)
|
|
}
|
|
if replayed.Email != g.Email {
|
|
t.Fatalf("replayed Email = %q, want %q", replayed.Email, g.Email)
|
|
}
|
|
if len(replayed.Addresses) != len(g.Addresses) {
|
|
t.Fatalf("replayed addresses = %d, want %d", len(replayed.Addresses), len(g.Addresses))
|
|
}
|
|
if replayed.Addresses[0].Label != g.Addresses[0].Label {
|
|
t.Fatalf("replayed first address label = %q, want %q", replayed.Addresses[0].Label, g.Addresses[0].Label)
|
|
}
|
|
if replayed.Addresses[0].Id != g.Addresses[0].Id {
|
|
t.Fatalf("replayed first address id = %d, want %d", replayed.Addresses[0].Id, g.Addresses[0].Id)
|
|
}
|
|
// Address book: second address should have been added AFTER the update to
|
|
// address 1, so its id is 2 and it should match the original.
|
|
if replayed.Addresses[1].City != g.Addresses[1].City {
|
|
t.Fatalf("replayed second address city = %q, want %q", replayed.Addresses[1].City, g.Addresses[1].City)
|
|
}
|
|
if len(replayed.Carts) != len(g.Carts) {
|
|
t.Fatalf("replayed carts = %d, want %d", len(replayed.Carts), len(g.Carts))
|
|
}
|
|
if len(replayed.Checkouts) != len(g.Checkouts) {
|
|
t.Fatalf("replayed checkouts = %d, want %d", len(replayed.Checkouts), len(g.Checkouts))
|
|
}
|
|
if len(replayed.Orders) != len(g.Orders) {
|
|
t.Fatalf("replayed orders = %d, want %d", len(replayed.Orders), len(g.Orders))
|
|
}
|
|
if replayed.Orders[0].Status != g.Orders[0].Status {
|
|
t.Fatalf("replayed first order status = %q, want %q", replayed.Orders[0].Status, g.Orders[0].Status)
|
|
}
|
|
if replayed.Orders[1].Status != g.Orders[1].Status {
|
|
t.Fatalf("replayed second order status = %q, want %q", replayed.Orders[1].Status, g.Orders[1].Status)
|
|
}
|
|
}
|