Skip to content

Commit 99e6338

Browse files
committed
refactor: simplify example code and integration test
1 parent edf77c1 commit 99e6338

6 files changed

Lines changed: 70 additions & 210 deletions

File tree

internal/myapp/cmd/app.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,23 @@ import (
77
"github.com/kinbiko/rogerr/internal/myapp/pkg/service"
88
)
99

10-
// App represents the main application
1110
type App struct {
1211
name string
1312
service *service.ProcessingService
1413
handler *handler.RequestHandler
1514
}
1615

17-
// NewApp creates a new application instance
18-
func NewApp(name string) *App {
16+
func NewApp() *App {
1917
svc := service.NewProcessingService()
20-
hdl := handler.NewRequestHandler(svc)
21-
22-
return &App{
23-
name: name,
24-
service: svc,
25-
handler: hdl,
26-
}
18+
handler := handler.NewRequestHandler(svc)
19+
return &App{name: "demo-app", service: svc, handler: handler}
2720
}
2821

2922
// Execute runs the application with the given arguments
3023
func (app *App) Execute(ctx context.Context, args []string) error {
3124
if len(args) == 0 {
3225
return app.handler.HandleRequest(ctx, "default")
3326
}
34-
27+
3528
return app.handler.HandleRequest(ctx, args[0])
36-
}
29+
}

internal/myapp/integration_test.go

Lines changed: 26 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -9,160 +9,42 @@ import (
99

1010
func TestStacktraceIntegration(t *testing.T) {
1111
err := run([]string{"testdata"})
12-
1312
if err == nil {
1413
t.Fatal("expected error from run")
1514
}
16-
17-
// Use ErrorHandler to extract stacktrace
18-
handler := rogerr.NewErrorHandler()
19-
frames := handler.Stacktrace(err)
20-
if len(frames) == 0 {
21-
t.Fatal("expected stacktrace frames")
22-
}
23-
24-
// Log all frames for debugging
25-
t.Log("Captured stacktrace frames:")
26-
for i, frame := range frames {
27-
t.Logf(" [%d] %s (%s:%d) InApp=%v", i, frame.Function, frame.File, frame.Line, frame.InApp)
28-
}
29-
30-
// Define expected frames in reverse order (closest to error first)
31-
expectedFrames := []struct {
32-
functionPattern string
33-
filePattern string
34-
inApp bool
35-
description string
36-
}{
37-
{
38-
functionPattern: "executeBusinessLogic.func1",
39-
filePattern: "/internal/myapp/pkg/service/processing.go",
40-
inApp: true,
41-
description: "anonymous function in service package",
42-
},
43-
{
44-
functionPattern: "executeBusinessLogic",
45-
filePattern: "/internal/myapp/pkg/service/processing.go",
46-
inApp: true,
47-
description: "method on ProcessingService",
48-
},
49-
{
50-
functionPattern: "ProcessData",
51-
filePattern: "/internal/myapp/pkg/service/processing.go",
52-
inApp: true,
53-
description: "method on ProcessingService",
54-
},
55-
{
56-
functionPattern: "processRequest.func1",
57-
filePattern: "/internal/myapp/pkg/handler/request.go",
58-
inApp: true,
59-
description: "anonymous function in handler package",
60-
},
61-
{
62-
functionPattern: "processRequest",
63-
filePattern: "/internal/myapp/pkg/handler/request.go",
64-
inApp: true,
65-
description: "method on RequestHandler",
66-
},
67-
{
68-
functionPattern: "HandleRequest",
69-
filePattern: "/internal/myapp/pkg/handler/request.go",
70-
inApp: true,
71-
description: "method on RequestHandler",
72-
},
73-
{
74-
functionPattern: "Execute",
75-
filePattern: "/internal/myapp/cmd/app.go",
76-
inApp: true,
77-
description: "method on App struct",
78-
},
79-
{
80-
functionPattern: "run",
81-
filePattern: "/internal/myapp/main.go",
82-
inApp: true,
83-
description: "main package function",
84-
},
85-
}
86-
87-
// Validate that myapp frames are marked as InApp=true
88-
myappFrameCount := 0
15+
16+
frames := rogerr.NewErrorHandler().Stacktrace(err)
17+
var myappFrames, mylibFrames, rogerrFrames int
18+
8919
for _, frame := range frames {
90-
if strings.Contains(frame.File, "/internal/myapp/") {
91-
myappFrameCount++
20+
if frame.File == "" || frame.Function == "" || frame.Line <= 0 {
21+
t.Errorf("Invalid frame: %+v", frame)
22+
}
23+
24+
switch {
25+
case strings.Contains(frame.File, "/internal/myapp/"):
26+
myappFrames++
9227
if !frame.InApp {
93-
t.Errorf("Frame from myapp should be InApp=true: %s (%s:%d)",
94-
frame.Function, frame.File, frame.Line)
28+
t.Errorf("myapp frame should be InApp=true: %s", frame.Function)
9529
}
96-
}
97-
}
98-
99-
if myappFrameCount == 0 {
100-
t.Error("expected to find frames from myapp module")
101-
}
102-
103-
// Validate that mylib frames are marked as InApp=false
104-
mylibFrameCount := 0
105-
for _, frame := range frames {
106-
if strings.Contains(frame.File, "/internal/mylib/") {
107-
mylibFrameCount++
30+
case strings.Contains(frame.File, "/internal/mylib/"):
31+
mylibFrames++
10832
if frame.InApp {
109-
t.Errorf("Frame from mylib should be InApp=false: %s (%s:%d)",
110-
frame.Function, frame.File, frame.Line)
33+
t.Errorf("mylib frame should be InApp=false: %s", frame.Function)
11134
}
35+
case strings.HasPrefix(frame.Function, "github.com/kinbiko/rogerr."):
36+
rogerrFrames++
37+
t.Errorf("Found rogerr main package frame: %s", frame.Function)
11238
}
11339
}
114-
115-
if mylibFrameCount == 0 {
116-
t.Error("expected to find frames from mylib module")
117-
}
118-
119-
// Validate specific function patterns exist (only for myapp frames)
120-
foundFunctions := make(map[string]bool)
121-
for _, frame := range frames {
122-
// Skip testing framework frames and mylib frames for our validation
123-
if strings.Contains(frame.Function, "testing.") ||
124-
strings.Contains(frame.Function, "runtime.") ||
125-
strings.Contains(frame.Function, "github.com/kinbiko/rogerr/internal/mylib") {
126-
continue
127-
}
128-
129-
for _, expected := range expectedFrames {
130-
if strings.Contains(frame.Function, expected.functionPattern) {
131-
foundFunctions[expected.functionPattern] = true
132-
133-
// Validate file path
134-
if !strings.Contains(frame.File, expected.filePattern) {
135-
t.Errorf("Frame %s should be in file containing %s, got %s",
136-
frame.Function, expected.filePattern, frame.File)
137-
}
138-
139-
// Validate InApp status
140-
if frame.InApp != expected.inApp {
141-
t.Errorf("Frame %s should have InApp=%v, got %v",
142-
frame.Function, expected.inApp, frame.InApp)
143-
}
144-
145-
// Validate line number is reasonable
146-
if frame.Line <= 0 {
147-
t.Errorf("Frame %s should have positive line number, got %d",
148-
frame.Function, frame.Line)
149-
}
150-
}
151-
}
40+
41+
if myappFrames != 9 {
42+
t.Errorf("expected 9 frames from myapp module, got %d", myappFrames)
15243
}
153-
154-
// Check that we found all expected function patterns
155-
for _, expected := range expectedFrames {
156-
if !foundFunctions[expected.functionPattern] {
157-
t.Errorf("Expected to find function containing '%s' (%s) in stacktrace",
158-
expected.functionPattern, expected.description)
159-
}
44+
if mylibFrames != 5 {
45+
t.Errorf("expected 5 frames from mylib module, got %d", mylibFrames)
16046
}
161-
162-
// Validate that no rogerr main package frames are present
163-
for _, frame := range frames {
164-
if strings.HasPrefix(frame.Function, "github.com/kinbiko/rogerr.") {
165-
t.Errorf("Found rogerr main package frame in stacktrace: %s", frame.Function)
166-
}
47+
if rogerrFrames != 0 {
48+
t.Errorf("expected 0 frames from rogerr module, got %d", rogerrFrames)
16749
}
168-
}
50+
}

internal/myapp/main.go

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,52 +10,49 @@ import (
1010
)
1111

1212
func main() {
13-
// Configure slog for JSON output
14-
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
15-
Level: slog.LevelInfo,
16-
}))
13+
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
1714
slog.SetDefault(logger)
1815

1916
args := os.Args[1:] // Skip program name
2017
if len(args) == 0 {
21-
args = []string{"demo-data"} // Default argument for demo
18+
args = []string{"demo-data"}
2219
}
2320

2421
err := run(args)
25-
if err != nil {
26-
// Extract stacktrace using ErrorHandler
27-
handler := rogerr.NewErrorHandler()
28-
frames := handler.Stacktrace(err)
29-
30-
// Convert frames to OTEL logging data model format
31-
frameData := make([]map[string]interface{}, len(frames))
32-
for i, frame := range frames {
33-
frameData[i] = map[string]interface{}{
34-
"code.function": frame.Function,
35-
"code.filepath": frame.File,
36-
"code.lineno": frame.Line,
37-
"code.namespace": func() string {
38-
if frame.InApp {
39-
return "application"
40-
}
41-
return "dependency"
42-
}(),
43-
}
44-
}
22+
if err == nil {
23+
return
24+
}
4525

46-
// Log using OTEL logging data model semantic conventions
47-
slog.Error("Exception occurred",
48-
slog.String("exception.type", "ApplicationError"),
49-
slog.String("exception.message", err.Error()),
50-
slog.Any("exception.stacktrace", frameData),
51-
slog.String("service.name", "demo-app"),
52-
slog.String("service.version", "1.0.0"),
53-
)
26+
frames := rogerr.NewErrorHandler().Stacktrace(err)
27+
28+
// Convert frames to OTEL logging data model format
29+
frameData := make([]map[string]interface{}, len(frames))
30+
for i, frame := range frames {
31+
frameData[i] = map[string]interface{}{
32+
"code.function": frame.Function,
33+
"code.filepath": frame.File,
34+
"code.lineno": frame.Line,
35+
"code.namespace": func() string {
36+
if frame.InApp {
37+
return "application"
38+
}
39+
return "dependency"
40+
}(),
41+
}
5442
}
43+
44+
// Log using OTEL logging data model semantic conventions
45+
slog.Error("Exception occurred",
46+
slog.String("exception.type", "ApplicationError"),
47+
slog.String("exception.message", err.Error()),
48+
slog.Any("exception.stacktrace", frameData),
49+
slog.String("service.name", "demo-app"),
50+
slog.String("service.version", "1.0.0"),
51+
)
5552
}
5653

5754
func run(args []string) error {
5855
ctx := context.Background()
59-
app := cmd.NewApp("demo-app")
56+
app := cmd.NewApp()
6057
return app.Execute(ctx, args)
6158
}

internal/myapp/pkg/handler/request.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,22 @@ import (
66
"github.com/kinbiko/rogerr/internal/myapp/pkg/service"
77
)
88

9-
// RequestHandler handles incoming requests
109
type RequestHandler struct {
1110
service *service.ProcessingService
1211
}
1312

14-
// NewRequestHandler creates a new request handler
1513
func NewRequestHandler(svc *service.ProcessingService) *RequestHandler {
16-
return &RequestHandler{
17-
service: svc,
18-
}
14+
return &RequestHandler{service: svc}
1915
}
2016

21-
// HandleRequest processes a request with the given input
2217
func (h *RequestHandler) HandleRequest(ctx context.Context, input string) error {
2318
return h.processRequest(ctx, input)
2419
}
2520

26-
// processRequest is an internal method that validates and processes the request
2721
func (h *RequestHandler) processRequest(ctx context.Context, input string) error {
28-
// Add validation layer
2922
validateFunc := func() error {
3023
return h.service.ProcessData(ctx, input)
3124
}
32-
25+
3326
return validateFunc()
34-
}
27+
}

internal/myapp/pkg/service/processing.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,24 @@ import (
66
"github.com/kinbiko/rogerr/internal/mylib"
77
)
88

9-
// ProcessingService handles business logic
109
type ProcessingService struct {
1110
name string
1211
}
1312

14-
// NewProcessingService creates a new processing service
1513
func NewProcessingService() *ProcessingService {
1614
return &ProcessingService{
1715
name: "data-processor",
1816
}
1917
}
2018

21-
// ProcessData processes the given data through the business logic layer
2219
func (s *ProcessingService) ProcessData(ctx context.Context, data string) error {
2320
return s.executeBusinessLogic(ctx, data)
2421
}
2522

26-
// executeBusinessLogic runs the core business logic
2723
func (s *ProcessingService) executeBusinessLogic(ctx context.Context, data string) error {
28-
// Call library function through service layer
2924
callLibrary := func() error {
3025
return mylib.ComplexOperation(ctx, data)
3126
}
32-
27+
3328
return callLibrary()
34-
}
29+
}

internal/mylib/errors.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ func (es *ErrorService) CreateError(ctx context.Context, msg string) error {
2828
// ProcessDataWithError is a package-level function that creates an error
2929
func ProcessDataWithError(ctx context.Context, data string) error {
3030
service := NewErrorService()
31-
31+
3232
// Call through anonymous function to add another stack frame
3333
processFunc := func() error {
3434
return service.CreateError(ctx, "failed to process data: "+data)
3535
}
36-
36+
3737
return processFunc()
3838
}
3939

@@ -45,4 +45,4 @@ func ComplexOperation(ctx context.Context, input string) error {
4545
// intermediateFunction is a helper function in the call chain
4646
func intermediateFunction(ctx context.Context, input string) error {
4747
return ProcessDataWithError(ctx, input)
48-
}
48+
}

0 commit comments

Comments
 (0)