func TestInMemoryEventBus(t *testing.T) { bus := cqrs.NewInMemoryEventBus() eventType := reflect.TypeOf(SampleEvent{}) // Create communication channels // // for closing the queue listener, closeChannel := make(chan chan error) // receiving errors from the listener thread (go routine) errorChannel := make(chan error) // and receiving events from the queue receiveEventChannel := make(chan cqrs.VersionedEventTransactedAccept) // Start receiving events by passing these channels to the worker thread (go routine) if err := bus.ReceiveEvents(cqrs.VersionedEventReceiverOptions{nil, closeChannel, errorChannel, receiveEventChannel, false}); err != nil { t.Fatal(err) } // Publish a simple event log.Println("Publishing events") go func() { if err := bus.PublishEvents([]cqrs.VersionedEvent{cqrs.VersionedEvent{ EventType: eventType.String(), Event: SampleEvent{"TestInMemoryEventBus"}}}); err != nil { t.Fatal(err) } }() // If we dont receive a message within 5 seconds this test is a failure. Use a channel to signal the timeout timeout := make(chan bool, 1) go func() { time.Sleep(5 * time.Second) timeout <- true }() // Wait on multiple channels using the select control flow. select { // Test timeout case <-timeout: t.Fatal("Test timed out") // Version event received channel receives a result with a channel to respond to, signifying successful processing of the message. // This should eventually call an event handler. See cqrs.NewVersionedEventDispatcher() case event := <-receiveEventChannel: sampleEvent := event.Event.Event.(SampleEvent) log.Println(sampleEvent.Message) event.ProcessedSuccessfully <- true // Receiving on this channel signifys an error has occured work processor side case err := <-errorChannel: t.Fatal(err) } closeResponse := make(chan error) closeChannel <- closeResponse <-closeResponse }
func TestScenario(t *testing.T) { log.SetFlags(log.LstdFlags | log.Lshortfile) // Type Registry typeRegistry := cqrs.NewTypeRegistry() // Event sourcing persistance := cqrs.NewInMemoryEventStreamRepository() bus := cqrs.NewInMemoryEventBus() repository := cqrs.NewRepositoryWithPublisher(persistance, bus, typeRegistry) typeRegistry.RegisterAggregate(&Account{}, AccountCreatedEvent{}, EmailAddressChangedEvent{}, AccountCreditedEvent{}, AccountDebitedEvent{}, PasswordChangedEvent{}) // Read Models readModel := NewReadModelAccounts() usersModel := NewUsersModel() // Command Handlers commandBus := cqrs.NewInMemoryCommandBus() commandDispatcher := cqrs.NewCommandDispatchManager(commandBus, typeRegistry) RegisterCommandHandlers(commandDispatcher, repository) // Integration events eventDispatcher := cqrs.NewVersionedEventDispatchManager(bus, typeRegistry) integrationEventsLog := cqrs.NewInMemoryEventStreamRepository() RegisterIntegrationEventHandlers(eventDispatcher, integrationEventsLog, readModel, usersModel) commandDispatcherStopChannel := make(chan bool) eventDispatcherStopChannel := make(chan bool) go commandDispatcher.Listen(commandDispatcherStopChannel, false) go eventDispatcher.Listen(eventDispatcherStopChannel, false) log.Println("Dump models") log.Println(readModel) log.Println(usersModel) log.Println("Find an account") readModelAccount := readModel.Accounts[accountID] log.Println(readModelAccount) log.Println("Find a user") user := usersModel.Users[accountID] log.Println(user) hashedPassword, err := GetHashForPassword("$ThisIsMyPassword1") if err != nil { t.Fatal("Error: ", err) } log.Println("Create new account...") createAccountCommand := cqrs.CreateCommand( CreateAccountCommand{"John", "Snow", "*****@*****.**", hashedPassword, 0.0}) commandBus.PublishCommands([]cqrs.Command{createAccountCommand}) log.Println("Dump models") log.Println(readModel) log.Println(usersModel) log.Println("Change Password") changePasswordCommand := cqrs.CreateCommand( ChangePasswordCommand{accountID, "$ThisIsANOTHERPassword"}) commandBus.PublishCommands([]cqrs.Command{changePasswordCommand}) log.Println("Change email address and credit the account") changeEmailAddressCommand := cqrs.CreateCommand( ChangeEmailAddressCommand{accountID, "*****@*****.**"}) creditAccountCommand := cqrs.CreateCommand( CreditAccountCommand{accountID, 50}) creditAccountCommand2 := cqrs.CreateCommand( CreditAccountCommand{accountID, 50}) commandBus.PublishCommands([]cqrs.Command{ changeEmailAddressCommand, creditAccountCommand, creditAccountCommand2}) log.Println("Dump models") log.Println(readModel) log.Println(usersModel) log.Println("Change the email address, credit 150, debit 200") lastEmailAddress := "*****@*****.**" changeEmailAddressCommand = cqrs.CreateCommand( ChangeEmailAddressCommand{accountID, lastEmailAddress}) creditAccountCommand = cqrs.CreateCommand( CreditAccountCommand{accountID, 150}) debitAccountCommand := cqrs.CreateCommand( DebitAccountCommand{accountID, 200}) commandBus.PublishCommands([]cqrs.Command{ changeEmailAddressCommand, creditAccountCommand, debitAccountCommand}) log.Println("Dump models") log.Println(readModel) log.Println(usersModel) time.Sleep(300 * time.Millisecond) log.Println("Dump history - integration events") if history, err := repository.GetEventStreamRepository().AllIntegrationEventsEverPublished(); err != nil { t.Fatal(err) } else { for _, event := range history { log.Println(event) } } log.Println("GetIntegrationEventsByCorrelationID") correlationEvents, err := repository.GetEventStreamRepository().GetIntegrationEventsByCorrelationID(debitAccountCommand.CorrelationID) if err != nil || len(correlationEvents) == 0 { t.Fatal(err) } for correlationEvent := range correlationEvents { log.Println(correlationEvent) } log.Println("Load the account from history") account, error := NewAccountFromHistory(accountID, repository) if error != nil { t.Fatal(error) } // All events should have been replayed and the email address should be the latest log.Println("Dump models") log.Println(account) log.Println(readModel) log.Println(usersModel) if account.EmailAddress != lastEmailAddress { t.Fatal("Expected emailaddress to be ", lastEmailAddress) } if account.Balance != readModel.Accounts[accountID].Balance { t.Fatal("Expected readmodel to be synced with write model") } eventDispatcherStopChannel <- true commandDispatcherStopChannel <- true }