refactor: use pinned golangci-lint Docker image for linting
All checks were successful
check / check (push) Successful in 1m37s
All checks were successful
check / check (push) Successful in 1m37s
Refactor Dockerfile to use a separate lint stage with a pinned golangci-lint v2.11.3 Docker image instead of installing golangci-lint via curl in the builder stage. This follows the pattern used by sneak/pixa. Changes: - Dockerfile: separate lint stage using golangci/golangci-lint:v2.11.3 (Debian-based, pinned by sha256) with COPY --from=lint dependency - Bump Go from 1.24 to 1.26.1 (golang:1.26.1-bookworm, pinned) - Bump golangci-lint from v1.64.8 to v2.11.3 - Migrate .golangci.yml from v1 to v2 format (same linters, format only) - All Docker images pinned by sha256 digest - Fix all lint issues from the v2 linter upgrade: - Add package comments to all packages - Add doc comments to all exported types, functions, and methods - Fix unchecked errors (errcheck) - Fix unused parameters (revive) - Fix gosec warnings (MaxBytesReader for form parsing) - Fix staticcheck suggestions (fmt.Fprintf instead of WriteString) - Rename DeliveryTask to Task to avoid stutter (delivery.Task) - Rename shadowed builtin 'max' parameter - Update README.md version requirements
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package database
|
||||
package database_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,23 +10,29 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/fx/fxtest"
|
||||
"gorm.io/gorm"
|
||||
"sneak.berlin/go/webhooker/internal/config"
|
||||
"sneak.berlin/go/webhooker/internal/database"
|
||||
"sneak.berlin/go/webhooker/internal/globals"
|
||||
"sneak.berlin/go/webhooker/internal/logger"
|
||||
)
|
||||
|
||||
func setupTestWebhookDBManager(t *testing.T) (*WebhookDBManager, *fxtest.Lifecycle) {
|
||||
func setupTestWebhookDBManager(
|
||||
t *testing.T,
|
||||
) (*database.WebhookDBManager, *fxtest.Lifecycle) {
|
||||
t.Helper()
|
||||
|
||||
lc := fxtest.NewLifecycle(t)
|
||||
|
||||
globals.Appname = "webhooker-test"
|
||||
globals.Version = "test"
|
||||
g := &globals.Globals{
|
||||
Appname: "webhooker-test",
|
||||
Version: "test",
|
||||
}
|
||||
|
||||
g, err := globals.New(lc)
|
||||
require.NoError(t, err)
|
||||
|
||||
l, err := logger.New(lc, logger.LoggerParams{Globals: g})
|
||||
l, err := logger.New(
|
||||
lc,
|
||||
logger.LoggerParams{Globals: g},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
dataDir := filepath.Join(t.TempDir(), "events")
|
||||
@@ -35,19 +41,25 @@ func setupTestWebhookDBManager(t *testing.T) (*WebhookDBManager, *fxtest.Lifecyc
|
||||
DataDir: dataDir,
|
||||
}
|
||||
|
||||
mgr, err := NewWebhookDBManager(lc, WebhookDBManagerParams{
|
||||
Config: cfg,
|
||||
Logger: l,
|
||||
})
|
||||
mgr, err := database.NewWebhookDBManager(
|
||||
lc,
|
||||
database.WebhookDBManagerParams{
|
||||
Config: cfg,
|
||||
Logger: l,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return mgr, lc
|
||||
}
|
||||
|
||||
func TestWebhookDBManager_CreateAndGetDB(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mgr, lc := setupTestWebhookDBManager(t)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, lc.Start(ctx))
|
||||
|
||||
defer func() { require.NoError(t, lc.Stop(ctx)) }()
|
||||
|
||||
webhookID := uuid.New().String()
|
||||
@@ -68,7 +80,7 @@ func TestWebhookDBManager_CreateAndGetDB(t *testing.T) {
|
||||
require.NotNil(t, db)
|
||||
|
||||
// Verify we can write an event
|
||||
event := &Event{
|
||||
event := &database.Event{
|
||||
WebhookID: webhookID,
|
||||
EntrypointID: uuid.New().String(),
|
||||
Method: "POST",
|
||||
@@ -80,27 +92,35 @@ func TestWebhookDBManager_CreateAndGetDB(t *testing.T) {
|
||||
assert.NotEmpty(t, event.ID)
|
||||
|
||||
// Verify we can read it back
|
||||
var readEvent Event
|
||||
require.NoError(t, db.First(&readEvent, "id = ?", event.ID).Error)
|
||||
var readEvent database.Event
|
||||
|
||||
require.NoError(
|
||||
t,
|
||||
db.First(&readEvent, "id = ?", event.ID).Error,
|
||||
)
|
||||
assert.Equal(t, webhookID, readEvent.WebhookID)
|
||||
assert.Equal(t, "POST", readEvent.Method)
|
||||
assert.Equal(t, `{"test": true}`, readEvent.Body)
|
||||
}
|
||||
|
||||
func TestWebhookDBManager_DeleteDB(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mgr, lc := setupTestWebhookDBManager(t)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, lc.Start(ctx))
|
||||
|
||||
defer func() { require.NoError(t, lc.Stop(ctx)) }()
|
||||
|
||||
webhookID := uuid.New().String()
|
||||
|
||||
// Create the DB and write some data
|
||||
require.NoError(t, mgr.CreateDB(webhookID))
|
||||
|
||||
db, err := mgr.GetDB(webhookID)
|
||||
require.NoError(t, err)
|
||||
|
||||
event := &Event{
|
||||
event := &database.Event{
|
||||
WebhookID: webhookID,
|
||||
EntrypointID: uuid.New().String(),
|
||||
Method: "POST",
|
||||
@@ -116,15 +136,19 @@ func TestWebhookDBManager_DeleteDB(t *testing.T) {
|
||||
assert.False(t, mgr.DBExists(webhookID))
|
||||
|
||||
// Verify the file is actually gone from disk
|
||||
dbPath := mgr.dbPath(webhookID)
|
||||
dbPath := mgr.DBPath(webhookID)
|
||||
|
||||
_, err = os.Stat(dbPath)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
}
|
||||
|
||||
func TestWebhookDBManager_LazyCreation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mgr, lc := setupTestWebhookDBManager(t)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, lc.Start(ctx))
|
||||
|
||||
defer func() { require.NoError(t, lc.Stop(ctx)) }()
|
||||
|
||||
webhookID := uuid.New().String()
|
||||
@@ -139,9 +163,12 @@ func TestWebhookDBManager_LazyCreation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWebhookDBManager_DeliveryWorkflow(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mgr, lc := setupTestWebhookDBManager(t)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, lc.Start(ctx))
|
||||
|
||||
defer func() { require.NoError(t, lc.Stop(ctx)) }()
|
||||
|
||||
webhookID := uuid.New().String()
|
||||
@@ -150,8 +177,23 @@ func TestWebhookDBManager_DeliveryWorkflow(t *testing.T) {
|
||||
db, err := mgr.GetDB(webhookID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an event
|
||||
event := &Event{
|
||||
event, delivery := seedDeliveryWorkflow(
|
||||
t, db, webhookID, targetID,
|
||||
)
|
||||
|
||||
verifyPendingDeliveries(t, db, event)
|
||||
completeDelivery(t, db, delivery)
|
||||
verifyNoPending(t, db)
|
||||
}
|
||||
|
||||
func seedDeliveryWorkflow(
|
||||
t *testing.T,
|
||||
db *gorm.DB,
|
||||
webhookID, targetID string,
|
||||
) (*database.Event, *database.Delivery) {
|
||||
t.Helper()
|
||||
|
||||
event := &database.Event{
|
||||
WebhookID: webhookID,
|
||||
EntrypointID: uuid.New().String(),
|
||||
Method: "POST",
|
||||
@@ -161,25 +203,45 @@ func TestWebhookDBManager_DeliveryWorkflow(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, db.Create(event).Error)
|
||||
|
||||
// Create a delivery
|
||||
delivery := &Delivery{
|
||||
delivery := &database.Delivery{
|
||||
EventID: event.ID,
|
||||
TargetID: targetID,
|
||||
Status: DeliveryStatusPending,
|
||||
Status: database.DeliveryStatusPending,
|
||||
}
|
||||
require.NoError(t, db.Create(delivery).Error)
|
||||
|
||||
// Query pending deliveries
|
||||
var pending []Delivery
|
||||
require.NoError(t, db.Where("status = ?", DeliveryStatusPending).
|
||||
Preload("Event").
|
||||
Find(&pending).Error)
|
||||
return event, delivery
|
||||
}
|
||||
|
||||
func verifyPendingDeliveries(
|
||||
t *testing.T,
|
||||
db *gorm.DB,
|
||||
event *database.Event,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
var pending []database.Delivery
|
||||
|
||||
require.NoError(
|
||||
t,
|
||||
db.Where(
|
||||
"status = ?",
|
||||
database.DeliveryStatusPending,
|
||||
).Preload("Event").Find(&pending).Error,
|
||||
)
|
||||
require.Len(t, pending, 1)
|
||||
assert.Equal(t, event.ID, pending[0].EventID)
|
||||
assert.Equal(t, "POST", pending[0].Event.Method)
|
||||
}
|
||||
|
||||
// Create a delivery result
|
||||
result := &DeliveryResult{
|
||||
func completeDelivery(
|
||||
t *testing.T,
|
||||
db *gorm.DB,
|
||||
delivery *database.Delivery,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
result := &database.DeliveryResult{
|
||||
DeliveryID: delivery.ID,
|
||||
AttemptNum: 1,
|
||||
Success: true,
|
||||
@@ -188,19 +250,40 @@ func TestWebhookDBManager_DeliveryWorkflow(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, db.Create(result).Error)
|
||||
|
||||
// Update delivery status
|
||||
require.NoError(t, db.Model(delivery).Update("status", DeliveryStatusDelivered).Error)
|
||||
require.NoError(
|
||||
t,
|
||||
db.Model(delivery).Update(
|
||||
"status",
|
||||
database.DeliveryStatusDelivered,
|
||||
).Error,
|
||||
)
|
||||
}
|
||||
|
||||
// Verify no more pending deliveries
|
||||
var stillPending []Delivery
|
||||
require.NoError(t, db.Where("status = ?", DeliveryStatusPending).Find(&stillPending).Error)
|
||||
func verifyNoPending(
|
||||
t *testing.T,
|
||||
db *gorm.DB,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
var stillPending []database.Delivery
|
||||
|
||||
require.NoError(
|
||||
t,
|
||||
db.Where(
|
||||
"status = ?",
|
||||
database.DeliveryStatusPending,
|
||||
).Find(&stillPending).Error,
|
||||
)
|
||||
assert.Empty(t, stillPending)
|
||||
}
|
||||
|
||||
func TestWebhookDBManager_MultipleWebhooks(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mgr, lc := setupTestWebhookDBManager(t)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, lc.Start(ctx))
|
||||
|
||||
defer func() { require.NoError(t, lc.Stop(ctx)) }()
|
||||
|
||||
webhook1 := uuid.New().String()
|
||||
@@ -212,34 +295,38 @@ func TestWebhookDBManager_MultipleWebhooks(t *testing.T) {
|
||||
|
||||
db1, err := mgr.GetDB(webhook1)
|
||||
require.NoError(t, err)
|
||||
|
||||
db2, err := mgr.GetDB(webhook2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Write events to each webhook's DB
|
||||
event1 := &Event{
|
||||
event1 := &database.Event{
|
||||
WebhookID: webhook1,
|
||||
EntrypointID: uuid.New().String(),
|
||||
Method: "POST",
|
||||
Body: `{"webhook": 1}`,
|
||||
ContentType: "application/json",
|
||||
}
|
||||
event2 := &Event{
|
||||
event2 := &database.Event{
|
||||
WebhookID: webhook2,
|
||||
EntrypointID: uuid.New().String(),
|
||||
Method: "PUT",
|
||||
Body: `{"webhook": 2}`,
|
||||
ContentType: "application/json",
|
||||
}
|
||||
|
||||
require.NoError(t, db1.Create(event1).Error)
|
||||
require.NoError(t, db2.Create(event2).Error)
|
||||
|
||||
// Verify isolation: each DB only has its own events
|
||||
var count1 int64
|
||||
db1.Model(&Event{}).Count(&count1)
|
||||
|
||||
db1.Model(&database.Event{}).Count(&count1)
|
||||
assert.Equal(t, int64(1), count1)
|
||||
|
||||
var count2 int64
|
||||
db2.Model(&Event{}).Count(&count2)
|
||||
|
||||
db2.Model(&database.Event{}).Count(&count2)
|
||||
assert.Equal(t, int64(1), count2)
|
||||
|
||||
// Delete webhook1's DB, webhook2 should be unaffected
|
||||
@@ -248,25 +335,31 @@ func TestWebhookDBManager_MultipleWebhooks(t *testing.T) {
|
||||
assert.True(t, mgr.DBExists(webhook2))
|
||||
|
||||
// webhook2's data should still be accessible
|
||||
var events []Event
|
||||
var events []database.Event
|
||||
|
||||
require.NoError(t, db2.Find(&events).Error)
|
||||
assert.Len(t, events, 1)
|
||||
assert.Equal(t, "PUT", events[0].Method)
|
||||
}
|
||||
|
||||
func TestWebhookDBManager_CloseAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mgr, lc := setupTestWebhookDBManager(t)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, lc.Start(ctx))
|
||||
|
||||
// Create a few DBs
|
||||
for i := 0; i < 3; i++ {
|
||||
require.NoError(t, mgr.CreateDB(uuid.New().String()))
|
||||
for range 3 {
|
||||
require.NoError(
|
||||
t,
|
||||
mgr.CreateDB(uuid.New().String()),
|
||||
)
|
||||
}
|
||||
|
||||
// CloseAll should close all connections without error
|
||||
require.NoError(t, mgr.CloseAll())
|
||||
|
||||
// Stop lifecycle (CloseAll already called, but shouldn't panic)
|
||||
// Stop lifecycle (CloseAll already called)
|
||||
require.NoError(t, lc.Stop(ctx))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user