Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/codspeed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CodSpeed Performance Benchmarks
on:
push:
branches:
- master
pull_request: null
permissions:
contents: read
id-token: write
jobs:
benchmarks:
name: Run Go Benchmarks with CodSpeed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.13"
- name: Get dependencies
run: |
go mod download
go mod verify
- name: Run benchmarks with CodSpeed
uses: CodSpeedHQ/action@v4.4.1
with:
upload-url: ${{ secrets.CODSPEED_STAGING_UPLOAD_URL }}
run: go test -bench=. -benchmem ./...
119 changes: 119 additions & 0 deletions internal/album/service_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package album

import (
"context"
"testing"

"github.com/qiangxue/go-rest-api/pkg/log"
)

func BenchmarkService_Create(b *testing.B) {
logger, _ := log.NewForTest()
s := NewService(&mockRepository{}, logger)
ctx := context.Background()
req := CreateAlbumRequest{Name: "benchmark album"}

b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = s.Create(ctx, req)
}
}

func BenchmarkService_Get(b *testing.B) {
logger, _ := log.NewForTest()
repo := &mockRepository{}
s := NewService(repo, logger)
ctx := context.Background()

// Setup: create an album first
album, _ := s.Create(ctx, CreateAlbumRequest{Name: "test album"})

b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = s.Get(ctx, album.ID)
}
}

func BenchmarkService_Update(b *testing.B) {
logger, _ := log.NewForTest()
repo := &mockRepository{}
s := NewService(repo, logger)
ctx := context.Background()

// Setup: create an album first
album, _ := s.Create(ctx, CreateAlbumRequest{Name: "test album"})
req := UpdateAlbumRequest{Name: "updated album"}

b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = s.Update(ctx, album.ID, req)
}
}

func BenchmarkService_Delete(b *testing.B) {
logger, _ := log.NewForTest()
ctx := context.Background()

b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
repo := &mockRepository{}
s := NewService(repo, logger)
album, _ := s.Create(ctx, CreateAlbumRequest{Name: "test album"})
b.StartTimer()

_, _ = s.Delete(ctx, album.ID)
}
}

func BenchmarkService_Query(b *testing.B) {
logger, _ := log.NewForTest()
repo := &mockRepository{}
s := NewService(repo, logger)
ctx := context.Background()

// Setup: create multiple albums
for i := 0; i < 10; i++ {
_, _ = s.Create(ctx, CreateAlbumRequest{Name: "test album"})
}

b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = s.Query(ctx, 0, 10)
}
}

func BenchmarkService_Count(b *testing.B) {
logger, _ := log.NewForTest()
repo := &mockRepository{}
s := NewService(repo, logger)
ctx := context.Background()

// Setup: create multiple albums
for i := 0; i < 10; i++ {
_, _ = s.Create(ctx, CreateAlbumRequest{Name: "test album"})
}

b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = s.Count(ctx)
}
}

func BenchmarkCreateAlbumRequest_Validate(b *testing.B) {
req := CreateAlbumRequest{Name: "test album"}

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = req.Validate()
}
}

func BenchmarkUpdateAlbumRequest_Validate(b *testing.B) {
req := UpdateAlbumRequest{Name: "updated album"}

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = req.Validate()
}
}
48 changes: 48 additions & 0 deletions internal/auth/service_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package auth

import (
"context"
"testing"

"github.com/qiangxue/go-rest-api/pkg/log"
)

func BenchmarkService_Login(b *testing.B) {
logger, _ := log.NewForTest()
s := NewService("test-signing-key", 3600, logger)
ctx := context.Background()

b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = s.Login(ctx, "demo", "pass")
}
}

func BenchmarkService_LoginInvalid(b *testing.B) {
logger, _ := log.NewForTest()
s := NewService("test-signing-key", 3600, logger)
ctx := context.Background()

b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = s.Login(ctx, "invalid", "invalid")
}
}

func BenchmarkWithUser(b *testing.B) {
ctx := context.Background()

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = WithUser(ctx, "100", "demo")
}
}

func BenchmarkCurrentUser(b *testing.B) {
ctx := WithUser(context.Background(), "100", "demo")

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = CurrentUser(ctx)
}
}
98 changes: 98 additions & 0 deletions pkg/log/logger_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package log

import (
"context"
"net/http"
"testing"
)

func BenchmarkLogger_With(b *testing.B) {
logger := New()
ctx := context.Background()
args := []interface{}{"key1", "value1", "key2", "value2"}

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = logger.With(ctx, args...)
}
}

func BenchmarkLogger_WithContext(b *testing.B) {
logger := New()
req, _ := http.NewRequest("GET", "/test", nil)
req.Header.Set("X-Request-ID", "test-request-id")
req.Header.Set("X-Correlation-ID", "test-correlation-id")
ctx := WithRequest(context.Background(), req)
args := []interface{}{"key", "value"}

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = logger.With(ctx, args...)
}
}

func BenchmarkLogger_Debug(b *testing.B) {
logger := New()

b.ResetTimer()
for i := 0; i < b.N; i++ {
logger.Debug("debug message")
}
}

func BenchmarkLogger_Info(b *testing.B) {
logger := New()

b.ResetTimer()
for i := 0; i < b.N; i++ {
logger.Info("info message")
}
}

func BenchmarkLogger_Error(b *testing.B) {
logger := New()

b.ResetTimer()
for i := 0; i < b.N; i++ {
logger.Error("error message")
}
}

func BenchmarkLogger_Debugf(b *testing.B) {
logger := New()

b.ResetTimer()
for i := 0; i < b.N; i++ {
logger.Debugf("debug message: %s %d", "test", i)
}
}

func BenchmarkLogger_Infof(b *testing.B) {
logger := New()

b.ResetTimer()
for i := 0; i < b.N; i++ {
logger.Infof("info message: %s %d", "test", i)
}
}

func BenchmarkLogger_Errorf(b *testing.B) {
logger := New()

b.ResetTimer()
for i := 0; i < b.N; i++ {
logger.Errorf("error message: %s %d", "test", i)
}
}

func BenchmarkWithRequest(b *testing.B) {
req, _ := http.NewRequest("GET", "/test", nil)
req.Header.Set("X-Request-ID", "test-request-id")
req.Header.Set("X-Correlation-ID", "test-correlation-id")
ctx := context.Background()

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = WithRequest(ctx, req)
}
}
81 changes: 81 additions & 0 deletions pkg/pagination/pages_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package pagination

import (
"net/http"
"testing"
)

func BenchmarkNew(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = New(1, 100, 1000)
}
}

func BenchmarkNewFromRequest(b *testing.B) {
req, _ := http.NewRequest("GET", "/test?page=2&per_page=50", nil)

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = NewFromRequest(req, 1000)
}
}

func BenchmarkPages_Offset(b *testing.B) {
pages := New(5, 100, 1000)

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = pages.Offset()
}
}

func BenchmarkPages_Limit(b *testing.B) {
pages := New(5, 100, 1000)

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = pages.Limit()
}
}

func BenchmarkPages_BuildLinks(b *testing.B) {
pages := New(5, 100, 1000)
baseURL := "http://example.com/api/items"

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = pages.BuildLinks(baseURL, 100)
}
}

func BenchmarkPages_BuildLinkHeader(b *testing.B) {
pages := New(5, 100, 1000)
baseURL := "http://example.com/api/items"

b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = pages.BuildLinkHeader(baseURL, 100)
}
}

func BenchmarkParseInt(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = parseInt("42", 10)
}
}

func BenchmarkParseIntDefault(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = parseInt("", 10)
}
}

func BenchmarkParseIntInvalid(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = parseInt("invalid", 10)
}
}
Loading