Reviewed-on: #2 Reviewed-by: Cloud Administrator <cloud-admin@noreply.gitstormr.dev> Co-authored-by: Rene Nochebuena <code-raider@noreply.gitstormr.dev> Co-committed-by: Rene Nochebuena <code-raider@noreply.gitstormr.dev>
This commit is contained in:
parent
1cbda29b36
commit
068ea278fa
50
.gitea/workflows/ci-basic.yml
Normal file
50
.gitea/workflows/ci-basic.yml
Normal file
@ -0,0 +1,50 @@
|
||||
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 ./...
|
50
.gitea/workflows/ci-protected.yml
Normal file
50
.gitea/workflows/ci-protected.yml
Normal file
@ -0,0 +1,50 @@
|
||||
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 ./...
|
43
.gitea/workflows/ci-tag.yml
Normal file
43
.gitea/workflows/ci-tag.yml
Normal file
@ -0,0 +1,43 @@
|
||||
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
Normal file
64
.gitignore
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
# 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,3 +1,85 @@
|
||||
# stonecqrs
|
||||
# 🔥 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! 🚀
|
||||
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
Normal file
206
dispatcher.go
Normal file
@ -0,0 +1,206 @@
|
||||
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)
|
||||
}
|
170
dispatcher_test.go
Normal file
170
dispatcher_test.go
Normal file
@ -0,0 +1,170 @@
|
||||
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
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module gitstormr.dev/stone-utils/stonecqrs
|
||||
|
||||
go 1.24
|
||||
|
||||
require gitstormr.dev/stone-utils/stoneerror v1.0.0
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
gitstormr.dev/stone-utils/stoneerror v1.0.0 h1:EJpn4MZBeYifWlCoQBEGmGdEtNABjOrUzJmQSqcXqY0=
|
||||
gitstormr.dev/stone-utils/stoneerror v1.0.0/go.mod h1:Rs34Oz14ILsbkZ++Ov9PObTz7mRvyyvcCcML9AeyIyk=
|
10
sonar-project.properties
Normal file
10
sonar-project.properties
Normal file
@ -0,0 +1,10 @@
|
||||
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