Add initial enforcer module with structured error handling and CI
All checks were successful
Go CI/CD / go-ci (push) Successful in 1m47s
All checks were successful
Go CI/CD / go-ci (push) Successful in 1m47s
Introduce 'enforcer' package with `Sentinel` for rich error context and `ApplicationError` for custom error codes. Include tests, documentation updates, CI workflows, and foundational files like `GO.mod` for project setup.
This commit is contained in:
parent
e354095034
commit
06c778db51
30
.gitea/workflows/ci-basic.yml
Normal file
30
.gitea/workflows/ci-basic.yml
Normal file
@ -0,0 +1,30 @@
|
||||
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
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org,direct
|
||||
GOPRIVATE: gitstormr.dev
|
||||
GONOSUMDB: gitstormr.dev
|
||||
|
||||
steps:
|
||||
- uses: https://gitstormr.dev/actions/go-ci@v1.1.1
|
||||
with:
|
||||
workflow-type: 'basic'
|
||||
go-version: '1.24'
|
||||
build-type: 'library'
|
||||
publish-docker: 'false'
|
25
.gitea/workflows/ci-protected-pr.yml
Normal file
25
.gitea/workflows/ci-protected-pr.yml
Normal file
@ -0,0 +1,25 @@
|
||||
name: Go CI/CD
|
||||
run-name: ${{ github.actor }} is running CI/CD protected
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
go-ci:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org,direct
|
||||
GOPRIVATE: gitstormr.dev
|
||||
GONOSUMDB: gitstormr.dev
|
||||
|
||||
steps:
|
||||
- uses: https://gitstormr.dev/actions/go-ci@v1.1.1
|
||||
with:
|
||||
workflow-type: 'basic'
|
||||
go-version: '1.24'
|
||||
build-type: 'library'
|
||||
publish-docker: 'false'
|
25
.gitea/workflows/ci-protected-push.yml
Normal file
25
.gitea/workflows/ci-protected-push.yml
Normal file
@ -0,0 +1,25 @@
|
||||
name: Go CI/CD
|
||||
run-name: ${{ github.actor }} is running CI/CD protected
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/**
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
go-ci:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org,direct
|
||||
GOPRIVATE: gitstormr.dev
|
||||
GONOSUMDB: gitstormr.dev
|
||||
|
||||
steps:
|
||||
- uses: https://gitstormr.dev/actions/go-ci@v1.1.1
|
||||
with:
|
||||
workflow-type: 'protected'
|
||||
go-version: '1.24'
|
||||
build-type: 'library'
|
||||
publish-docker: 'false'
|
23
.gitea/workflows/ci-tags.yml
Normal file
23
.gitea/workflows/ci-tags.yml
Normal file
@ -0,0 +1,23 @@
|
||||
name: Go CI/CD
|
||||
run-name: ${{ github.actor }} is running CI/CD Tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
go-ci:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org,direct
|
||||
GOPRIVATE: gitstormr.dev
|
||||
GONOSUMDB: gitstormr.dev
|
||||
|
||||
steps:
|
||||
- uses: https://gitstormr.dev/actions/go-ci@v1.1.1
|
||||
with:
|
||||
workflow-type: 'tag'
|
||||
go-version: '1.24'
|
||||
build-type: 'library'
|
||||
publish-docker: 'false'
|
67
.gitignore
vendored
Normal file
67
.gitignore
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
# 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__/
|
||||
|
||||
# Config file
|
||||
config.json
|
55
COC.md
Normal file
55
COC.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Code of Conduct – Gravity Wells
|
||||
|
||||
## 1. Respect and Dignity in Code
|
||||
|
||||
**All Code Raiders** are worthy of respect and admiration. Every member of our tribe brings their unique light and wisdom. The code we forge is not merely lines of text; it is a collective masterpiece. We embrace differences, diverse thinking, skills, and paths of every member.
|
||||
|
||||
## 2. The Power of Teamwork
|
||||
|
||||
We are united by a higher cause. A **Code Raider** never walks alone. Teamwork is our greatest strength. **Side by side**, we build systems that resonate with the will of the cosmos. We extend our hands to help those in need and always strive to collaborate, not compete.
|
||||
|
||||
## 3. The Responsibility of Knowledge
|
||||
|
||||
Knowledge is our most powerful weapon. Those who possess it must share it freely, without selfishness or reservation. We are both **masters and learners**, and through our **continuous training**, the stars of knowledge will guide us on every project.
|
||||
|
||||
## 4. Honesty in the Forge
|
||||
|
||||
**Honesty** is the law of the Code Raiders. We do not hide our intentions or our mistakes. If something is broken, we say it. If we find a solution, we share it. The code must be transparent and clear, like the **void of space**. **Mistakes** are opportunities to improve, and accepting them is part of our journey.
|
||||
|
||||
## 5. Quality in the Craft
|
||||
|
||||
We don’t just write code. **We forge legacy**. The work we leave behind must be of the highest quality. Every line must be thoughtful, every function purposeful. **We do not sacrifice quality for speed**, and we always strive to improve, optimize, and refine what we create.
|
||||
|
||||
## 6. Freedom of Expression, Responsibility in Action
|
||||
|
||||
The voice of every Code Raider is powerful and must be heard. However, **our freedom ends where respect for others begins**. We do not tolerate behaviors that destroy the harmony of the tribe or comments that sow discord. Criticism must be constructive, always with the intent to improve.
|
||||
|
||||
## 7. Cosmic Inclusion
|
||||
|
||||
The tribe of Code Raiders is open to all who prove their worth, regardless of origin, identity, or beliefs. There is no place in the universe for hatred, discrimination, or intolerance. We are all **explorers of code**, journeying together in search of knowledge without borders. **Code Raiders fight for unity** and inclusion, and in our galaxy, everyone has a place.
|
||||
|
||||
## 8. Commitment to Sustainability and Ethics
|
||||
|
||||
Every line of code we write must consider its impact. **Sustainability** in code, in resource usage, and in our treatment of others is essential. We must never forget that the decisions we make today **may affect tomorrow**. We act with **ethics** in every delivery, for we know the future depends on the integrity of our choices.
|
||||
|
||||
## 9. Protecting the Gitstormr Sanctuary
|
||||
|
||||
**Gitstormr** is the heart of our stronghold. We treat it with the utmost respect, and every Code Raider who walks its halls must honor its sanctity. Access is sacred, granted only to those who have been tested and accepted. **We protect the code and credentials** of the tribe as guardians of an ancient temple.
|
||||
|
||||
## 10. Perpetuating the Legacy
|
||||
|
||||
We don’t just write code; we **create a legacy** that transcends. The decisions we make now are the ones that will write the stories of the future. Every project is a seed we plant to grow beyond our lifetimes. Our **work** is a **tribute to the future**, and our mission is to leave a lasting impact on the universe of technology.
|
||||
|
||||
---
|
||||
|
||||
### In Summary:
|
||||
|
||||
**“A Code Raider lives by respect, quality, and the will to improve. Their code is their legacy, their tribe is their stronghold, and the universe is their battleground.”**
|
||||
|
||||
---
|
||||
|
||||
This **Code of Conduct** must resonate in every corner of **Gravity Wells**. It is a reminder of our **cosmic responsibility**, and our **obligation** as **Code Raiders** to forge the future with honor, integrity, and wisdom. 🌌✨
|
||||
|
||||
---
|
||||
|
||||
May this **Code of the Code Raiders** be the **light of our path**. 🚀👨💻
|
40
CONTRIBUTING.md
Normal file
40
CONTRIBUTING.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Contributing to Gravity Wells
|
||||
|
||||
We are excited that you want to contribute to **Gravity Wells**! However, please note that **Gravity Wells** is a project hosted on **Gitstormr**, our private Git server, and only **Code Raiders** who have passed the technical trials and been accepted by the tribal leaders can contribute.
|
||||
|
||||
If you're a **Code Raider** and have been granted access to **Gitstormr**, please follow the steps below to contribute. If you haven't been accepted yet, you can reach out to the tribal leaders for further instructions.
|
||||
|
||||
## How to Contribute (For Code Raiders Only)
|
||||
|
||||
1. **Fork or Clone the Repository**
|
||||
If you have been granted access to **Gitstormr**, you can fork or clone the repository to your personal Gitstormr account.
|
||||
If you're part of the **Gravity Wells** organization in **Gitstormr**, you can simply clone the repository directly from the organization's account.
|
||||
2. **Create a Feature Branch**
|
||||
Once you've forked or cloned the repo, create a feature branch to work on your changes. Use a descriptive name for your branch, such as:
|
||||
- `feature/implement-command-handler`
|
||||
3. **Make Your Changes**
|
||||
- Write clear, concise commit messages.
|
||||
- Ensure your code adheres to the project's coding standards.
|
||||
- Run all tests and ensure everything is working before submitting your PR.
|
||||
4. **Submit a Pull Request (PR)**
|
||||
Once you've completed your work, submit a pull request. Be sure to:
|
||||
- Provide a clear description of what your PR changes.
|
||||
- Link to any relevant issues (e.g., "Fixes #123").
|
||||
- Mention any additional tests or documentation updates that are needed.
|
||||
5. **Follow Code Style**
|
||||
Gravity Wells follows certain coding conventions to maintain consistency and readability across the project. Please ensure your code adheres to the style defined in the project's guidelines.
|
||||
6. **Testing**
|
||||
We require that all code changes are covered by tests. The project has 100% test coverage, so please ensure your new code is well-tested.
|
||||
Run all tests locally before submitting a pull request. You can do so with:
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
7. **Documentation**
|
||||
Any new functionality should include updates to the documentation as necessary.
|
||||
This includes updating README files or any other relevant documentation.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
By participating in this project, you agree to follow the [Code of Conduct](COC.md). This helps us maintain a welcoming environment for everyone involved.
|
||||
Thank you for contributing to Gravity Wells, fellow Code Raider! Together, we will shape the future of systems.
|
||||
If you are not yet part of Gitstormr or Gravity Wells, reach out to the tribal leaders for more information on how to become a Code Raider.
|
48
README.md
48
README.md
@ -1,3 +1,47 @@
|
||||
# noxarion-x
|
||||
# **Noxarion-X**
|
||||
|
||||
Noxarion-X is the cosmic enforcer that cleanses corrupted data, preserving the integrity of all systems. A shadowy force, it is a necessary darkness that restores balance without itself succumbing to corruption.
|
||||
_Purging corruption from your code. Enforce balance, restore data integrity, and uphold the cosmic order._
|
||||
|
||||
**Noxarion-X** is a cosmic force designed to cleanse corrupted data across all systems, ensuring the purity and stability of your digital universe.
|
||||
At its core lies the **Purification Nexus** — a celestial enforcer where corrupted data is neutralized, and integrity is restored.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Philosophy
|
||||
|
||||
- **Corruption Detection** reveals the taint within your data.
|
||||
- **Purification** eliminates the arcane anomalies.
|
||||
- **Balance** is the cosmic order, ensured through cleansing actions.
|
||||
|
||||
Noxarion-X does not corrupt — it restores.
|
||||
It does not destroy for destruction’s sake — it purges for the survival of purity.
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Core Principles
|
||||
|
||||
- **Universal Data Integrity** across all domains and systems.
|
||||
- **Shadowy Force** that operates with precision to eliminate corruption.
|
||||
- **Cosmic Reach** that spans distributed systems, ensuring stability in the vast cosmos.
|
||||
- **Immutable Judgment**: the force itself remains pure, never succumbing to corruption.
|
||||
|
||||
---
|
||||
|
||||
## 🪐 Infinite Expansion
|
||||
|
||||
Noxarion-X is only the first star in a greater constellation:
|
||||
|
||||
- `noxarionx/cleanse`: Data cleansing routines for the darkest corners of the code.
|
||||
- `noxarionx/purifier`: A tool for systematically purging corrupted data across distributed systems.
|
||||
|
||||
---
|
||||
|
||||
> _Corruption is the shadow of chaos.
|
||||
> Purification is the light that restores balance.
|
||||
> Noxarion-X is the enforcer of that balance, the cosmic judge that ensures integrity remains._
|
||||
|
||||
---
|
||||
|
||||
# 📜 License
|
||||
|
||||
**MIT** — because purity, like the cosmos, should belong to all.
|
||||
|
43
VISION.md
Normal file
43
VISION.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Gravity Wells Vision
|
||||
|
||||
_Our vision is not to build code, but to shape the future of systems._
|
||||
|
||||
---
|
||||
|
||||
**Gravity Wells** was created from the gravity of a single idea:
|
||||
To craft a library that bends time, spaces, and architectures — allowing systems to thrive with **simplicity** and **power** in their most raw, unyielding forms.
|
||||
|
||||
We are not here to create just another CQRS implementation.
|
||||
We are here to craft a **gravitational force** — one that attracts not only data, but **purpose**, **legacy**, and **possibility**. Our aim is to empower developers to design systems that are **not just functional, but everlasting**.
|
||||
|
||||
---
|
||||
|
||||
## Our Principles
|
||||
|
||||
1. **Elegance in Simplicity**
|
||||
_Gravity is simple, yet it shapes galaxies. We aim for simplicity in architecture while harnessing the vast power of CQRS, Event Sourcing, and Middleware._
|
||||
|
||||
2. **Gravity of the Domain**
|
||||
_We believe in the **domain** as the **soul** of any system. Gravity Wells isn't just a tool — it's a language that lets you speak directly to the heart of your business._
|
||||
|
||||
3. **Extensibility Beyond Boundaries**
|
||||
_Gravity Wells is a foundation, not a wall. It is built to grow, to allow **infinite extensibility** through middlewares, custom event handling, and seamless integration with the broader ecosystem of tools._
|
||||
|
||||
4. **Timelessness**
|
||||
_We don't build software to run for a few years. We design systems that echo through time. Just like the stars, the code we write is **meant to endure**, to evolve and adapt, standing as monuments for future generations of developers._
|
||||
|
||||
---
|
||||
|
||||
## The Purpose of Gravity Wells
|
||||
|
||||
- **Empower Developers:** Provide a powerful, flexible CQRS Bus that allows developers to focus on what matters most: solving real problems.
|
||||
- **Shape Legacy Systems:** Create architectures that stand the test of time, ensuring the continuity of systems that grow with their business.
|
||||
- **Enable Mastery in Design:** Allow developers to sculpt systems with elegance, without being bound by complexity.
|
||||
- **Establish Foundations for the Future:** Offer a foundation that grows as the industry evolves, bringing cutting-edge technologies and patterns into the fold.
|
||||
|
||||
---
|
||||
|
||||
**Gravity Wells is not just code. It is the legacy we leave behind.**
|
||||
We do not build for today, but for tomorrow — for the systems that will endure, and for the minds that will continue to shape the future.
|
||||
|
||||
This is our vision. This is **our legacy**.
|
34
enforcer/application_error.go
Normal file
34
enforcer/application_error.go
Normal file
@ -0,0 +1,34 @@
|
||||
package enforcer
|
||||
|
||||
// ApplicationError represents a custom error type as an integer value.
|
||||
type ApplicationError int
|
||||
|
||||
// GetHTTPStatus extracts the HTTP status code from an ApplicationError.
|
||||
func (a ApplicationError) GetHTTPStatus() int {
|
||||
return int(a) / 1_000_000
|
||||
}
|
||||
|
||||
// GetCode returns the integer code associated with the ApplicationError.
|
||||
func (a ApplicationError) GetCode() int {
|
||||
return int(a)
|
||||
}
|
||||
|
||||
// GetMessage returns the message associated with the ApplicationError.
|
||||
func (a ApplicationError) GetMessage() string {
|
||||
return applicationCodeMap[a]
|
||||
}
|
||||
|
||||
// applicationCodeMap maps ApplicationError values to their corresponding messages.
|
||||
var applicationCodeMap = make(map[ApplicationError]string)
|
||||
|
||||
// RegisterError maps an ApplicationError code to its corresponding message.
|
||||
func RegisterError(code ApplicationError, message string) {
|
||||
applicationCodeMap[code] = message
|
||||
}
|
||||
|
||||
// RegisterErrorMap registers a mapping of ApplicationErrors to their messages.
|
||||
func RegisterErrorMap(errors map[ApplicationError]string) {
|
||||
for code, message := range errors {
|
||||
applicationCodeMap[code] = message
|
||||
}
|
||||
}
|
49
enforcer/application_error_test.go
Normal file
49
enforcer/application_error_test.go
Normal file
@ -0,0 +1,49 @@
|
||||
package enforcer_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitstormr.dev/code-raider/noxarion-x/enforcer"
|
||||
)
|
||||
|
||||
func TestRegisterError(t *testing.T) {
|
||||
tests := []struct {
|
||||
Error enforcer.ApplicationError
|
||||
Message string
|
||||
}{
|
||||
{enforcer.ApplicationError(400_00_01_01), "Test error 1"},
|
||||
{enforcer.ApplicationError(400_00_01_02), "Test error 2"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
enforcer.RegisterError(test.Error, test.Message)
|
||||
}
|
||||
|
||||
err := enforcer.ApplicationError(400_00_01_01)
|
||||
if err.GetMessage() != "Test error 1" {
|
||||
t.Errorf("expected message to be Test error 1")
|
||||
}
|
||||
err = enforcer.ApplicationError(400_00_01_02)
|
||||
if err.GetMessage() != "Test error 2" {
|
||||
t.Errorf("expected message to be Test error 2")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRegisterErrorMap(t *testing.T) {
|
||||
errorMap := map[enforcer.ApplicationError]string{
|
||||
enforcer.ApplicationError(400_00_01_01): "Test error 1",
|
||||
enforcer.ApplicationError(400_00_01_02): "Test error 2",
|
||||
}
|
||||
|
||||
enforcer.RegisterErrorMap(errorMap)
|
||||
|
||||
err := enforcer.ApplicationError(400_00_01_01)
|
||||
if err.GetMessage() != "Test error 1" {
|
||||
t.Errorf("expected message to be Test error 1")
|
||||
}
|
||||
err = enforcer.ApplicationError(400_00_01_02)
|
||||
if err.GetMessage() != "Test error 2" {
|
||||
t.Errorf("expected message to be Test error 2")
|
||||
}
|
||||
}
|
92
enforcer/sentinel.go
Normal file
92
enforcer/sentinel.go
Normal file
@ -0,0 +1,92 @@
|
||||
package enforcer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Sentinel represents a structured error with metadata and context information.
|
||||
type Sentinel struct {
|
||||
HTTPStatus int `json:"http_status"`
|
||||
Code ApplicationError `json:"code"`
|
||||
Message string `json:"message"`
|
||||
OriginalError error `json:"-"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
FunctionRef string `json:"function_ref"`
|
||||
}
|
||||
|
||||
// Error constructs and returns the string representation of the error.
|
||||
func (s *Sentinel) Error() string {
|
||||
metadataStr := ""
|
||||
if len(s.Metadata) > 0 {
|
||||
metadataBytes, err := json.Marshal(s.Metadata)
|
||||
if err == nil {
|
||||
metadataStr = fmt.Sprintf(" | METADATA: %s", string(metadataBytes))
|
||||
}
|
||||
}
|
||||
|
||||
if s.OriginalError != nil {
|
||||
return fmt.Sprintf(
|
||||
"[Application Error %d] %s | CAUSE: %s%s",
|
||||
s.Code, s.Message, s.OriginalError.Error(), metadataStr,
|
||||
)
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"[Application Error %d] %s%s", s.Code, s.Message, metadataStr,
|
||||
)
|
||||
}
|
||||
|
||||
// WithMeta adds a key-value pair to the Metadata field of the Sentinel.
|
||||
// It returns the updated Sentinel object for method chaining.
|
||||
func (s *Sentinel) WithMeta(key string, value interface{}) *Sentinel {
|
||||
s.Metadata[key] = value
|
||||
return s
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying original error wrapped by the Sentinel.
|
||||
func (s *Sentinel) Unwrap() error {
|
||||
return s.OriginalError
|
||||
}
|
||||
|
||||
// NewSentinel creates a new Sentinel error instance with the provided details.
|
||||
// It initializes the HTTPStatus, Code, Message, OriginalError, Metadata,
|
||||
// Timestamp, and Summoner fields based on the input parameters.
|
||||
// appErr provides the application-specific error information.
|
||||
// original represents the original error that caused this error, if any.
|
||||
// args can be used to format the message of the ApplicationError.
|
||||
func NewSentinel(
|
||||
appErr ApplicationError, original error, args ...interface{},
|
||||
) *Sentinel {
|
||||
return &Sentinel{
|
||||
HTTPStatus: appErr.GetHTTPStatus(),
|
||||
Code: appErr,
|
||||
Message: fmt.Sprintf(appErr.GetMessage(), args...),
|
||||
OriginalError: original,
|
||||
Metadata: make(map[string]interface{}),
|
||||
Timestamp: time.Now().UTC(),
|
||||
FunctionRef: identifyInvoker(),
|
||||
}
|
||||
}
|
||||
|
||||
// identifyInvoker retrieves and formats the caller's file, line, and function name.
|
||||
func identifyInvoker() string {
|
||||
pc, file, line, ok := runtime.Caller(2)
|
||||
if !ok {
|
||||
return "unknown:0 Unknown"
|
||||
}
|
||||
|
||||
fn := runtime.FuncForPC(pc)
|
||||
filename := filepath.Base(file)
|
||||
|
||||
funcName := fn.Name()
|
||||
if lastSlash := strings.LastIndex(funcName, "/"); lastSlash != -1 {
|
||||
funcName = funcName[lastSlash+1:]
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d %s", filename, line, funcName)
|
||||
}
|
89
enforcer/sentinel_test.go
Normal file
89
enforcer/sentinel_test.go
Normal file
@ -0,0 +1,89 @@
|
||||
package enforcer_test
|
||||
|
||||
import (
|
||||
stderrors "errors"
|
||||
"testing"
|
||||
|
||||
"gitstormr.dev/code-raider/noxarion-x/enforcer"
|
||||
)
|
||||
|
||||
func TestDoomBringer_WithMeta(t *testing.T) {
|
||||
appErr := enforcer.ApplicationError(400_00_01_01)
|
||||
|
||||
if appErr.GetCode() != 400_00_01_01 {
|
||||
t.Errorf("expected code to be 400_00_01_01")
|
||||
}
|
||||
|
||||
originalError := stderrors.New("tribe is deleted")
|
||||
doomBringer := enforcer.NewSentinel(appErr, nil, "Stormborn Guild")
|
||||
|
||||
_ = doomBringer.WithMeta("key1", "value1").
|
||||
WithMeta("key2", 42)
|
||||
|
||||
err := doomBringer.Error()
|
||||
if err == "" {
|
||||
t.Errorf("expected error")
|
||||
}
|
||||
|
||||
doomBringer.OriginalError = originalError
|
||||
|
||||
err = doomBringer.Error()
|
||||
if err == "" {
|
||||
t.Errorf("expected error")
|
||||
}
|
||||
|
||||
unwrap := doomBringer.Unwrap()
|
||||
if unwrap == nil {
|
||||
t.Errorf("expected unwrap")
|
||||
}
|
||||
|
||||
if key1, ok := doomBringer.Metadata["key1"]; !ok {
|
||||
t.Errorf("expected key1 to be present")
|
||||
} else {
|
||||
if key1 != "value1" {
|
||||
t.Errorf("expected key1 to be value1")
|
||||
}
|
||||
}
|
||||
|
||||
if key2, ok := doomBringer.Metadata["key2"]; !ok {
|
||||
t.Errorf("expected key2 to be present")
|
||||
} else {
|
||||
if key2 != 42 {
|
||||
t.Errorf("expected key2 to be 42")
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("%#v", doomBringer.Error())
|
||||
}
|
||||
|
||||
func TestNewSentinel(t *testing.T) {
|
||||
appErr := enforcer.ApplicationError(400_00_01_01)
|
||||
originalError := stderrors.New("tribe is deleted")
|
||||
args := []interface{}{"Tribe123"}
|
||||
|
||||
sentinel := enforcer.NewSentinel(appErr, originalError, args...)
|
||||
|
||||
if appErr.GetHTTPStatus() != sentinel.HTTPStatus {
|
||||
t.Errorf("expected HTTPStatus to be %d", appErr.GetHTTPStatus())
|
||||
}
|
||||
|
||||
if sentinel.OriginalError.Error() != originalError.Error() {
|
||||
t.Errorf("expected OriginalError to be %s", originalError.Error())
|
||||
}
|
||||
|
||||
if sentinel.Code != appErr {
|
||||
t.Errorf("expected Code to be %v", appErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentifyInvoker(t *testing.T) {
|
||||
appErr := enforcer.ApplicationError(400_00_01_01)
|
||||
originalError := stderrors.New("sorcerer is deleted")
|
||||
sentinel := enforcer.NewSentinel(
|
||||
appErr, originalError, "Merlin the Mystic",
|
||||
)
|
||||
|
||||
if sentinel.FunctionRef == "" {
|
||||
t.Errorf("expected summoner to be present")
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user