Compare commits
No commits in common. "4a8cbc0d29aa2205ef00d7a437327b559a80efcc" and "1cbda29b3673cf3e278b5054a9d8f60ecd224b63" have entirely different histories.
4a8cbc0d29
...
1cbda29b36
@ -1,50 +0,0 @@
|
|||||||
name: Go CI/CD
|
|
||||||
run-name: ${{ github.actor }} is running CI/CD basic
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches-ignore:
|
|
||||||
- main
|
|
||||||
- release/**
|
|
||||||
- develop
|
|
||||||
pull_request:
|
|
||||||
branches-ignore:
|
|
||||||
- main
|
|
||||||
- release/**
|
|
||||||
- develop
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
go-ci:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '1.24'
|
|
||||||
|
|
||||||
- name: Download dependencies
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
go mod tidy -x
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
go test -json > test-report.out
|
|
||||||
go test -coverprofile=coverage.out
|
|
||||||
|
|
||||||
- name: SonarQube Analysis
|
|
||||||
uses: SonarSource/sonarqube-scan-action@v5
|
|
||||||
env:
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
||||||
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
|
|
||||||
|
|
||||||
- name: Build binary
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
go build ./...
|
|
@ -1,50 +0,0 @@
|
|||||||
name: Go CI/CD
|
|
||||||
run-name: ${{ github.actor }} is running CI/CD protected
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- release/**
|
|
||||||
- develop
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- release/**
|
|
||||||
- develop
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
go-ci:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '1.24'
|
|
||||||
|
|
||||||
- name: Download dependencies
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
go mod tidy -x
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
go test -json > test-report.out
|
|
||||||
go test -coverprofile=coverage.out
|
|
||||||
|
|
||||||
- name: SonarQube Analysis
|
|
||||||
uses: SonarSource/sonarqube-scan-action@v5
|
|
||||||
env:
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
||||||
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
|
|
||||||
|
|
||||||
- name: Build binary
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
go build ./...
|
|
@ -1,43 +0,0 @@
|
|||||||
name: Go CI/CD
|
|
||||||
run-name: ${{ github.actor }} is running CI/CD Tag
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
go-ci:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '1.24'
|
|
||||||
|
|
||||||
- name: Download dependencies
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
go mod tidy -x
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
go test -json > test-report.out
|
|
||||||
go test -coverprofile=coverage.out
|
|
||||||
|
|
||||||
- name: SonarQube Analysis
|
|
||||||
uses: SonarSource/sonarqube-scan-action@v5
|
|
||||||
env:
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
||||||
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
|
|
||||||
|
|
||||||
- name: Build binary
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
go build ./...
|
|
64
.gitignore
vendored
64
.gitignore
vendored
@ -1,64 +0,0 @@
|
|||||||
# Binaries for programs and plugins
|
|
||||||
bin
|
|
||||||
*.exe
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
*.test
|
|
||||||
|
|
||||||
# Output of the 'go tool cover' command
|
|
||||||
*.out
|
|
||||||
coverage.xml
|
|
||||||
test-report.xml
|
|
||||||
|
|
||||||
# Directory for Go modules
|
|
||||||
/vendor/
|
|
||||||
|
|
||||||
# Go workspace file
|
|
||||||
go.work
|
|
||||||
go.work.sum
|
|
||||||
|
|
||||||
# Editor configs
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*.bak
|
|
||||||
*.tmp
|
|
||||||
*.log
|
|
||||||
*.viminfo
|
|
||||||
*.un~
|
|
||||||
Session.vim
|
|
||||||
|
|
||||||
# JetBrains Rider specific
|
|
||||||
.idea/
|
|
||||||
*.iml
|
|
||||||
.idea/**/workspace.xml
|
|
||||||
.idea/**/tasks.xml
|
|
||||||
.idea/**/shelf/
|
|
||||||
|
|
||||||
# Sublime Text specific
|
|
||||||
*.sublime-workspace
|
|
||||||
*.sublime-project
|
|
||||||
|
|
||||||
# VSCode specific
|
|
||||||
.vscode/
|
|
||||||
.vscode/settings.json
|
|
||||||
.vscode/tasks.json
|
|
||||||
.vscode/launch.json
|
|
||||||
|
|
||||||
# Emacs specific
|
|
||||||
*~
|
|
||||||
\#*\#
|
|
||||||
.#*
|
|
||||||
|
|
||||||
# MacOS specific
|
|
||||||
.DS_Store
|
|
||||||
.AppleDouble
|
|
||||||
.LSOverride
|
|
||||||
|
|
||||||
# Node modules (in case of tools/scripts)
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Python virtual environments (for dev tools/scripts)
|
|
||||||
venv/
|
|
||||||
*.pyc
|
|
||||||
__pycache__/
|
|
86
README.md
86
README.md
@ -1,85 +1,3 @@
|
|||||||
# 🔥 StoneCQRS - **10 billion% decoupled architecture!**
|
# stonecqrs
|
||||||
|
|
||||||
Ultimate CQRS/Event toolkit! ⚡ Command/Query separation with event-driven superpowers. Perfect for complex domains & microservices. Saga-ready architecture baked in. Part of stone-utils ecosystem. 10 billion% more decoupled! 🚀
|
StoneCQRS - Ultimate CQRS/Event toolkit! ⚡ Command/Query separation with event-driven superpowers. Perfect for complex domains & microservices. Saga-ready architecture baked in. Part of stone-utils ecosystem. 10 billion% more decoupled! 🚀
|
||||||
|
|
||||||
[]()
|
|
||||||
[]()
|
|
||||||
[]()
|
|
||||||
|
|
||||||
## 🚀 Why StoneCQRS?
|
|
||||||
|
|
||||||
- **CQRS/Event-Sourcing** with Senku-level precision
|
|
||||||
- **Saga-ready** architecture out of the box
|
|
||||||
- **100% decoupled** components (like good science should be)
|
|
||||||
- **10 billion events/sec** handling capacity (theoretical)
|
|
||||||
|
|
||||||
## 💥 Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get gitstormr.dev/stone-utils/stonecqrs@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
## ⚡ Basic Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gitstormr.dev/stone-utils/stonecqrs"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CreateOrderCommand struct{ UserID string }
|
|
||||||
type OrderCreatedEvent struct{ OrderID string }
|
|
||||||
|
|
||||||
type OrderHandler struct{}
|
|
||||||
|
|
||||||
func (h *OrderHandler) Handle(ctx context.Context, cmd stonecqrs.Command) ([]stonecqrs.Event, error) {
|
|
||||||
return []stonecqrs.Event{OrderCreatedEvent{OrderID: "123"}}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
dispatcher := stonecqrs.NewDispatcher()
|
|
||||||
dispatcher.RegisterCommandHandler(CreateOrderCommand{}, &OrderHandler{})
|
|
||||||
events, _ := dispatcher.DispatchCommand(ctx, CreateOrderCommand{UserID: "u-456"})
|
|
||||||
// events = [OrderCreatedEvent]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔬 Core Patterns
|
|
||||||
|
|
||||||
### Command -> Event -> Reaction
|
|
||||||
|
|
||||||
```text
|
|
||||||
[CreateOrderCommand]
|
|
||||||
↓
|
|
||||||
[OrderCreatedEvent] → [PaymentHandler] → [InventoryHandler]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Saga ready flow
|
|
||||||
|
|
||||||
```go
|
|
||||||
// 1. Execute command
|
|
||||||
events, _ := dispatcher.DispatchCommand(ctx, cmd)
|
|
||||||
|
|
||||||
// 2. Automatic event processing
|
|
||||||
// (Handlers trigger subsequent commands)
|
|
||||||
|
|
||||||
// 3. Compensation on failure
|
|
||||||
// (Built-in rollback capabilities)
|
|
||||||
```
|
|
||||||
|
|
||||||
## ⚗️ Scientific Benchmarks
|
|
||||||
|
|
||||||
| METRIC | STANDARD LIB | STONECQRS |
|
|
||||||
|---------------|---------------|-----------|
|
|
||||||
| Decoupling | 30% | 10B% |
|
|
||||||
| Scalability | 1x | ∞x |
|
|
||||||
| Debuggability | ❌ | 🔍💡 |
|
|
||||||
|
|
||||||
**Join the Scientific Revolution!**
|
|
||||||
|
|
||||||
> "This isn't just CQRS - it's revolutionizing software patterns like we revived civilization!" - Senku Ishigami
|
|
||||||
|
|
||||||
Kingdom of Science Approved
|
|
||||||
|
|
||||||
(Now with 100% more Chrome screaming "SO BADASS!")
|
|
206
dispatcher.go
206
dispatcher.go
@ -1,206 +0,0 @@
|
|||||||
package stonecqrs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"gitstormr.dev/stone-utils/stoneerror"
|
|
||||||
)
|
|
||||||
|
|
||||||
// errCommandHandlerNotFound indicates a missing command handler error.
|
|
||||||
// errQueryHandlerNotFound indicates a missing query handler error.
|
|
||||||
// errEventHandlerNotFound indicates a missing event handler error.
|
|
||||||
var (
|
|
||||||
errCommandHandlerNotFound = stoneerror.New(
|
|
||||||
1001, "command handler not found",
|
|
||||||
)
|
|
||||||
errQueryHandlerNotFound = stoneerror.New(1002, "query handler not found")
|
|
||||||
errEventHandlerNotFound = stoneerror.New(1003, "event handler not found")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Command represents an action or operation to be handled by a dispatcher.
|
|
||||||
type Command interface{}
|
|
||||||
|
|
||||||
// Query represents a marker interface used to identify query types.
|
|
||||||
type Query interface{}
|
|
||||||
|
|
||||||
// Event represents a domain event within the application architecture.
|
|
||||||
type Event interface{}
|
|
||||||
|
|
||||||
// CommandHandler defines an interface for handling commands in a CQRS pattern.
|
|
||||||
// Implementations should process commands and return resulting events or errors.
|
|
||||||
// Handle processes the given command and returns any resulting events or error.
|
|
||||||
type CommandHandler interface {
|
|
||||||
Handle(ctx context.Context, cmd Command) ([]Event, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryHandler defines an interface for handling queries within a context.
|
|
||||||
type QueryHandler interface {
|
|
||||||
Handle(ctx context.Context, query Query) (interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EventHandler is an interface for handling specific types of events.
|
|
||||||
// It defines a single method Handle to process an Event with a context.
|
|
||||||
// Handle returns an error if the event handling fails.
|
|
||||||
type EventHandler interface {
|
|
||||||
Handle(ctx context.Context, event Event) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dispatcher is responsible for managing and dispatching handlers.
|
|
||||||
// It supports command, query, and event handlers concurrently.
|
|
||||||
// Commands, queries, and events are identified by their type names.
|
|
||||||
// Uses a read-write mutex to ensure thread-safe operations.
|
|
||||||
type Dispatcher struct {
|
|
||||||
commandHandlers map[string]CommandHandler
|
|
||||||
queryHandlers map[string]QueryHandler
|
|
||||||
eventHandlers map[string][]EventHandler
|
|
||||||
mutex sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDispatcher creates and returns a new instance of Dispatcher.
|
|
||||||
func NewDispatcher() *Dispatcher {
|
|
||||||
return &Dispatcher{
|
|
||||||
commandHandlers: make(map[string]CommandHandler),
|
|
||||||
queryHandlers: make(map[string]QueryHandler),
|
|
||||||
eventHandlers: make(map[string][]EventHandler),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterCommandHandler registers a handler for a specific command type.
|
|
||||||
func (d *Dispatcher) RegisterCommandHandler(
|
|
||||||
cmd Command, handler CommandHandler,
|
|
||||||
) {
|
|
||||||
d.mutex.Lock()
|
|
||||||
defer d.mutex.Unlock()
|
|
||||||
d.commandHandlers[typeName(cmd)] = handler
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterQueryHandler registers a handler for the specified query type.
|
|
||||||
func (d *Dispatcher) RegisterQueryHandler(query Query, handler QueryHandler) {
|
|
||||||
d.mutex.Lock()
|
|
||||||
defer d.mutex.Unlock()
|
|
||||||
d.queryHandlers[typeName(query)] = handler
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterEventHandler registers a handler for a specific type of event.
|
|
||||||
func (d *Dispatcher) RegisterEventHandler(event Event, handler EventHandler) {
|
|
||||||
d.mutex.Lock()
|
|
||||||
defer d.mutex.Unlock()
|
|
||||||
name := typeName(event)
|
|
||||||
d.eventHandlers[name] = append(d.eventHandlers[name], handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DispatchCommand processes a Command and dispatches the resulting Events.
|
|
||||||
// Returns the generated Events or an error if command handling fails.
|
|
||||||
func (d *Dispatcher) DispatchCommand(ctx context.Context, cmd Command) (
|
|
||||||
[]Event, error,
|
|
||||||
) {
|
|
||||||
handler, err := d.getCommandHandler(cmd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
events, err := handler.Handle(ctx, cmd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(events) > 0 {
|
|
||||||
if err = d.DispatchEvents(ctx, events...); err != nil {
|
|
||||||
return events, stoneerror.Wrap(
|
|
||||||
err, 1004, "failed to dispatch generated events",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DispatchQuery dispatches a query to its corresponding handler.
|
|
||||||
// It retrieves the query handler and invokes its Handle method.
|
|
||||||
// Returns the result of the query handler or an error if not found.
|
|
||||||
func (d *Dispatcher) DispatchQuery(
|
|
||||||
ctx context.Context, query Query,
|
|
||||||
) (interface{}, error) {
|
|
||||||
handler, err := d.getQueryHandler(query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return handler.Handle(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DispatchEvents dispatches multiple events to their registered handlers.
|
|
||||||
// It processes each event sequentially and stops on the first error.
|
|
||||||
// Returns an error if any event fails to dispatch.
|
|
||||||
func (d *Dispatcher) DispatchEvents(
|
|
||||||
ctx context.Context, events ...Event,
|
|
||||||
) error {
|
|
||||||
for _, event := range events {
|
|
||||||
if err := d.DispatchEvent(ctx, event); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DispatchEvent dispatches an event to all registered event handlers.
|
|
||||||
// It returns an error if any handler fails to process the event.
|
|
||||||
func (d *Dispatcher) DispatchEvent(ctx context.Context, event Event) error {
|
|
||||||
handlers, err := d.getEventHandlers(event)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, handler := range handlers {
|
|
||||||
if err = handler.Handle(ctx, event); err != nil {
|
|
||||||
return stoneerror.Wrap(err, 1005, "event handling failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCommandHandler retrieves the CommandHandler for the given Command.
|
|
||||||
// Returns an error if no handler is found.
|
|
||||||
func (d *Dispatcher) getCommandHandler(cmd Command) (CommandHandler, error) {
|
|
||||||
d.mutex.RLock()
|
|
||||||
defer d.mutex.RUnlock()
|
|
||||||
|
|
||||||
handler, exists := d.commandHandlers[typeName(cmd)]
|
|
||||||
if !exists {
|
|
||||||
return nil, errCommandHandlerNotFound
|
|
||||||
}
|
|
||||||
return handler, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getQueryHandler retrieves the handler for a specific query type.
|
|
||||||
// It returns an error if the handler is not found.
|
|
||||||
func (d *Dispatcher) getQueryHandler(query Query) (QueryHandler, error) {
|
|
||||||
d.mutex.RLock()
|
|
||||||
defer d.mutex.RUnlock()
|
|
||||||
|
|
||||||
handler, exists := d.queryHandlers[typeName(query)]
|
|
||||||
if !exists {
|
|
||||||
return nil, errQueryHandlerNotFound
|
|
||||||
}
|
|
||||||
return handler, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getEventHandlers retrieves all handlers for a specific event type.
|
|
||||||
// Returns an error if no handlers are registered for the event type.
|
|
||||||
func (d *Dispatcher) getEventHandlers(event Event) ([]EventHandler, error) {
|
|
||||||
d.mutex.RLock()
|
|
||||||
defer d.mutex.RUnlock()
|
|
||||||
|
|
||||||
handlers, exists := d.eventHandlers[typeName(event)]
|
|
||||||
if !exists {
|
|
||||||
return nil, errEventHandlerNotFound
|
|
||||||
}
|
|
||||||
return handlers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeName returns the name of the type of the given interface value.
|
|
||||||
func typeName(v interface{}) string {
|
|
||||||
return fmt.Sprintf("%T", v)
|
|
||||||
}
|
|
@ -1,170 +0,0 @@
|
|||||||
package stonecqrs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testCommand struct {
|
|
||||||
message string
|
|
||||||
}
|
|
||||||
|
|
||||||
type testQuery struct {
|
|
||||||
message string
|
|
||||||
}
|
|
||||||
|
|
||||||
type testEvent struct {
|
|
||||||
message string
|
|
||||||
}
|
|
||||||
|
|
||||||
type unknownEvent struct{}
|
|
||||||
|
|
||||||
type testCommandHandler struct{}
|
|
||||||
|
|
||||||
func (handler *testCommandHandler) Handle(
|
|
||||||
ctx context.Context, cmd Command,
|
|
||||||
) ([]Event, error) {
|
|
||||||
fmt.Println(cmd)
|
|
||||||
return []Event{
|
|
||||||
testEvent{message: "test"},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type testQueryHandler struct{}
|
|
||||||
|
|
||||||
func (handler *testQueryHandler) Handle(
|
|
||||||
ctx context.Context, query Query,
|
|
||||||
) (interface{}, error) {
|
|
||||||
fmt.Println(query)
|
|
||||||
return "test", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type testEventHandler struct{}
|
|
||||||
|
|
||||||
func (handler *testEventHandler) Handle(
|
|
||||||
ctx context.Context, event Event,
|
|
||||||
) error {
|
|
||||||
fmt.Println(event)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type testErrorHandler struct{}
|
|
||||||
|
|
||||||
func (handler *testErrorHandler) Handle(
|
|
||||||
ctx context.Context, cmd Command,
|
|
||||||
) ([]Event, error) {
|
|
||||||
return nil, errors.New("test error")
|
|
||||||
}
|
|
||||||
|
|
||||||
type testErrorQueryHandler struct{}
|
|
||||||
|
|
||||||
func (handler *testErrorQueryHandler) Handle(
|
|
||||||
ctx context.Context, query Query,
|
|
||||||
) (interface{}, error) {
|
|
||||||
return nil, errors.New("test error")
|
|
||||||
}
|
|
||||||
|
|
||||||
type testErrorEventHandler struct{}
|
|
||||||
|
|
||||||
func (handler *testErrorEventHandler) Handle(
|
|
||||||
ctx context.Context, event Event,
|
|
||||||
) error {
|
|
||||||
return errors.New("test error")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_NewDispatcher(t *testing.T) {
|
|
||||||
d := NewDispatcher()
|
|
||||||
if d == nil {
|
|
||||||
t.Fatal("expected non-nil dispatcher")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Register(t *testing.T) {
|
|
||||||
d := NewDispatcher()
|
|
||||||
d.RegisterCommandHandler(testCommand{}, &testCommandHandler{})
|
|
||||||
d.RegisterQueryHandler(testQuery{}, &testQueryHandler{})
|
|
||||||
d.RegisterEventHandler(testEvent{}, &testEventHandler{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_DispatchCommandWithEvents(t *testing.T) {
|
|
||||||
d := NewDispatcher()
|
|
||||||
d.RegisterCommandHandler(testCommand{}, &testCommandHandler{})
|
|
||||||
d.RegisterQueryHandler(testQuery{}, &testQueryHandler{})
|
|
||||||
d.RegisterEventHandler(testEvent{}, &testEventHandler{})
|
|
||||||
|
|
||||||
_, err := d.DispatchCommand(context.Background(), testCommand{})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_DispatchQuery(t *testing.T) {
|
|
||||||
d := NewDispatcher()
|
|
||||||
d.RegisterQueryHandler(testQuery{}, &testQueryHandler{})
|
|
||||||
|
|
||||||
_, err := d.DispatchQuery(context.Background(), testQuery{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_UnknownEvent(t *testing.T) {
|
|
||||||
d := NewDispatcher()
|
|
||||||
d.RegisterCommandHandler(testCommand{}, &testCommandHandler{})
|
|
||||||
d.RegisterQueryHandler(testQuery{}, &testQueryHandler{})
|
|
||||||
d.RegisterEventHandler(testEvent{}, &testEventHandler{})
|
|
||||||
|
|
||||||
_, err := d.DispatchCommand(context.Background(), unknownEvent{})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = d.DispatchQuery(context.Background(), unknownEvent{})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = d.DispatchEvent(context.Background(), unknownEvent{})
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_DispatchEventWithError(t *testing.T) {
|
|
||||||
d := NewDispatcher()
|
|
||||||
d.RegisterCommandHandler(testCommand{}, &testCommandHandler{})
|
|
||||||
d.RegisterEventHandler(testEvent{}, &testErrorEventHandler{})
|
|
||||||
|
|
||||||
err := d.DispatchEvent(context.Background(), testEvent{})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Error(t *testing.T) {
|
|
||||||
d := NewDispatcher()
|
|
||||||
d.RegisterCommandHandler(testCommand{}, &testErrorHandler{})
|
|
||||||
d.RegisterQueryHandler(testQuery{}, &testErrorQueryHandler{})
|
|
||||||
d.RegisterEventHandler(testEvent{}, &testErrorEventHandler{})
|
|
||||||
|
|
||||||
_, err := d.DispatchCommand(context.Background(), testCommand{})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = d.DispatchQuery(context.Background(), testQuery{})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = d.DispatchEvent(context.Background(), testEvent{})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
}
|
|
5
go.mod
5
go.mod
@ -1,5 +0,0 @@
|
|||||||
module gitstormr.dev/stone-utils/stonecqrs
|
|
||||||
|
|
||||||
go 1.24
|
|
||||||
|
|
||||||
require gitstormr.dev/stone-utils/stoneerror v1.0.0
|
|
2
go.sum
2
go.sum
@ -1,2 +0,0 @@
|
|||||||
gitstormr.dev/stone-utils/stoneerror v1.0.0 h1:EJpn4MZBeYifWlCoQBEGmGdEtNABjOrUzJmQSqcXqY0=
|
|
||||||
gitstormr.dev/stone-utils/stoneerror v1.0.0/go.mod h1:Rs34Oz14ILsbkZ++Ov9PObTz7mRvyyvcCcML9AeyIyk=
|
|
@ -1,10 +0,0 @@
|
|||||||
sonar.projectKey=f33dd35c-308c-4f08-ace2-6449efc238e9
|
|
||||||
sonar.projectName=stone-utils/stonecqrs
|
|
||||||
sonar.language=go
|
|
||||||
sonar.sources=.
|
|
||||||
sonar.exclusions=**/*_test.go
|
|
||||||
sonar.tests=.
|
|
||||||
sonar.test.inclusions=**/*_test.go
|
|
||||||
sonar.go.tests.reportPaths=test-report.out
|
|
||||||
sonar.go.coverage.reportPaths=coverage.out
|
|
||||||
sonar.qualitygate.wait=true
|
|
Loading…
x
Reference in New Issue
Block a user