Merged in feature/s3integration (pull request #47)

Set Up S3 integration

* cfginterfaceandfirsts3funcs

* addlocalstack

* generallypassesfullsuite

* addedmultipleattemptedpings

* cleanup

* stabiliseplusskip
This commit is contained in:
Michael McGuinness
2025-02-05 12:52:41 +00:00
parent 9254b91d45
commit 92334ad1dd
838 changed files with 139249 additions and 6671 deletions
+16 -10
View File
@@ -5,6 +5,8 @@ import (
"os"
"path"
"queryorchestration/internal/database/migrations"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/database"
"queryorchestration/internal/test"
"testing"
@@ -17,8 +19,9 @@ func TestRunMigrations(t *testing.T) {
}
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
@@ -32,14 +35,17 @@ func TestRunMigrations(t *testing.T) {
func TestRunMigrationsNoDB(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg.DBUser = "invalid_user"
cfg.DBSecret = "invalid_pass"
cfg.DBHost = "invalid_host"
cfg.DBPort = 5432
cfg.DBName = "invalid_name"
cfg.DBNoSSL = true
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
cfg.SetDBConfig(&database.DBConfig{
DBUser: "invalid_user",
DBSecret: "invalid_pass",
DBHost: "invalid_host",
DBPort: 5432,
DBName: "invalid_name",
DBNoSSL: true,
})
err := migrations.Run(ctx, cfg)
assert.Error(t, err)
+5 -3
View File
@@ -5,6 +5,7 @@ import (
"os"
"path"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"testing"
@@ -14,15 +15,16 @@ import (
func TestClient(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
id, err := queries.CreateClient(ctx, "example_client")
assert.NoError(t, err)
@@ -7,6 +7,7 @@ import (
"path"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"testing"
@@ -17,15 +18,16 @@ import (
func TestCollector(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
contextId, err := queries.CreateQuery(ctx, repository.QuerytypeContextFull)
assert.NoError(t, err)
@@ -5,6 +5,7 @@ import (
"os"
"path"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"testing"
@@ -14,15 +15,16 @@ import (
func TestDocument(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
clientId, err := queries.CreateClient(ctx, "example_client")
assert.NoError(t, err)
+5 -3
View File
@@ -5,6 +5,7 @@ import (
"os"
"path"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"testing"
@@ -14,15 +15,16 @@ import (
func TestJob(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
clientId, err := queries.CreateClient(ctx, "example_client")
assert.NoError(t, err)
+13 -9
View File
@@ -6,6 +6,7 @@ import (
"path"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"testing"
@@ -17,15 +18,16 @@ import (
func TestQueries(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
contextQueryID, err := queries.CreateQuery(ctx, repository.Querytype(repository.QuerytypeContextFull))
assert.NoError(t, err)
@@ -164,15 +166,16 @@ func TestQueries(t *testing.T) {
func TestQueryDependencyTree(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
contextQueryID, err := queries.CreateQuery(ctx, repository.Querytype(repository.QuerytypeContextFull))
assert.NoError(t, err)
@@ -234,15 +237,16 @@ func TestQueryDependencyTree(t *testing.T) {
func TestQueriesList(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
contextQueryID, err := queries.CreateQuery(ctx, repository.Querytype(repository.QuerytypeContextFull))
assert.NoError(t, err)
+13 -9
View File
@@ -5,6 +5,7 @@ import (
"os"
"path"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"testing"
@@ -15,15 +16,16 @@ import (
func TestResults(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
jsonQueryID, err := queries.CreateQuery(ctx, repository.Querytype(repository.QuerytypeJsonExtractor))
assert.NoError(t, err)
@@ -73,15 +75,16 @@ func TestResults(t *testing.T) {
func TestResultValues(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
jsonQueryID, err := queries.CreateQuery(ctx, repository.Querytype(repository.QuerytypeJsonExtractor))
assert.NoError(t, err)
@@ -170,15 +173,16 @@ func TestResultValues(t *testing.T) {
func TestUnsyncedQueries(t *testing.T) {
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
})
defer cleanup()
queries := cfg.DBQueries
queries := cfg.GetDBQueries()
clientId, err := queries.CreateClient(ctx, "example_client")
assert.NoError(t, err)
+10 -12
View File
@@ -4,7 +4,6 @@ import (
"context"
"os"
"path"
"queryorchestration/internal/server"
"queryorchestration/internal/test"
queuemock "queryorchestration/mocks/queue"
runnermock "queryorchestration/mocks/runner"
@@ -21,17 +20,16 @@ func TestNew(t *testing.T) {
t.Skip("Skipping long test in short mode")
}
ctx := context.Background()
sccfg := test.CreateBaseConfig()
sccfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &BaseConfig{
BaseConfig: server.BaseConfig{
BaseConfig: *sccfg,
},
}
qcleanup := test.CreateQueueClient(t, ctx, &test.CreateQueueConfig{
Cfg: &cfg.BaseConfig.BaseConfig,
cfg := &BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, acleanup := test.CreateAWSContainer(t, ctx, &test.CreateAWSConfig{
Cfg: cfg,
})
defer qcleanup()
defer acleanup()
err := cfg.SetQueueClient(ctx)
assert.NoError(t, err)
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
})
@@ -43,7 +41,7 @@ func TestNew(t *testing.T) {
cfg.ControllerFunc = func() Controller {
return runnermock.NewMockController(t)
}
cfg.QueueURL = test.CreateQueue(t, ctx, &cfg.BaseConfig.BaseConfig, "queueName")
cfg.QueueURL = test.CreateQueue(t, ctx, cfg, "queueName")
srvPtr, err := New(ctx, cfg)
assert.NotNil(t, srvPtr)
+3 -5
View File
@@ -18,11 +18,9 @@ func TestNew(t *testing.T) {
}
ctx := context.Background()
sccfg := test.CreateBaseConfig()
sccfg.BasePath = path.Join(os.Getenv("PWD"), "../..")
cfg := &server.BaseConfig{
BaseConfig: *sccfg,
}
cfg := &server.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
+5 -13
View File
@@ -4,8 +4,6 @@ import (
"context"
"os"
"path"
"queryorchestration/internal/server"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"testing"
@@ -21,13 +19,9 @@ func TestNew(t *testing.T) {
}
ctx := context.Background()
sccfg := test.CreateBaseConfig()
sccfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &BaseConfig{
BaseConfig: server.BaseConfig{
BaseConfig: *sccfg,
},
}
cfg := &BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
})
@@ -37,11 +31,9 @@ func TestNew(t *testing.T) {
return nil, nil
}
cfg.RegisterHandlersFunc = registerHandlers
errInitializingConfig := serviceconfig.InitializeConfig(cfg)
assert.NoError(t, errInitializingConfig)
serverInstance, errCreatingServer := New(ctx, cfg)
assert.NoError(t, errCreatingServer)
serverInstance, err := New(ctx, cfg)
assert.NoError(t, err)
assert.Equal(t, 8080, serverInstance.port)
assert.Equal(t, "0.0.0.0", serverInstance.host)
assert.Equal(t, "0.0.0.0:8080", serverInstance.address)
+38 -5
View File
@@ -1,10 +1,43 @@
package aws
type AWSConfig struct {
AWSKeyID string `env:"AWS_ACCESS_KEY_ID,required,notEmpty"`
AWSSecretKey string `env:"AWS_SECRET_ACCESS_KEY,required,notEmpty"`
AWSRegion string `env:"AWS_REGION,required,notEmpty"`
AWSSessionToken string `env:"AWS_SESSION_TOKEN"`
AWSKeyID string `env:"AWS_ACCESS_KEY_ID,required,notEmpty"`
AWSSecretKey string `env:"AWS_SECRET_ACCESS_KEY,required,notEmpty"`
AWSRegion string `env:"AWS_REGION,required,notEmpty"`
AWSDefaultRegion string `env:"AWS_DEFAULT_REGION"`
AWSSessionToken string `env:"AWS_SESSION_TOKEN"`
AWSEndpointUrl string `env:"AWS_ENDPOINT_URL"`
}
type ConfigProvider interface{}
type ConfigProvider interface {
GetAWSKeyID() string
GetAWSSecretKey() string
GetAWSRegion() string
GetAWSSessionToken() string
GetAWSEndpoint() string
SetAWSEndpoint(string)
}
func (c *AWSConfig) SetAWSEndpoint(e string) {
c.AWSEndpointUrl = e
}
func (c *AWSConfig) GetAWSEndpoint() string {
return c.AWSEndpointUrl
}
func (c *AWSConfig) GetAWSKeyID() string {
return c.AWSKeyID
}
func (c *AWSConfig) GetAWSSecretKey() string {
return c.AWSSecretKey
}
func (c *AWSConfig) GetAWSSessionToken() string {
return c.AWSSessionToken
}
func (c *AWSConfig) GetAWSRegion() string {
return c.AWSRegion
}
+77
View File
@@ -0,0 +1,77 @@
package aws_test
import (
"queryorchestration/internal/serviceconfig/aws"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetAWSKeyID(t *testing.T) {
cfg := aws.AWSConfig{}
name := cfg.GetAWSKeyID()
assert.Equal(t, "", name)
cfg.AWSKeyID = "name"
name = cfg.GetAWSKeyID()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.AWSKeyID, name)
}
func TestGetAWSSecretKey(t *testing.T) {
cfg := aws.AWSConfig{}
name := cfg.GetAWSSecretKey()
assert.Equal(t, "", name)
cfg.AWSSecretKey = "name"
name = cfg.GetAWSSecretKey()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.AWSSecretKey, name)
}
func TestGetAWSRegion(t *testing.T) {
cfg := aws.AWSConfig{}
name := cfg.GetAWSRegion()
assert.Equal(t, "", name)
cfg.AWSRegion = "name"
name = cfg.GetAWSRegion()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.AWSRegion, name)
}
func TestGetAWSSessionToken(t *testing.T) {
cfg := aws.AWSConfig{}
name := cfg.GetAWSSessionToken()
assert.Equal(t, "", name)
cfg.AWSSessionToken = "name"
name = cfg.GetAWSSessionToken()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.AWSSessionToken, name)
}
func TestGetAWSEndpoint(t *testing.T) {
cfg := aws.AWSConfig{}
name := cfg.GetAWSEndpoint()
assert.Equal(t, "", name)
cfg.AWSEndpointUrl = "name"
name = cfg.GetAWSEndpoint()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.AWSEndpointUrl, name)
}
func TestSetAWSEndpoint(t *testing.T) {
cfg := aws.AWSConfig{}
assert.Equal(t, "", cfg.AWSEndpointUrl)
url := "/i/am/here"
cfg.SetAWSEndpoint(url)
assert.Equal(t, url, cfg.AWSEndpointUrl)
}
+10
View File
@@ -48,7 +48,9 @@ type ConfigProvider interface {
aws.ConfigProvider
queue.ConfigProvider
GetBasePath() string
SetBasePath(string)
SetDBConfig(*database.DBConfig)
SetAWSConfig(*aws.AWSConfig)
}
// Configuration Methods
@@ -123,6 +125,14 @@ func (b *BaseConfig) GetBasePath() string {
return b.Pwd
}
func (b *BaseConfig) SetBasePath(p string) {
b.BasePath = p
}
func (b *BaseConfig) SetDBConfig(cfg *database.DBConfig) {
b.DBConfig = *cfg
}
func (b *BaseConfig) SetAWSConfig(cfg *aws.AWSConfig) {
b.AWSConfig = *cfg
}
+17 -1
View File
@@ -2,6 +2,7 @@ package serviceconfig
import (
"os"
"queryorchestration/internal/serviceconfig/aws"
"queryorchestration/internal/serviceconfig/database"
"strings"
"testing"
@@ -146,10 +147,16 @@ func TestGetBasePath(t *testing.T) {
Pwd: "pwd_path",
}
assert.Equal(t, "pwd_path", cfg.GetBasePath())
cfg.BasePath = "base_path"
cfg.SetBasePath("base_path")
assert.Equal(t, "base_path", cfg.GetBasePath())
}
func TestSetBasePath(t *testing.T) {
cfg := &BaseConfig{}
cfg.SetBasePath("/i/am/here")
assert.Equal(t, "/i/am/here", cfg.BasePath)
}
func TestSetDBConfig(t *testing.T) {
cfg := &BaseConfig{}
dbcfg := database.DBConfig{
@@ -158,3 +165,12 @@ func TestSetDBConfig(t *testing.T) {
cfg.SetDBConfig(&dbcfg)
assert.Equal(t, dbcfg, cfg.DBConfig)
}
func TestSetAWSConfig(t *testing.T) {
cfg := &BaseConfig{}
acfg := aws.AWSConfig{
AWSRegion: "user",
}
cfg.SetAWSConfig(&acfg)
assert.Equal(t, acfg, cfg.AWSConfig)
}
+30 -4
View File
@@ -26,12 +26,18 @@ type ConfigProvider interface {
GetDBURI() string
GetDBOpts() map[string]string
GetDBOptsString() string
GetDBHost() string
GetDBPort() int
GetDBUser() string
GetDBSecret() string
IsDBNoSSL() bool
GetDBName() string
GetDBDriver() string
SetDBPoolConfig() error
SetDBPool(ctx context.Context) error
GetDBQueries() *repository.Queries
GetDBPool() Pool
DBPing(context.Context) error
ExecuteDBTransaction(context.Context, func(context.Context, *repository.Queries) error) error
}
@@ -69,10 +75,30 @@ func (b *DBConfig) GetDBBaseURI() string {
return fmt.Sprintf("%s://%s:%s@%s:%d/", b.GetDBDriver(), b.DBUser, b.DBSecret, b.DBHost, b.DBPort)
}
func (b *DBConfig) GetDBName() string {
return b.DBName
}
func (b *DBConfig) GetDBURI() string {
return fmt.Sprintf("%s%s?%s", b.GetDBBaseURI(), b.DBName, b.GetDBOptsString())
}
func (b *DBConfig) GetDBHost() string {
return b.DBHost
}
func (b *DBConfig) GetDBPort() int {
return b.DBPort
}
func (b *DBConfig) GetDBUser() string {
return b.DBUser
}
func (b *DBConfig) GetDBSecret() string {
return b.DBSecret
}
func (b *DBConfig) IsDBNoSSL() bool {
return b.DBNoSSL
}
func (b *DBConfig) GetDBName() string {
return b.DBName
}
@@ -66,6 +66,66 @@ func TestGetDBURI(t *testing.T) {
assert.Equal(t, "postgres://user:pass@host:123/name?sslmode=disable", uri)
}
func TestGetDBHost(t *testing.T) {
cfg := database.DBConfig{}
name := cfg.GetDBHost()
assert.Equal(t, "", name)
cfg.DBHost = "name"
name = cfg.GetDBHost()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.DBHost, name)
}
func TestGetDBPort(t *testing.T) {
cfg := database.DBConfig{}
name := cfg.GetDBPort()
assert.Equal(t, 0, name)
cfg.DBPort = 8080
name = cfg.GetDBPort()
assert.Equal(t, 8080, name)
assert.Equal(t, cfg.DBPort, name)
}
func TestGetDBUser(t *testing.T) {
cfg := database.DBConfig{}
name := cfg.GetDBUser()
assert.Equal(t, "", name)
cfg.DBUser = "name"
name = cfg.GetDBUser()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.DBUser, name)
}
func TestGetDBSecret(t *testing.T) {
cfg := database.DBConfig{}
name := cfg.GetDBSecret()
assert.Equal(t, "", name)
cfg.DBSecret = "name"
name = cfg.GetDBSecret()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.DBSecret, name)
}
func TestIsDBNoSSL(t *testing.T) {
cfg := database.DBConfig{}
name := cfg.IsDBNoSSL()
assert.False(t, name)
cfg.DBNoSSL = true
name = cfg.IsDBNoSSL()
assert.True(t, name)
assert.Equal(t, cfg.DBNoSSL, name)
}
func TestGetDBName(t *testing.T) {
cfg := database.DBConfig{}
+29 -6
View File
@@ -2,9 +2,10 @@ package database
import (
"context"
"errors"
"fmt"
"log/slog"
"queryorchestration/internal/database/repository"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
@@ -61,13 +62,35 @@ func (b *DBConfig) SetDBPool(ctx context.Context) error {
return err
}
err = pool.Ping(ctx)
if err != nil {
return errors.New("unable to ping database")
}
b.DBPool = pool
b.DBQueries = repository.New(b.DBPool)
err = b.DBPing(ctx)
if err != nil {
return err
}
return nil
}
func (b *DBConfig) DBPing(ctx context.Context) error {
timeout := time.After(30 * time.Second)
tick := time.NewTicker(2 * time.Second)
defer tick.Stop()
for {
select {
case <-timeout:
return fmt.Errorf("unable to ping database")
case <-tick.C:
err := b.DBPool.Ping(ctx)
if err == nil {
slog.Info("Successful database ping")
return nil
}
slog.Info("Attempted database ping")
case <-ctx.Done():
return ctx.Err()
}
}
}
+5 -3
View File
@@ -5,6 +5,7 @@ import (
"os"
"path"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/database"
"queryorchestration/internal/test"
"testing"
@@ -19,8 +20,9 @@ func TestSetDBPool(t *testing.T) {
}
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../../.."))
_, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
RunMigrations: true,
@@ -29,7 +31,7 @@ func TestSetDBPool(t *testing.T) {
err := cfg.SetDBPool(ctx)
assert.NoError(t, err)
assert.NotNil(t, cfg.DBPool)
assert.NotNil(t, cfg.GetDBPool())
}
func TestGetDBPoolConfig(t *testing.T) {
@@ -0,0 +1,50 @@
package objectstore
import (
"context"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
// S3Client defines the interface for interacting with S3
type S3Client interface {
// Bucket Operations
CreateBucket(ctx context.Context, params *s3.CreateBucketInput, opts ...func(*s3.Options)) (*s3.CreateBucketOutput, error)
DeleteBucket(ctx context.Context, params *s3.DeleteBucketInput, opts ...func(*s3.Options)) (*s3.DeleteBucketOutput, error)
HeadBucket(ctx context.Context, params *s3.HeadBucketInput, opts ...func(*s3.Options)) (*s3.HeadBucketOutput, error)
ListBuckets(ctx context.Context, params *s3.ListBucketsInput, opts ...func(*s3.Options)) (*s3.ListBucketsOutput, error)
// Object Operations
PutObject(ctx context.Context, params *s3.PutObjectInput, opts ...func(*s3.Options)) (*s3.PutObjectOutput, error)
GetObject(ctx context.Context, params *s3.GetObjectInput, opts ...func(*s3.Options)) (*s3.GetObjectOutput, error)
DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, opts ...func(*s3.Options)) (*s3.DeleteObjectOutput, error)
DeleteObjects(ctx context.Context, params *s3.DeleteObjectsInput, opts ...func(*s3.Options)) (*s3.DeleteObjectsOutput, error)
CopyObject(ctx context.Context, params *s3.CopyObjectInput, opts ...func(*s3.Options)) (*s3.CopyObjectOutput, error)
HeadObject(ctx context.Context, params *s3.HeadObjectInput, opts ...func(*s3.Options)) (*s3.HeadObjectOutput, error)
ListObjects(ctx context.Context, params *s3.ListObjectsInput, opts ...func(*s3.Options)) (*s3.ListObjectsOutput, error)
ListObjectsV2(ctx context.Context, params *s3.ListObjectsV2Input, opts ...func(*s3.Options)) (*s3.ListObjectsV2Output, error)
// Multipart Upload Operations
CreateMultipartUpload(ctx context.Context, params *s3.CreateMultipartUploadInput, opts ...func(*s3.Options)) (*s3.CreateMultipartUploadOutput, error)
UploadPart(ctx context.Context, params *s3.UploadPartInput, opts ...func(*s3.Options)) (*s3.UploadPartOutput, error)
CompleteMultipartUpload(ctx context.Context, params *s3.CompleteMultipartUploadInput, opts ...func(*s3.Options)) (*s3.CompleteMultipartUploadOutput, error)
AbortMultipartUpload(ctx context.Context, params *s3.AbortMultipartUploadInput, opts ...func(*s3.Options)) (*s3.AbortMultipartUploadOutput, error)
ListMultipartUploads(ctx context.Context, params *s3.ListMultipartUploadsInput, opts ...func(*s3.Options)) (*s3.ListMultipartUploadsOutput, error)
ListParts(ctx context.Context, params *s3.ListPartsInput, opts ...func(*s3.Options)) (*s3.ListPartsOutput, error)
// Tagging Operations
GetObjectTagging(ctx context.Context, params *s3.GetObjectTaggingInput, opts ...func(*s3.Options)) (*s3.GetObjectTaggingOutput, error)
PutObjectTagging(ctx context.Context, params *s3.PutObjectTaggingInput, opts ...func(*s3.Options)) (*s3.PutObjectTaggingOutput, error)
DeleteObjectTagging(ctx context.Context, params *s3.DeleteObjectTaggingInput, opts ...func(*s3.Options)) (*s3.DeleteObjectTaggingOutput, error)
// ACL Operations
GetObjectAcl(ctx context.Context, params *s3.GetObjectAclInput, opts ...func(*s3.Options)) (*s3.GetObjectAclOutput, error)
PutObjectAcl(ctx context.Context, params *s3.PutObjectAclInput, opts ...func(*s3.Options)) (*s3.PutObjectAclOutput, error)
GetBucketAcl(ctx context.Context, params *s3.GetBucketAclInput, opts ...func(*s3.Options)) (*s3.GetBucketAclOutput, error)
PutBucketAcl(ctx context.Context, params *s3.PutBucketAclInput, opts ...func(*s3.Options)) (*s3.PutBucketAclOutput, error)
// Lifecycle Operations
GetBucketLifecycleConfiguration(ctx context.Context, params *s3.GetBucketLifecycleConfigurationInput, opts ...func(*s3.Options)) (*s3.GetBucketLifecycleConfigurationOutput, error)
PutBucketLifecycleConfiguration(ctx context.Context, params *s3.PutBucketLifecycleConfigurationInput, opts ...func(*s3.Options)) (*s3.PutBucketLifecycleConfigurationOutput, error)
DeleteBucketLifecycle(ctx context.Context, params *s3.DeleteBucketLifecycleInput, opts ...func(*s3.Options)) (*s3.DeleteBucketLifecycleOutput, error)
}
@@ -0,0 +1,94 @@
package objectstore
import (
"context"
"fmt"
"log/slog"
awsc "queryorchestration/internal/serviceconfig/aws"
"time"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
type ObjectStoreConfig struct {
BucketName string `env:"BUCKET_NAME,required,notEmpty"`
AWSEndpointUrlS3 string `env:"AWS_ENDPOINT_URL_S3"`
AWSS3UsePathStyle bool `env:"AWS_S3_USE_PATH_STYLE" envDefault:"false"`
StoreClient S3Client
}
type ConfigProvider interface {
awsc.ConfigProvider
SetStoreClient(context.Context) error
SetStoreClientAndPingByName(context.Context, string) error
GetStoreClient() S3Client
PingStoreByName(context.Context, string) error
SetS3Endpoint(string)
GetS3Endpoint() string
SetS3UsePathStyle(bool)
}
func (c *ObjectStoreConfig) SetStoreClientAndPingByName(ctx context.Context, name string) error {
err := c.SetStoreClient(ctx)
if err != nil {
return err
}
return c.PingStoreByName(ctx, name)
}
func (c *ObjectStoreConfig) SetStoreClient(ctx context.Context) error {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return fmt.Errorf("unable to load SDK config: %v", err)
}
c.StoreClient = s3.NewFromConfig(cfg, func(o *s3.Options) {
o.UsePathStyle = c.AWSS3UsePathStyle
})
return nil
}
func (c *ObjectStoreConfig) GetStoreClient() S3Client {
return c.StoreClient
}
func (c *ObjectStoreConfig) PingStoreByName(ctx context.Context, name string) error {
timeout := time.After(30 * time.Second)
tick := time.NewTicker(2 * time.Second)
defer tick.Stop()
input := &s3.HeadBucketInput{
Bucket: &name,
}
for {
select {
case <-timeout:
return fmt.Errorf("unable to ping bucket by name: %s", name)
case <-tick.C:
_, err := c.StoreClient.HeadBucket(ctx, input)
if err == nil {
slog.Info("Successful bucket ping", "name", name)
return nil
}
slog.Info("Attempted bucket ping", "name", name)
case <-ctx.Done():
return ctx.Err()
}
}
}
func (c *ObjectStoreConfig) GetS3Endpoint() string {
return c.AWSEndpointUrlS3
}
func (c *ObjectStoreConfig) SetS3Endpoint(e string) {
c.AWSEndpointUrlS3 = e
}
func (c *ObjectStoreConfig) SetS3UsePathStyle(e bool) {
c.AWSS3UsePathStyle = e
}
@@ -0,0 +1,51 @@
package objectstore_test
import (
"context"
"os"
objectstore "queryorchestration/internal/serviceconfig/objectstore"
objectstoremock "queryorchestration/mocks/objectstore"
"testing"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestGetStoreClient(t *testing.T) {
c := objectstore.ObjectStoreConfig{}
assert.Nil(t, c.GetStoreClient())
c.StoreClient = &s3.Client{}
assert.Equal(t, &s3.Client{}, c.GetStoreClient())
}
func TestStoreClient(t *testing.T) {
os.Clearenv()
ctx := context.Background()
c := objectstore.ObjectStoreConfig{}
err := c.SetStoreClient(ctx)
assert.NoError(t, err)
assert.NotNil(t, c.StoreClient)
}
func TestPingStoreByName(t *testing.T) {
ctx := context.Background()
mocks3 := objectstoremock.NewMockS3Client(t)
c := objectstore.ObjectStoreConfig{
StoreClient: mocks3,
}
name := "bucket-name"
mocks3.EXPECT().
HeadBucket(
mock.Anything,
mock.MatchedBy(func(in *s3.HeadBucketInput) bool {
return *in.Bucket == name
}),
mock.Anything,
).
Return(&s3.HeadBucketOutput{}, nil)
err := c.PingStoreByName(ctx, name)
assert.NoError(t, err)
}
+39 -12
View File
@@ -3,52 +3,79 @@ package queue
import (
"context"
"fmt"
"queryorchestration/internal/serviceconfig/aws"
"log/slog"
awsc "queryorchestration/internal/serviceconfig/aws"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
)
type QueueConfig struct {
aws.AWSConfig
EnableOtel bool `env:"ENABLE_OTEL" envDefault:"false"`
AWSEndpointUrlSQS string `env:"AWS_ENDPOINT_URL_SQS"`
QueueClient SQSClient
}
type ConfigProvider interface {
aws.ConfigProvider
awsc.ConfigProvider
SetQueueClient(context.Context) error
GetQueueClient() SQSClient
PingQueueByURL(context.Context, string) error
SendToQueue(context.Context, *SendParams) error
ReceiveFromQueue(context.Context, *ReceiveParams) (*sqs.ReceiveMessageOutput, error)
DeleteFromQueue(context.Context, *DeleteParams) error
SetSQSEndpoint(string)
GetSQSEndpoint() string
}
func (c *QueueConfig) GetQueueClient() SQSClient {
return c.QueueClient
}
func (c *QueueConfig) GetSQSEndpoint() string {
return c.AWSEndpointUrlSQS
}
func (c *QueueConfig) PingQueueByURL(ctx context.Context, url string) error {
_, err := c.QueueClient.GetQueueAttributes(ctx, &sqs.GetQueueAttributesInput{
QueueUrl: &url,
timeout := time.After(30 * time.Second)
tick := time.NewTicker(2 * time.Second)
defer tick.Stop()
input := &sqs.GetQueueAttributesInput{
QueueUrl: aws.String(url),
AttributeNames: []types.QueueAttributeName{types.QueueAttributeNameApproximateNumberOfMessages},
})
if err != nil {
return fmt.Errorf("unable to load ping queue by url (%s): %v", url, err)
}
return err
for {
select {
case <-timeout:
return fmt.Errorf("unable to ping queue by url: %s", url)
case <-tick.C:
r, err := c.QueueClient.GetQueueAttributes(ctx, input)
if err == nil {
slog.Info("Successful queue ping", "url", url, "approx_msgs", r.Attributes[string(types.QueueAttributeNameApproximateNumberOfMessages)])
return nil
}
slog.Info("Attempted queue ping", "url", url)
case <-ctx.Done():
return ctx.Err()
}
}
}
func (c *QueueConfig) SetQueueClient(ctx context.Context) error {
qcfg, err := config.LoadDefaultConfig(ctx)
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return fmt.Errorf("unable to load SDK config: %v", err)
}
c.QueueClient = sqs.NewFromConfig(qcfg)
c.QueueClient = sqs.NewFromConfig(cfg)
return nil
}
func (c *QueueConfig) SetSQSEndpoint(e string) {
c.AWSEndpointUrlSQS = e
}
@@ -49,3 +49,23 @@ func TestPingQueueByURL(t *testing.T) {
err := c.PingQueueByURL(ctx, url)
assert.NoError(t, err)
}
func TestGetSQSEndpoint(t *testing.T) {
cfg := queue.QueueConfig{}
name := cfg.GetSQSEndpoint()
assert.Equal(t, "", name)
cfg.AWSEndpointUrlSQS = "name"
name = cfg.GetSQSEndpoint()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.AWSEndpointUrlSQS, name)
}
func TestSetSQSEndpoint(t *testing.T) {
cfg := queue.QueueConfig{}
assert.Equal(t, "", cfg.AWSEndpointUrlSQS)
url := "/i/am/here"
cfg.SetSQSEndpoint(url)
assert.Equal(t, url, cfg.AWSEndpointUrlSQS)
}
@@ -0,0 +1,20 @@
package documentclean_test
import (
"queryorchestration/internal/serviceconfig/queue/documentclean"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetDocumentCleanURL(t *testing.T) {
cfg := documentclean.DocCleanConfig{}
name := cfg.GetDocumentCleanURL()
assert.Equal(t, "", name)
cfg.DocumentCleanURL = "name"
name = cfg.GetDocumentCleanURL()
assert.Equal(t, "name", name)
assert.Equal(t, cfg.DocumentCleanURL, name)
}
+2 -2
View File
@@ -15,8 +15,8 @@ func (c *QueueConfig) ReceiveFromQueue(ctx context.Context, params *ReceiveParam
return c.QueueClient.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{
QueueUrl: &params.QueueURL,
MaxNumberOfMessages: 1,
WaitTimeSeconds: 2,
VisibilityTimeout: 2,
WaitTimeSeconds: 20,
VisibilityTimeout: 30,
MessageAttributeNames: params.Attributes,
})
}
+100
View File
@@ -0,0 +1,100 @@
package test
import (
"context"
"fmt"
"queryorchestration/internal/serviceconfig"
"testing"
"github.com/docker/go-connections/nat"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)
type AWSContainerConfig struct {
Container testcontainers.Container
ExternalEndpoint string
NetworkEndpoint string
}
type CreateAWSConfig struct {
Network *testcontainers.DockerNetwork
Cfg serviceconfig.ConfigProvider
}
func CreateAWSContainer(t *testing.T, ctx context.Context, cfg *CreateAWSConfig) (*AWSContainerConfig, func()) {
alias := "localstack"
port, err := nat.NewPort("tcp", "4566")
if err != nil {
t.Fatalf("Failed to create port: %v", err)
}
req := testcontainers.ContainerRequest{
Image: "localstack/localstack:4.1.0",
Env: map[string]string{
"AWS_ACCESS_KEY_ID": cfg.Cfg.GetAWSKeyID(),
"AWS_SECRET_ACCESS_KEY": cfg.Cfg.GetAWSSecretKey(),
"AWS_SESSION_TOKEN": cfg.Cfg.GetAWSSessionToken(),
"AWS_REGION": cfg.Cfg.GetAWSRegion(),
"SERVICES": "s3,sqs",
"SKIP_SSL_CERT_DOWNLOAD": "1",
"LOCALSTACK_HOST": alias,
"SQS_ENDPOINT_STRATEGY": "path",
"EAGER_SERVICE_LOADING": "1",
},
ExposedPorts: []string{port.Port()},
WaitingFor: wait.ForAll(
// wait.ForExposedPort(),
wait.ForListeningPort(port),
wait.ForLog("Ready."),
),
}
if cfg.Network != nil {
req.Networks = []string{cfg.Network.Name}
req.NetworkAliases = map[string][]string{
cfg.Network.Name: {alias},
}
}
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
if err != nil {
t.Fatalf("Failed to start container: %v", err)
}
host, err := container.Host(ctx)
if err != nil {
t.Fatalf("Failed to extract host: %v", err)
}
mappedPort, err := container.MappedPort(ctx, port)
if err != nil {
t.Fatalf("Failed to extract port: %v", err)
}
extEndpoint := fmt.Sprintf("http://%s:%s", host, mappedPort.Port())
t.Setenv("AWS_ENDPOINT_URL", extEndpoint)
t.Setenv("AWS_ENDPOINT_URL_SQS", extEndpoint)
t.Setenv("AWS_ENDPOINT_URL_S3", extEndpoint)
cfg.Cfg.SetAWSEndpoint(extEndpoint)
var endpoint string
if cfg.Network != nil {
endpoint = fmt.Sprintf("http://%s:%s", alias, port.Port())
cfg.Cfg.SetAWSEndpoint(endpoint)
}
return &AWSContainerConfig{
Container: container,
ExternalEndpoint: extEndpoint,
NetworkEndpoint: endpoint,
}, func() {
err := container.Terminate(ctx)
if err != nil {
t.Error(err)
}
}
}
+26
View File
@@ -0,0 +1,26 @@
package test
import (
"context"
"queryorchestration/internal/serviceconfig"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCreateQueueContainer(t *testing.T) {
if testing.Short() {
t.Skip("Skipping long test in short mode")
}
ctx := context.Background()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
qcfg, cleanup := CreateAWSContainer(t, ctx, &CreateAWSConfig{
Cfg: cfg,
})
assert.NotNil(t, qcfg)
assert.NotNil(t, cleanup)
cleanup()
}
+14 -11
View File
@@ -21,7 +21,7 @@ type Container struct {
type containerConfig struct {
Name string
Cfg *serviceconfig.BaseConfig
Cfg serviceconfig.ConfigProvider
Network *testcontainers.DockerNetwork
Env map[string]string
WaitForMsg string
@@ -30,16 +30,19 @@ type containerConfig struct {
func createContainer(t *testing.T, ctx context.Context, cfg *containerConfig) (testcontainers.Container, func()) {
env := map[string]string{
"DB_USER": cfg.Cfg.DBUser,
"DB_PASS": cfg.Cfg.DBSecret,
"DB_HOST": cfg.Cfg.DBHost,
"DB_NAME": cfg.Cfg.DBName,
"DB_PORT": strconv.Itoa(cfg.Cfg.DBPort),
"DB_NOSSL": "true",
"AWS_ACCESS_KEY_ID": cfg.Cfg.AWSKeyID,
"AWS_SECRET_ACCESS_KEY": cfg.Cfg.AWSRegion,
"AWS_REGION": cfg.Cfg.AWSSecretKey,
"AWS_ENDPOINT_URL_SQS": cfg.Cfg.AWSEndpointUrlSQS,
"DB_USER": cfg.Cfg.GetDBUser(),
"DB_PASS": cfg.Cfg.GetDBSecret(),
"DB_HOST": cfg.Cfg.GetDBHost(),
"DB_NAME": cfg.Cfg.GetDBName(),
"DB_PORT": strconv.Itoa(cfg.Cfg.GetDBPort()),
"DB_NOSSL": strconv.FormatBool(cfg.Cfg.IsDBNoSSL()),
"AWS_ACCESS_KEY_ID": cfg.Cfg.GetAWSKeyID(),
"AWS_SECRET_ACCESS_KEY": cfg.Cfg.GetAWSSecretKey(),
"AWS_REGION": cfg.Cfg.GetAWSRegion(),
"AWS_ENDPOINT_URL": cfg.Cfg.GetAWSEndpoint(),
"AWS_ENDPOINT_URL_SQS": cfg.Cfg.GetAWSEndpoint(),
"AWS_ENDPOINT_URL_S3": cfg.Cfg.GetAWSEndpoint(),
"AWS_S3_USE_PATH_STYLE": strconv.FormatBool(true),
}
if cfg.Env != nil {
for k, v := range cfg.Env {
+3 -1
View File
@@ -2,6 +2,7 @@ package test
import (
"context"
"queryorchestration/internal/serviceconfig"
"testing"
"github.com/stretchr/testify/assert"
@@ -16,7 +17,8 @@ func TestCreateContainer(t *testing.T) {
ncfg, ncleanup := CreateNetwork(t, ctx)
defer ncleanup()
cfg := CreateBaseConfig()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
_, dbcleanup := CreateDB(t, ctx, &CreateDatabaseConfig{
Network: ncfg,
+15 -14
View File
@@ -86,26 +86,27 @@ func CreateDB(t *testing.T, ctx context.Context, cfg *CreateDatabaseConfig) (tes
t.Setenv("DB_HOST", dbcfg.DBHost)
t.Setenv("DB_PORT", fmt.Sprint(dbcfg.DBPort))
if cfg.Cfg != nil {
if cfg.RunMigrations {
cfg.Cfg.SetDBConfig(dbcfg)
if cfg.Cfg == nil {
SetCfgProvider(t, cfg.Cfg)
}
err := migrations.Run(ctx, cfg.Cfg)
if err != nil {
t.Fatal(err)
}
cfg.Cfg.SetDBConfig(dbcfg)
err = dbcfg.SetDBPool(ctx)
if err != nil {
t.Fatal(err)
}
if cfg.RunMigrations {
err := migrations.Run(ctx, cfg.Cfg)
if err != nil {
t.Fatal(err)
}
if cfg.Network != nil {
dbcfg.DBHost = alias
dbcfg.DBPort = port.Int()
err = cfg.Cfg.SetDBPool(ctx)
if err != nil {
t.Fatal(err)
}
}
if cfg.Network != nil {
dbcfg.DBHost = alias
dbcfg.DBPort = port.Int()
cfg.Cfg.SetDBConfig(dbcfg)
}
+9 -6
View File
@@ -4,6 +4,7 @@ import (
"context"
"os"
"path"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"testing"
@@ -16,12 +17,13 @@ func TestCreateDB(t *testing.T) {
}
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../.."))
dbcfg, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{Cfg: cfg})
assert.NotNil(t, dbcfg)
assert.Nil(t, cfg.DBPool)
assert.Nil(t, cfg.GetDBPool())
assert.NotNil(t, cleanup)
cleanup()
@@ -33,8 +35,9 @@ func TestCreateDBWithMigrations(t *testing.T) {
}
ctx := context.Background()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../.."))
dbcfg, cleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Cfg: cfg,
@@ -43,6 +46,6 @@ func TestCreateDBWithMigrations(t *testing.T) {
defer cleanup()
assert.NotNil(t, dbcfg)
assert.NotNil(t, cfg.DBPool)
assert.NotNil(t, cfg.GetDBPool())
assert.NotNil(t, cleanup)
}
+52 -35
View File
@@ -3,6 +3,8 @@ package test
import (
"context"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/aws"
"queryorchestration/internal/serviceconfig/database"
"testing"
"github.com/testcontainers/testcontainers-go"
@@ -11,21 +13,18 @@ import (
type EcosystemConfig struct {
Services map[Service]*Container
Runners map[Runner]*Container
Cfg *serviceconfig.BaseConfig
Cfg serviceconfig.ConfigProvider
}
type EcosystemNetworkConfig struct {
Runners []*RunnerNetworkConfig
Services []*ServiceNetworkConfig
Cfg *serviceconfig.BaseConfig
Network *testcontainers.DockerNetwork
Runners []*RunnerNetworkConfig
Services []*ServiceNetworkConfig
Cfg serviceconfig.ConfigProvider
Network *testcontainers.DockerNetwork
AWSContainer *AWSContainerConfig
}
func CreateRunnersAndServicesNetwork(t *testing.T, ctx context.Context, ncfg *EcosystemNetworkConfig) (*EcosystemConfig, func()) {
if ncfg.Cfg == nil {
ncfg.Cfg = CreateBaseConfig()
}
network := ncfg.Network
var ncleanup func()
if network == nil {
@@ -52,13 +51,20 @@ func CreateRunnersAndServicesNetwork(t *testing.T, ctx context.Context, ncfg *Ec
}
var qcleanup func()
if ncfg.Cfg.QueueClient == nil {
qcleanup = CreateQueueClient(t, ctx, &CreateQueueConfig{
if ncfg.AWSContainer == nil {
_, qcleanup = CreateAWSContainer(t, ctx, &CreateAWSConfig{
Network: network,
Cfg: ncfg.Cfg,
})
}
if ncfg.Cfg.GetQueueClient() == nil {
err := ncfg.Cfg.SetQueueClient(ctx)
if err != nil {
t.Fatal(err)
}
}
qs := make(map[string]*Container, len(ncfg.Runners))
qclean := make([]func(), len(ncfg.Runners))
for i, r := range ncfg.Runners {
@@ -96,20 +102,26 @@ func CreateRunnersAndServicesNetwork(t *testing.T, ctx context.Context, ncfg *Ec
}
}
func CreateBaseConfig() *serviceconfig.BaseConfig {
cfg := &serviceconfig.BaseConfig{}
func SetCfgProvider(t *testing.T, cfg serviceconfig.ConfigProvider) {
cfg.SetAWSConfig(&aws.AWSConfig{
AWSKeyID: "test",
AWSSecretKey: "test",
AWSRegion: "us-east-1",
})
t.Setenv("AWS_ACCESS_KEY_ID", cfg.GetAWSKeyID())
t.Setenv("AWS_SECRET_ACCESS_KEY", cfg.GetAWSSecretKey())
t.Setenv("AWS_SESSION_TOKEN", cfg.GetAWSSessionToken())
t.Setenv("AWS_REGION", cfg.GetAWSRegion())
t.Setenv("AWS_DEFAULT_REGION", cfg.GetAWSRegion())
cfg.AWSKeyID = "test"
cfg.AWSSecretKey = "test"
cfg.AWSRegion = "us-east-1"
cfg.DBUser = "invalid_user"
cfg.DBSecret = "invalid_pass"
cfg.DBHost = "invalid_host"
cfg.DBPort = 5432
cfg.DBName = "invalid_name"
cfg.DBNoSSL = true
return cfg
cfg.SetDBConfig(&database.DBConfig{
DBUser: "invalid_user",
DBSecret: "invalid_pass",
DBHost: "invalid_host",
DBPort: 5432,
DBName: "invalid_name",
DBNoSSL: true,
})
}
type ServiceNetworkConfig struct {
@@ -118,7 +130,8 @@ type ServiceNetworkConfig struct {
}
func CreateServiceNetwork(t *testing.T, ctx context.Context, scfg *ServiceNetworkConfig) (*Container, func()) {
cfg := CreateBaseConfig()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
network, ncleanup := CreateNetwork(t, ctx)
@@ -142,17 +155,14 @@ func CreateServiceNetwork(t *testing.T, ctx context.Context, scfg *ServiceNetwor
}
type RunnerNetworkConfig struct {
Network *testcontainers.DockerNetwork
Cfg *serviceconfig.BaseConfig
Name Runner
Env map[string]string
Network *testcontainers.DockerNetwork
AWSContainer *AWSContainerConfig
Cfg serviceconfig.ConfigProvider
Name Runner
Env map[string]string
}
func CreateRunnerNetwork(t *testing.T, ctx context.Context, rcfg *RunnerNetworkConfig) (*Container, func()) {
if rcfg.Cfg == nil {
rcfg.Cfg = CreateBaseConfig()
}
network := rcfg.Network
var ncleanup func()
if network == nil {
@@ -160,13 +170,20 @@ func CreateRunnerNetwork(t *testing.T, ctx context.Context, rcfg *RunnerNetworkC
}
var qcleanup func()
if rcfg.Cfg.QueueClient == nil {
qcleanup = CreateQueueClient(t, ctx, &CreateQueueConfig{
if rcfg.AWSContainer == nil {
_, qcleanup = CreateAWSContainer(t, ctx, &CreateAWSConfig{
Network: network,
Cfg: rcfg.Cfg,
})
}
if rcfg.Cfg.GetQueueClient() == nil {
err := rcfg.Cfg.SetQueueClient(ctx)
if err != nil {
t.Fatal(err)
}
}
url := CreateQueue(t, ctx, rcfg.Cfg, rcfg.Name)
_, dbcleanup := CreateDB(t, ctx, &CreateDatabaseConfig{
+9 -3
View File
@@ -4,6 +4,7 @@ import (
"context"
"os"
"path"
"queryorchestration/internal/serviceconfig"
"testing"
"github.com/stretchr/testify/assert"
@@ -15,7 +16,10 @@ func TestCreateRunnerNetwork(t *testing.T) {
}
ctx := context.Background()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
conn, cleanup := CreateRunnerNetwork(t, ctx, &RunnerNetworkConfig{
Cfg: cfg,
Name: QueryRunner,
})
@@ -46,8 +50,9 @@ func TestCreateRunnersAndServicesNetwork(t *testing.T) {
t.Skip("Skipping long test in short mode")
}
ctx := context.Background()
cfg := CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../..")
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../.."))
conn, cleanup := CreateRunnersAndServicesNetwork(t, ctx, &EcosystemNetworkConfig{
Cfg: cfg,
@@ -66,7 +71,8 @@ func TestCreateRunnersAndServicesNetwork(t *testing.T) {
}
func TestCreateBaseConfig(t *testing.T) {
cfg := CreateBaseConfig()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
assert.Equal(t, "test", cfg.AWSKeyID)
assert.Equal(t, "test", cfg.AWSSecretKey)
assert.Equal(t, "us-east-1", cfg.AWSRegion)
+33
View File
@@ -0,0 +1,33 @@
package test
import (
"context"
objectstore "queryorchestration/internal/serviceconfig/objectstore"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
func CreateBucket(t *testing.T, ctx context.Context, cfg objectstore.ConfigProvider, name string) {
_, err := cfg.GetStoreClient().CreateBucket(ctx, &s3.CreateBucketInput{
Bucket: aws.String(name),
})
if err != nil {
t.Fatal(err)
}
err = cfg.PingStoreByName(ctx, name)
if err != nil {
t.Fatal(err)
}
}
func CreateStoreClient(t *testing.T, ctx context.Context, cfg objectstore.ConfigProvider, endpoint string) {
cfg.SetS3Endpoint(endpoint)
cfg.SetS3UsePathStyle(true)
err := cfg.SetStoreClient(ctx)
if err != nil {
t.Fatal(err)
}
}
+52
View File
@@ -0,0 +1,52 @@
package test
import (
"context"
"queryorchestration/internal/serviceconfig"
objectstore "queryorchestration/internal/serviceconfig/objectstore"
"testing"
"github.com/stretchr/testify/assert"
)
type StoreConfig struct {
serviceconfig.BaseConfig
objectstore.ObjectStoreConfig
}
func TestCreateBucket(t *testing.T) {
if testing.Short() {
t.Skip("Skipping long test in short mode")
}
ctx := context.Background()
cfg := &StoreConfig{}
SetCfgProvider(t, cfg)
acfg, cleanup := CreateAWSContainer(t, ctx, &CreateAWSConfig{
Cfg: cfg,
})
defer cleanup()
CreateStoreClient(t, ctx, cfg, acfg.ExternalEndpoint)
CreateBucket(t, ctx, cfg, "myname")
}
func TestCreateStoreClient(t *testing.T) {
if testing.Short() {
t.Skip("Skipping long test in short mode")
}
ctx := context.Background()
cfg := &StoreConfig{}
SetCfgProvider(t, cfg)
acfg, cleanup := CreateAWSContainer(t, ctx, &CreateAWSConfig{
Cfg: cfg,
})
defer cleanup()
CreateStoreClient(t, ctx, cfg, acfg.ExternalEndpoint)
assert.NotNil(t, cfg.GetStoreClient())
}
+9 -114
View File
@@ -2,128 +2,25 @@ package test
import (
"context"
"fmt"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/queue"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
"github.com/docker/go-connections/nat"
"github.com/stretchr/testify/assert"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)
type queueContainerConfig struct {
Container testcontainers.Container
ExternalEndpoint string
NetworkEndpoint string
}
type CreateQueueConfig struct {
Network *testcontainers.DockerNetwork
Cfg *serviceconfig.BaseConfig
}
func createQueueContainer(t *testing.T, ctx context.Context, cfg *CreateQueueConfig) (*queueContainerConfig, func()) {
alias := "localstack"
provider := credentials.NewStaticCredentialsProvider(cfg.Cfg.AWSKeyID, cfg.Cfg.AWSSecretKey, cfg.Cfg.AWSSessionToken)
port, err := nat.NewPort("tcp", "4566")
if err != nil {
t.Fatalf("Failed to create port: %v", err)
}
req := testcontainers.ContainerRequest{
Image: "localstack/localstack:4.0.3",
Env: map[string]string{
"AWS_ACCESS_KEY_ID": provider.Value.AccessKeyID,
"AWS_SECRET_ACCESS_KEY": provider.Value.SecretAccessKey,
"AWS_SESSION_TOKEN": provider.Value.SessionToken,
"AWS_REGION": cfg.Cfg.AWSRegion,
"SERVICES": "sqs",
"SKIP_SSL_CERT_DOWNLOAD": "1",
"LOCALSTACK_HOST": alias,
"SQS_ENDPOINT_STRATEGY": "path",
},
ExposedPorts: []string{port.Port()},
WaitingFor: wait.ForAll(
// wait.ForExposedPort(),
wait.ForListeningPort(port),
wait.ForLog("Ready."),
),
}
if cfg.Network != nil {
req.Networks = []string{cfg.Network.Name}
req.NetworkAliases = map[string][]string{
cfg.Network.Name: {alias},
}
}
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
func CreateQueue(t *testing.T, ctx context.Context, cfg serviceconfig.ConfigProvider, name string) string {
queueM, err := cfg.GetQueueClient().CreateQueue(ctx, &sqs.CreateQueueInput{
QueueName: aws.String(name),
})
if err != nil {
t.Fatalf("Failed to start container: %v", err)
}
host, err := container.Host(ctx)
if err != nil {
t.Fatalf("Failed to extract host: %v", err)
}
mappedPort, err := container.MappedPort(ctx, port)
if err != nil {
t.Fatalf("Failed to extract port: %v", err)
}
extEndpoint := fmt.Sprintf("http://%s:%s", host, mappedPort.Port())
t.Setenv("AWS_ENDPOINT_URL_SQS", extEndpoint)
cfg.Cfg.AWSEndpointUrlSQS = extEndpoint
var endpoint string
if cfg.Network != nil {
endpoint = fmt.Sprintf("http://%s:%s", alias, port.Port())
cfg.Cfg.AWSEndpointUrlSQS = endpoint
}
return &queueContainerConfig{
Container: container,
ExternalEndpoint: extEndpoint,
NetworkEndpoint: endpoint,
}, func() {
err := container.Terminate(ctx)
if err != nil {
t.Error(err)
}
}
}
func CreateQueueClient(t *testing.T, ctx context.Context, cfg *CreateQueueConfig) func() {
qcfg, clean := createQueueContainer(t, ctx, cfg)
provider := credentials.NewStaticCredentialsProvider(cfg.Cfg.AWSKeyID, cfg.Cfg.AWSSecretKey, cfg.Cfg.AWSSessionToken)
sqsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(cfg.Cfg.AWSRegion), config.WithCredentialsProvider(provider), config.WithBaseEndpoint(qcfg.ExternalEndpoint))
if err != nil {
t.Fatal(err)
}
cfg.Cfg.QueueClient = sqs.NewFromConfig(sqsCfg)
return clean
}
func CreateQueue(t *testing.T, ctx context.Context, cfg *serviceconfig.BaseConfig, name string) string {
queueM, err := cfg.QueueClient.CreateQueue(ctx, &sqs.CreateQueueInput{
QueueName: aws.String(name),
})
err = cfg.PingQueueByURL(ctx, *queueM.QueueUrl)
if err != nil {
t.Fatal(err)
}
@@ -131,9 +28,7 @@ func CreateQueue(t *testing.T, ctx context.Context, cfg *serviceconfig.BaseConfi
return *queueM.QueueUrl
}
func AssertMessageWait(t *testing.T, ctx context.Context, cfg serviceconfig.ConfigProvider, params *queue.ReceiveParams) types.Message {
time.Sleep(1 * time.Second)
func AssertMessage(t *testing.T, ctx context.Context, cfg serviceconfig.ConfigProvider, params *queue.ReceiveParams) types.Message {
result, err := cfg.ReceiveFromQueue(ctx, params)
assert.NoError(t, err)
assert.NotNil(t, result.Messages)
@@ -143,16 +38,16 @@ func AssertMessageWait(t *testing.T, ctx context.Context, cfg serviceconfig.Conf
return result.Messages[0]
}
func AssertMessageBodyWait(t *testing.T, ctx context.Context, cfg serviceconfig.ConfigProvider, url string, body string) {
message := AssertMessageWait(t, ctx, cfg, &queue.ReceiveParams{
func AssertMessageBody(t *testing.T, ctx context.Context, cfg serviceconfig.ConfigProvider, url string, body string) {
message := AssertMessage(t, ctx, cfg, &queue.ReceiveParams{
QueueURL: url,
})
assert.Equal(t, body, *message.Body)
}
func AssertMessageAttrWait(t *testing.T, ctx context.Context, cfg serviceconfig.ConfigProvider, url string, name string, value string) {
message := AssertMessageWait(t, ctx, cfg, &queue.ReceiveParams{
func AssertMessageAttr(t *testing.T, ctx context.Context, cfg serviceconfig.ConfigProvider, url string, name string, value string) {
message := AssertMessage(t, ctx, cfg, &queue.ReceiveParams{
QueueURL: url,
Attributes: []string{name},
})
+28 -46
View File
@@ -2,6 +2,7 @@ package test
import (
"context"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/queue"
"testing"
@@ -10,51 +11,23 @@ import (
"github.com/stretchr/testify/assert"
)
func TestCreateQueueContainer(t *testing.T) {
if testing.Short() {
t.Skip("Skipping long test in short mode")
}
ctx := context.Background()
cfg := CreateBaseConfig()
qcfg, cleanup := createQueueContainer(t, ctx, &CreateQueueConfig{
Cfg: cfg,
})
assert.NotNil(t, qcfg)
assert.NotNil(t, cleanup)
cleanup()
}
func TestCreateQueueClient(t *testing.T) {
if testing.Short() {
t.Skip("Skipping long test in short mode")
}
ctx := context.Background()
cfg := CreateBaseConfig()
cleanup := CreateQueueClient(t, ctx, &CreateQueueConfig{
Cfg: cfg,
})
assert.NotNil(t, cfg.QueueClient)
assert.NotNil(t, cleanup)
cleanup()
}
func TestCreateQueue(t *testing.T) {
if testing.Short() {
t.Skip("Skipping long test in short mode")
}
ctx := context.Background()
cfg := CreateBaseConfig()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
cleanup := CreateQueueClient(t, ctx, &CreateQueueConfig{
_, cleanup := CreateAWSContainer(t, ctx, &CreateAWSConfig{
Cfg: cfg,
})
defer cleanup()
err := cfg.SetQueueClient(ctx)
assert.NoError(t, err)
url := CreateQueue(t, ctx, cfg, "myname")
assert.Equal(t, "http://localstack:4566/queue/us-east-1/000000000000/myname", url)
}
@@ -65,21 +38,24 @@ func TestAssertMessageWait(t *testing.T) {
}
ctx := context.Background()
cfg := CreateBaseConfig()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
cleanup := CreateQueueClient(t, ctx, &CreateQueueConfig{
_, cleanup := CreateAWSContainer(t, ctx, &CreateAWSConfig{
Cfg: cfg,
})
defer cleanup()
err := cfg.SetQueueClient(ctx)
assert.NoError(t, err)
url := CreateQueue(t, ctx, cfg, "myname")
err := cfg.SendToQueue(ctx, &queue.SendParams{
err = cfg.SendToQueue(ctx, &queue.SendParams{
QueueURL: url,
Body: "body",
})
assert.NoError(t, err)
msg := AssertMessageWait(t, ctx, cfg, &queue.ReceiveParams{
msg := AssertMessage(t, ctx, cfg, &queue.ReceiveParams{
QueueURL: url,
})
assert.NotNil(t, msg)
@@ -91,21 +67,24 @@ func TestAssertMessageBodyWait(t *testing.T) {
}
ctx := context.Background()
cfg := CreateBaseConfig()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
cleanup := CreateQueueClient(t, ctx, &CreateQueueConfig{
_, cleanup := CreateAWSContainer(t, ctx, &CreateAWSConfig{
Cfg: cfg,
})
defer cleanup()
err := cfg.SetQueueClient(ctx)
assert.NoError(t, err)
url := CreateQueue(t, ctx, cfg, "myname")
err := cfg.SendToQueue(ctx, &queue.SendParams{
err = cfg.SendToQueue(ctx, &queue.SendParams{
QueueURL: url,
Body: "body",
})
assert.NoError(t, err)
AssertMessageBodyWait(t, ctx, cfg, url, "\"body\"")
AssertMessageBody(t, ctx, cfg, url, "\"body\"")
}
func TestAssertMessageAttrWait(t *testing.T) {
@@ -114,18 +93,21 @@ func TestAssertMessageAttrWait(t *testing.T) {
}
ctx := context.Background()
cfg := CreateBaseConfig()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
cleanup := CreateQueueClient(t, ctx, &CreateQueueConfig{
_, cleanup := CreateAWSContainer(t, ctx, &CreateAWSConfig{
Cfg: cfg,
})
defer cleanup()
err := cfg.SetQueueClient(ctx)
assert.NoError(t, err)
url := CreateQueue(t, ctx, cfg, "myname")
name := "name"
value := "value"
err := cfg.SendToQueue(ctx, &queue.SendParams{
err = cfg.SendToQueue(ctx, &queue.SendParams{
QueueURL: url,
Body: "body",
Attributes: map[string]types.MessageAttributeValue{
@@ -137,5 +119,5 @@ func TestAssertMessageAttrWait(t *testing.T) {
})
assert.NoError(t, err)
AssertMessageAttrWait(t, ctx, cfg, url, name, value)
AssertMessageAttr(t, ctx, cfg, url, name, value)
}
+1 -1
View File
@@ -21,7 +21,7 @@ const (
type RunnerConfig struct {
Name Runner
QueueURL string
Cfg *serviceconfig.BaseConfig
Cfg serviceconfig.ConfigProvider
Network *testcontainers.DockerNetwork
Env map[string]string
}
+6 -2
View File
@@ -2,6 +2,7 @@ package test
import (
"context"
"queryorchestration/internal/serviceconfig"
"testing"
"github.com/stretchr/testify/assert"
@@ -16,13 +17,16 @@ func TestCreateRunner(t *testing.T) {
ncfg, ncleanup := CreateNetwork(t, ctx)
defer ncleanup()
cfg := CreateBaseConfig()
cfg := &serviceconfig.BaseConfig{}
SetCfgProvider(t, cfg)
qcleanup := CreateQueueClient(t, ctx, &CreateQueueConfig{
_, qcleanup := CreateAWSContainer(t, ctx, &CreateAWSConfig{
Network: ncfg,
Cfg: cfg,
})
defer qcleanup()
err := cfg.SetQueueClient(ctx)
assert.NoError(t, err)
url := CreateQueue(t, ctx, cfg, "myname")
_, dbcleanup := CreateDB(t, ctx, &CreateDatabaseConfig{
+1 -1
View File
@@ -19,7 +19,7 @@ const (
type ServiceConfig struct {
Name Service
Cfg *serviceconfig.BaseConfig
Cfg serviceconfig.ConfigProvider
Network *testcontainers.DockerNetwork
Env map[string]string
}
+4 -2
View File
@@ -4,6 +4,7 @@ import (
"context"
"os"
"path"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/test"
"testing"
@@ -19,8 +20,9 @@ func TestCreateService(t *testing.T) {
ncfg, ncleanup := test.CreateNetwork(t, ctx)
defer ncleanup()
cfg := test.CreateBaseConfig()
cfg.BasePath = path.Join(os.Getenv("PWD"), "../..")
cfg := &serviceconfig.BaseConfig{}
test.SetCfgProvider(t, cfg)
cfg.SetBasePath(path.Join(os.Getenv("PWD"), "../.."))
_, dbcleanup := test.CreateDB(t, ctx, &test.CreateDatabaseConfig{
Network: ncfg,
Cfg: cfg,