mirror of
https://github.com/makayabou/asg-server.git
synced 2026-05-02 17:43:36 +02:00
[refactor] move reusable code to the library
This commit is contained in:
parent
2839fb959c
commit
f4188c5fdf
2
go.mod
2
go.mod
@ -6,7 +6,7 @@ require (
|
||||
firebase.google.com/go/v4 v4.12.1
|
||||
github.com/android-sms-gateway/client-go v1.5.1
|
||||
github.com/ansrivas/fiberprometheus/v2 v2.6.1
|
||||
github.com/capcom6/go-helpers v0.0.0-20240521035631-865ee2879fa3
|
||||
github.com/capcom6/go-helpers v0.1.0
|
||||
github.com/capcom6/go-infra-fx v0.2.0
|
||||
github.com/go-playground/assert/v2 v2.2.0
|
||||
github.com/go-playground/validator/v10 v10.16.0
|
||||
|
||||
2
go.sum
2
go.sum
@ -43,6 +43,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/capcom6/go-helpers v0.0.0-20240521035631-865ee2879fa3 h1:mq9rmBMCCzqGnZtbQqFSd+Ua3fahqUOYaTf26YFhWJc=
|
||||
github.com/capcom6/go-helpers v0.0.0-20240521035631-865ee2879fa3/go.mod h1:WDqc7HZNqHxUTisArkYIBZtqUfJBVyPWeQI+FMwEzAw=
|
||||
github.com/capcom6/go-helpers v0.1.0 h1:eootTmUSCzlu8xOHJfDlT1AasVWsgzZ9grhDxovMtwY=
|
||||
github.com/capcom6/go-helpers v0.1.0/go.mod h1:WDqc7HZNqHxUTisArkYIBZtqUfJBVyPWeQI+FMwEzAw=
|
||||
github.com/capcom6/go-infra-fx v0.2.0 h1:FrWtdFiG58unIK7xN7kMJn3LfOFecp20W/ZVgvN3bsM=
|
||||
github.com/capcom6/go-infra-fx v0.2.0/go.mod h1:T/DnT1EDrF9F+44eZw/lZnmsz5Dry0w/CTk0FB1Nct0=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
|
||||
@ -3,13 +3,13 @@ package converters
|
||||
import (
|
||||
"github.com/android-sms-gateway/client-go/smsgateway"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/models"
|
||||
"github.com/android-sms-gateway/server/pkg/types"
|
||||
"github.com/capcom6/go-helpers/anys"
|
||||
)
|
||||
|
||||
func DeviceToDTO(device models.Device) smsgateway.Device {
|
||||
return smsgateway.Device{
|
||||
ID: device.ID,
|
||||
Name: types.OrDefault(device.Name, ""),
|
||||
Name: anys.OrDefault(device.Name, ""),
|
||||
CreatedAt: device.CreatedAt,
|
||||
UpdatedAt: device.UpdatedAt,
|
||||
DeletedAt: device.DeletedAt,
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"github.com/android-sms-gateway/client-go/smsgateway"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/converters"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/models"
|
||||
"github.com/android-sms-gateway/server/pkg/types"
|
||||
"github.com/capcom6/go-helpers/anys"
|
||||
"github.com/go-playground/assert/v2"
|
||||
)
|
||||
|
||||
@ -30,7 +30,7 @@ func TestDeviceToDTO(t *testing.T) {
|
||||
name: "non-empty device",
|
||||
device: models.Device{
|
||||
ID: "test-id",
|
||||
Name: types.AsPointer("test-name"),
|
||||
Name: anys.AsPointer("test-name"),
|
||||
LastSeen: lastSeenAt,
|
||||
TimedModel: models.TimedModel{
|
||||
CreatedAt: createdAt,
|
||||
|
||||
@ -5,7 +5,7 @@ import (
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/base"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/health"
|
||||
"github.com/android-sms-gateway/server/internal/version"
|
||||
"github.com/android-sms-gateway/server/pkg/maps"
|
||||
"github.com/capcom6/go-helpers/maps"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/models"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/auth"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/messages"
|
||||
"github.com/android-sms-gateway/server/pkg/types"
|
||||
"github.com/capcom6/go-helpers/anys"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/keyauth"
|
||||
@ -50,7 +50,7 @@ func (h *mobileHandler) getDevice(device models.Device, c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if !device.IsEmpty() {
|
||||
res.Device = types.AsPointer(converters.DeviceToDTO(device))
|
||||
res.Device = anys.AsPointer(converters.DeviceToDTO(device))
|
||||
}
|
||||
|
||||
return c.JSON(res)
|
||||
|
||||
@ -6,7 +6,7 @@ import (
|
||||
"github.com/android-sms-gateway/client-go/smsgateway"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/base"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/push"
|
||||
"github.com/android-sms-gateway/server/pkg/types"
|
||||
"github.com/capcom6/go-helpers/anys"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/limiter"
|
||||
@ -69,7 +69,7 @@ func (h *upstreamHandler) postPush(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
event := push.Event{
|
||||
Event: types.ZeroDefault(v.Event, smsgateway.PushMessageEnqueued),
|
||||
Event: anys.ZeroDefault(v.Event, smsgateway.PushMessageEnqueued),
|
||||
Data: v.Data,
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/models"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/devices"
|
||||
"github.com/android-sms-gateway/server/pkg/crypto"
|
||||
"github.com/android-sms-gateway/server/pkg/types/cache"
|
||||
"github.com/capcom6/go-helpers/cache"
|
||||
"github.com/jaevor/go-nanoid"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/models"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/db"
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/push"
|
||||
"github.com/android-sms-gateway/server/pkg/types"
|
||||
"github.com/capcom6/go-helpers/anys"
|
||||
"github.com/capcom6/go-helpers/slices"
|
||||
"github.com/nyaruka/phonenumbers"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@ -119,7 +119,7 @@ func (s *Service) SelectPending(deviceID string) ([]smsgateway.Message, error) {
|
||||
ID: v.ExtID,
|
||||
Message: v.Message,
|
||||
SimNumber: v.SimNumber,
|
||||
WithDeliveryReport: types.AsPointer[bool](v.WithDeliveryReport),
|
||||
WithDeliveryReport: anys.AsPointer[bool](v.WithDeliveryReport),
|
||||
IsEncrypted: v.IsEncrypted,
|
||||
PhoneNumbers: s.recipientsToDomain(v.Recipients),
|
||||
TTL: ttl,
|
||||
@ -206,7 +206,7 @@ func (s *Service) Enqeue(device models.Device, message smsgateway.Message, opts
|
||||
|
||||
var validUntil *time.Time = message.ValidUntil
|
||||
if message.TTL != nil && *message.TTL > 0 {
|
||||
validUntil = types.AsPointer(time.Now().Add(time.Duration(*message.TTL) * time.Second))
|
||||
validUntil = anys.AsPointer(time.Now().Add(time.Duration(*message.TTL) * time.Second))
|
||||
}
|
||||
|
||||
msg := models.Message{
|
||||
@ -215,7 +215,7 @@ func (s *Service) Enqeue(device models.Device, message smsgateway.Message, opts
|
||||
Message: message.Message,
|
||||
ValidUntil: validUntil,
|
||||
SimNumber: message.SimNumber,
|
||||
WithDeliveryReport: types.OrDefault[bool](message.WithDeliveryReport, true),
|
||||
WithDeliveryReport: anys.OrDefault[bool](message.WithDeliveryReport, true),
|
||||
IsEncrypted: message.IsEncrypted,
|
||||
Device: device,
|
||||
Recipients: s.recipientsToModel(message.PhoneNumbers),
|
||||
|
||||
@ -6,7 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/push/domain"
|
||||
"github.com/android-sms-gateway/server/pkg/types/cache"
|
||||
"github.com/capcom6/go-helpers/cache"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"go.uber.org/fx"
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
package maps
|
||||
|
||||
func MapValues[K comparable, V any, R any](m map[K]V, f func(V) R) map[K]R {
|
||||
result := make(map[K]R, len(m))
|
||||
for k, v := range m {
|
||||
result[k] = f(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
102
pkg/types/cache/cache.go
vendored
102
pkg/types/cache/cache.go
vendored
@ -1,102 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type item[T any] struct {
|
||||
value T
|
||||
validUntil time.Time
|
||||
}
|
||||
|
||||
func (i *item[T]) isExpired(now time.Time) bool {
|
||||
return !i.validUntil.IsZero() && now.After(i.validUntil)
|
||||
}
|
||||
|
||||
type Cache[T any] struct {
|
||||
items map[string]*item[T]
|
||||
ttl time.Duration
|
||||
empty T
|
||||
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
func New[T any](cfg Config) *Cache[T] {
|
||||
return &Cache[T]{
|
||||
items: make(map[string]*item[T]),
|
||||
ttl: cfg.TTL,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache[T]) Set(key string, value T) error {
|
||||
var validUntil time.Time
|
||||
if c.ttl > 0 {
|
||||
validUntil = time.Now().Add(c.ttl)
|
||||
}
|
||||
|
||||
c.mux.Lock()
|
||||
c.items[key] = &item[T]{
|
||||
value: value,
|
||||
validUntil: validUntil,
|
||||
}
|
||||
c.mux.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache[T]) Get(key string) (T, error) {
|
||||
c.mux.RLock()
|
||||
item, ok := c.items[key]
|
||||
c.mux.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return c.empty, ErrKeyNotFound
|
||||
}
|
||||
|
||||
if item.isExpired(time.Now()) {
|
||||
return c.empty, ErrKeyExpired
|
||||
}
|
||||
|
||||
return item.value, nil
|
||||
}
|
||||
|
||||
func (c *Cache[T]) Delete(key string) error {
|
||||
c.mux.Lock()
|
||||
delete(c.items, key)
|
||||
c.mux.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache[T]) Drain() map[string]T {
|
||||
t := time.Now()
|
||||
|
||||
c.mux.Lock()
|
||||
copy := c.items
|
||||
c.items = make(map[string]*item[T], len(copy))
|
||||
c.mux.Unlock()
|
||||
|
||||
items := make(map[string]T, len(copy))
|
||||
for key, item := range copy {
|
||||
if item.isExpired(t) {
|
||||
continue
|
||||
}
|
||||
items[key] = item.value
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
func (c *Cache[T]) Cleanup() {
|
||||
t := time.Now()
|
||||
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
for key, item := range c.items {
|
||||
if item.isExpired(t) {
|
||||
delete(c.items, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
387
pkg/types/cache/cache_test.go
vendored
387
pkg/types/cache/cache_test.go
vendored
@ -1,387 +0,0 @@
|
||||
package cache_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/android-sms-gateway/server/pkg/types/cache"
|
||||
)
|
||||
|
||||
func TestCache_Set(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
value any
|
||||
ttl time.Duration
|
||||
wait time.Duration
|
||||
expected any
|
||||
}{
|
||||
{
|
||||
name: "Set string value w/o TTL",
|
||||
key: "key1",
|
||||
value: "value1",
|
||||
ttl: 0,
|
||||
wait: 0,
|
||||
expected: "value1",
|
||||
},
|
||||
{
|
||||
name: "Set string value",
|
||||
key: "key1",
|
||||
value: "value1",
|
||||
ttl: 10 * time.Second,
|
||||
wait: 0,
|
||||
expected: "value1",
|
||||
},
|
||||
{
|
||||
name: "Set integer value",
|
||||
key: "key2",
|
||||
value: 123,
|
||||
ttl: 10 * time.Second,
|
||||
wait: 0,
|
||||
expected: 123,
|
||||
},
|
||||
{
|
||||
name: "Set value with expired TTL",
|
||||
key: "key3",
|
||||
value: "value3",
|
||||
ttl: 1 * time.Second,
|
||||
wait: 2 * time.Second,
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cache := cache.New[any](cache.Config{TTL: tt.ttl})
|
||||
if err := cache.Set(tt.key, tt.value); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if tt.wait > 0 {
|
||||
time.Sleep(tt.wait)
|
||||
}
|
||||
|
||||
result, err := cache.Get(tt.key)
|
||||
if tt.expected == nil {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error, got nil")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if result != tt.expected {
|
||||
t.Errorf("Expected %v, got %v", tt.expected, result)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCache_Drain(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup map[string]any
|
||||
ttl time.Duration
|
||||
wait time.Duration
|
||||
expected map[string]any
|
||||
}{
|
||||
{
|
||||
name: "Drain non-expired items",
|
||||
setup: map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": 42,
|
||||
"key3": true,
|
||||
},
|
||||
ttl: 10 * time.Second,
|
||||
wait: 0,
|
||||
expected: map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": 42,
|
||||
"key3": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Drain with some expired items",
|
||||
setup: map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": 42,
|
||||
"key3": true,
|
||||
},
|
||||
ttl: 1 * time.Second,
|
||||
wait: 2 * time.Second,
|
||||
expected: map[string]any{}, // All items should be expired
|
||||
},
|
||||
{
|
||||
name: "Drain empty cache",
|
||||
setup: map[string]any{},
|
||||
ttl: 10 * time.Second,
|
||||
wait: 0,
|
||||
expected: map[string]any{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cache := cache.New[any](cache.Config{TTL: tt.ttl})
|
||||
|
||||
// Setup cache
|
||||
for k, v := range tt.setup {
|
||||
err := cache.Set(k, v)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set up cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait if specified
|
||||
if tt.wait > 0 {
|
||||
time.Sleep(tt.wait)
|
||||
}
|
||||
|
||||
// Drain cache
|
||||
result := cache.Drain()
|
||||
|
||||
// Check result
|
||||
if len(result) != len(tt.expected) {
|
||||
t.Errorf("Expected %d items, got %d", len(tt.expected), len(result))
|
||||
}
|
||||
|
||||
for k, v := range tt.expected {
|
||||
if rv, ok := result[k]; !ok || rv != v {
|
||||
t.Errorf("Expected %v for key %s, got %v", v, k, rv)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify cache is empty after drain
|
||||
if len(cache.Drain()) != 0 {
|
||||
t.Errorf("Cache should be empty after Drain")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCache_Cleanup(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup map[string]any
|
||||
ttl time.Duration
|
||||
wait time.Duration
|
||||
expected map[string]any
|
||||
}{
|
||||
{
|
||||
name: "No expired items",
|
||||
setup: map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": 42,
|
||||
"key3": true,
|
||||
},
|
||||
ttl: 10 * time.Second,
|
||||
wait: 0,
|
||||
expected: map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": 42,
|
||||
"key3": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Some expired items",
|
||||
setup: map[string]any{
|
||||
"key1": "value1",
|
||||
"key2": 42,
|
||||
"key3": true,
|
||||
},
|
||||
ttl: 1 * time.Second,
|
||||
wait: 2 * time.Second,
|
||||
expected: map[string]any{},
|
||||
},
|
||||
{
|
||||
name: "Empty cache",
|
||||
setup: map[string]any{},
|
||||
ttl: 10 * time.Second,
|
||||
wait: 0,
|
||||
expected: map[string]any{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cache := cache.New[any](cache.Config{TTL: tt.ttl})
|
||||
|
||||
// Setup cache
|
||||
for k, v := range tt.setup {
|
||||
err := cache.Set(k, v)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set up cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait if specified
|
||||
if tt.wait > 0 {
|
||||
time.Sleep(tt.wait)
|
||||
}
|
||||
|
||||
// Run cleanup
|
||||
cache.Cleanup()
|
||||
|
||||
// Check remaining items
|
||||
result := cache.Drain()
|
||||
|
||||
if len(result) != len(tt.expected) {
|
||||
t.Errorf("Expected %d items after cleanup, got %d", len(tt.expected), len(result))
|
||||
}
|
||||
|
||||
for k, v := range tt.expected {
|
||||
if rv, ok := result[k]; !ok || rv != v {
|
||||
t.Errorf("Expected %v for key %s after cleanup, got %v", v, k, rv)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
func BenchmarkCache_Get(b *testing.B) {
|
||||
cache := cache.New[string](cache.Config{TTL: 1 * time.Hour})
|
||||
|
||||
// Prepare the cache with some data
|
||||
testKey := "testKey"
|
||||
testValue := "testValue"
|
||||
err := cache.Set(testKey, testValue)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to set up cache: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
value, err := cache.Get(testKey)
|
||||
if err != nil {
|
||||
b.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if value != testValue {
|
||||
b.Fatalf("Unexpected value: got %v, want %v", value, testValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCache_GetNonExistent(b *testing.B) {
|
||||
c := cache.New[string](cache.Config{TTL: 1 * time.Hour})
|
||||
|
||||
nonExistentKey := "nonExistentKey"
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := c.Get(nonExistentKey)
|
||||
if err != cache.ErrKeyNotFound {
|
||||
b.Fatalf("Unexpected error: got %v, want %v", err, cache.ErrKeyNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCache_GetConcurrent(b *testing.B) {
|
||||
cache := cache.New[string](cache.Config{TTL: 1 * time.Hour})
|
||||
|
||||
// Prepare the cache with some data
|
||||
testKey := "testKey"
|
||||
testValue := "testValue"
|
||||
err := cache.Set(testKey, testValue)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to set up cache: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
value, err := cache.Get(testKey)
|
||||
if err != nil {
|
||||
b.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if value != testValue {
|
||||
b.Fatalf("Unexpected value: got %v, want %v", value, testValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkCache_Set(b *testing.B) {
|
||||
cache := cache.New[string](cache.Config{TTL: 1 * time.Hour})
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
key := fmt.Sprintf("key%d", i)
|
||||
value := fmt.Sprintf("value%d", i)
|
||||
err := cache.Set(key, value)
|
||||
if err != nil {
|
||||
b.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCache_SetExisting(b *testing.B) {
|
||||
cache := cache.New[string](cache.Config{TTL: 1 * time.Hour})
|
||||
|
||||
// Prepare the cache with initial data
|
||||
testKey := "testKey"
|
||||
initialValue := "initialValue"
|
||||
err := cache.Set(testKey, initialValue)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to set up cache: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
newValue := fmt.Sprintf("value%d", i)
|
||||
err := cache.Set(testKey, newValue)
|
||||
if err != nil {
|
||||
b.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCache_SetConcurrent(b *testing.B) {
|
||||
cache := cache.New[string](cache.Config{TTL: 1 * time.Hour})
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
key := fmt.Sprintf("key%d", i)
|
||||
value := fmt.Sprintf("value%d", i)
|
||||
err := cache.Set(key, value)
|
||||
if err != nil {
|
||||
b.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkCache_SetAndGetConcurrent(b *testing.B) {
|
||||
cache := cache.New[string](cache.Config{TTL: 1 * time.Hour})
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
key := fmt.Sprintf("key%d", i)
|
||||
value := fmt.Sprintf("value%d", i)
|
||||
err := cache.Set(key, value)
|
||||
if err != nil {
|
||||
b.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
_, err = cache.Get(key)
|
||||
if err != nil {
|
||||
b.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
7
pkg/types/cache/config.go
vendored
7
pkg/types/cache/config.go
vendored
@ -1,7 +0,0 @@
|
||||
package cache
|
||||
|
||||
import "time"
|
||||
|
||||
type Config struct {
|
||||
TTL time.Duration
|
||||
}
|
||||
8
pkg/types/cache/errors.go
vendored
8
pkg/types/cache/errors.go
vendored
@ -1,8 +0,0 @@
|
||||
package cache
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrKeyNotFound = errors.New("key not found")
|
||||
ErrKeyExpired = errors.New("key expired")
|
||||
)
|
||||
@ -1,28 +0,0 @@
|
||||
package types
|
||||
|
||||
func AsPointer[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
func OrDefault[T any](v *T, def T) T {
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
return *v
|
||||
}
|
||||
|
||||
// ZeroDefault returns the default value if the given value is zero, otherwise it returns the value.
|
||||
//
|
||||
// Parameters:
|
||||
// - v: The value to check.
|
||||
// - def: The default value to return if v is zero.
|
||||
//
|
||||
// Returns:
|
||||
// - The default value if v is zero, otherwise the value.
|
||||
func ZeroDefault[T comparable](v T, def T) T {
|
||||
zero := new(T)
|
||||
if v == *zero {
|
||||
return def
|
||||
}
|
||||
return v
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestZeroDefault(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
def string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "String zero value",
|
||||
value: "",
|
||||
def: "default",
|
||||
want: "default",
|
||||
},
|
||||
{
|
||||
name: "String non-zero value",
|
||||
value: "value",
|
||||
def: "default",
|
||||
want: "value",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := ZeroDefault(tt.value, tt.def)
|
||||
if got != tt.want {
|
||||
t.Errorf("ZeroDefault() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user