Files
portainer/api/database/boltdb/tx_test.go
T
2026-05-25 22:57:38 -03:00

161 lines
4.1 KiB
Go

package boltdb
import (
"errors"
"strconv"
"testing"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/stretchr/testify/require"
)
const testBucketName = "test-bucket"
const testId = 1234
type testStruct struct {
Key string
Value string
}
func TestTxs(t *testing.T) {
t.Parallel()
conn := DbConnection{Path: t.TempDir()}
err := conn.Open()
require.NoError(t, err)
t.Cleanup(func() {
err := conn.Close()
require.NoError(t, err)
})
// Error propagation
err = conn.UpdateTx(func(tx portainer.Transaction) error {
return errors.New("this is an error")
})
require.Error(t, err)
// Create an object
newObj := testStruct{Key: "key", Value: "value"}
err = conn.UpdateTx(func(tx portainer.Transaction) error {
if err := tx.SetServiceName(testBucketName); err != nil {
return err
}
return tx.CreateObjectWithId(testBucketName, testId, newObj)
})
require.NoError(t, err)
obj := testStruct{}
err = conn.ViewTx(func(tx portainer.Transaction) error {
return tx.GetObject(testBucketName, conn.ConvertToKey(testId), &obj)
})
require.NoError(t, err)
if obj.Key != newObj.Key || obj.Value != newObj.Value {
t.Fatalf("expected %s:%s, got %s:%s instead", newObj.Key, newObj.Value, obj.Key, obj.Value)
}
// Update an object
updatedObj := testStruct{Key: "updated-key", Value: "updated-value"}
err = conn.UpdateTx(func(tx portainer.Transaction) error {
return tx.UpdateObject(testBucketName, conn.ConvertToKey(testId), &updatedObj)
})
require.NoError(t, err)
err = conn.ViewTx(func(tx portainer.Transaction) error {
return tx.GetObject(testBucketName, conn.ConvertToKey(testId), &obj)
})
require.NoError(t, err)
if obj.Key != updatedObj.Key || obj.Value != updatedObj.Value {
t.Fatalf("expected %s:%s, got %s:%s instead", updatedObj.Key, updatedObj.Value, obj.Key, obj.Value)
}
// Delete an object
err = conn.UpdateTx(func(tx portainer.Transaction) error {
return tx.DeleteObject(testBucketName, conn.ConvertToKey(testId))
})
require.NoError(t, err)
err = conn.ViewTx(func(tx portainer.Transaction) error {
return tx.GetObject(testBucketName, conn.ConvertToKey(testId), &obj)
})
require.True(t, dataservices.IsErrObjectNotFound(err))
// Get next identifier
err = conn.UpdateTx(func(tx portainer.Transaction) error {
id1 := tx.GetNextIdentifier(testBucketName)
id2 := tx.GetNextIdentifier(testBucketName)
if id1+1 != id2 {
return errors.New("unexpected identifier sequence")
}
return nil
})
require.NoError(t, err)
// Try to write in a read transaction
err = conn.ViewTx(func(tx portainer.Transaction) error {
return tx.CreateObjectWithId(testBucketName, testId, newObj)
})
require.Error(t, err)
}
func BenchmarkGetAll(b *testing.B) {
const endpointBucket = "endpoints"
const n = 10000
conn := DbConnection{Path: b.TempDir()}
err := conn.Open()
require.NoError(b, err)
b.Cleanup(func() {
err := conn.Close()
require.NoError(b, err)
})
err = conn.UpdateTx(func(tx portainer.Transaction) error {
if err := tx.SetServiceName(endpointBucket); err != nil {
return err
}
for i := 1; i <= n; i++ {
ep := portainer.Endpoint{
ID: portainer.EndpointID(i),
Name: "env-" + strconv.Itoa(i),
Type: portainer.DockerEnvironment,
URL: "tcp://192.168.1." + strconv.Itoa(i%254+1) + ":2375",
PublicURL: "https://env-" + strconv.Itoa(i) + ".example.com",
GroupID: portainer.EndpointGroupID(i%10 + 1),
TagIDs: []portainer.TagID{portainer.TagID(i%5 + 1), portainer.TagID(i%3 + 1)},
LastCheckInDate: int64(i) * 1000,
EdgeID: "edge-" + strconv.Itoa(i),
}
if err := tx.CreateObjectWithId(endpointBucket, i, &ep); err != nil {
return err
}
}
return nil
})
require.NoError(b, err)
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
var collection []portainer.Endpoint
if err := conn.ViewTx(func(tx portainer.Transaction) error {
return tx.GetAll(endpointBucket, new(portainer.Endpoint), dataservices.AppendFn(&collection))
}); err != nil {
b.Fatal(err)
}
}
}