mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-06 23:46:29 +02:00
213 lines
5.8 KiB
Go
213 lines
5.8 KiB
Go
package tests
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"runtime/debug"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/docker"
|
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
|
"geeks-accelerator/oss/saas-starter-kit/internal/schema"
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/kelseyhightower/envconfig"
|
|
)
|
|
|
|
// Success and failure markers.
|
|
const (
|
|
Success = "\u2713"
|
|
Failed = "\u2717"
|
|
)
|
|
|
|
// Test owns state for running/shutting down tests.
|
|
type Test struct {
|
|
Log *log.Logger
|
|
MasterDB *sqlx.DB
|
|
container *docker.Container
|
|
AwsSession *session.Session
|
|
}
|
|
|
|
// Flag used to disable setting up database.
|
|
var DisableDb bool
|
|
|
|
// New is the entry point for tests.
|
|
func New() *Test {
|
|
|
|
// =========================================================================
|
|
// Logging
|
|
|
|
log := log.New(os.Stdout, "TEST : ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile)
|
|
|
|
// =========================================================================
|
|
// Configuration
|
|
var cfg struct {
|
|
Aws struct {
|
|
AccessKeyID string `envconfig:"AWS_ACCESS_KEY_ID"` // WEB_API_AWS_AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY_ID
|
|
SecretAccessKey string `envconfig:"AWS_SECRET_ACCESS_KEY" json:"-"` // don't print
|
|
Region string `default:"us-west-2" envconfig:"AWS_REGION"`
|
|
UseRole bool `envconfig:"AWS_USE_ROLE"`
|
|
}
|
|
}
|
|
|
|
// For additional details refer to https://github.com/kelseyhightower/envconfig
|
|
if err := envconfig.Process("TESTS", &cfg); err != nil {
|
|
log.Fatalf("startup : Parsing Config : %+v", err)
|
|
}
|
|
|
|
// AWS access keys are required, if roles are enabled, remove any placeholders.
|
|
if cfg.Aws.UseRole {
|
|
cfg.Aws.AccessKeyID = ""
|
|
cfg.Aws.SecretAccessKey = ""
|
|
|
|
// Get an AWS session from an implicit source if no explicit
|
|
// configuration is provided. This is useful for taking advantage of
|
|
// EC2/ECS instance roles.
|
|
if cfg.Aws.Region == "" {
|
|
sess := session.Must(session.NewSession())
|
|
md := ec2metadata.New(sess)
|
|
|
|
var err error
|
|
cfg.Aws.Region, err = md.Region()
|
|
if err != nil {
|
|
log.Fatalf("startup : Load region of ecs metadata : %+v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Print the config for our logs. It's important to any credentials in the config
|
|
// that could expose a security risk are excluded from being json encoded by
|
|
// applying the tag `json:"-"` to the struct var.
|
|
{
|
|
cfgJSON, err := json.MarshalIndent(cfg, "", " ")
|
|
if err != nil {
|
|
log.Fatalf("startup : Marshalling Config to JSON : %+v", err)
|
|
}
|
|
log.Printf("startup : Config : %v\n", string(cfgJSON))
|
|
}
|
|
|
|
// ============================================================
|
|
// Init AWS Session
|
|
var awsSession *session.Session
|
|
if cfg.Aws.UseRole {
|
|
// Get an AWS session from an implicit source if no explicit
|
|
// configuration is provided. This is useful for taking advantage of
|
|
// EC2/ECS instance roles.
|
|
awsSession = session.Must(session.NewSession())
|
|
if cfg.Aws.Region != "" {
|
|
awsSession.Config.WithRegion(cfg.Aws.Region)
|
|
}
|
|
|
|
log.Printf("startup : AWS : Using role.\n")
|
|
} else if cfg.Aws.AccessKeyID != "" {
|
|
creds := credentials.NewStaticCredentials(cfg.Aws.AccessKeyID, cfg.Aws.SecretAccessKey, "")
|
|
awsSession = session.New(&aws.Config{Region: aws.String(cfg.Aws.Region), Credentials: creds})
|
|
|
|
log.Printf("startup : AWS : Using static credentials\n")
|
|
}
|
|
|
|
// ============================================================
|
|
// Startup Postgres container
|
|
|
|
var (
|
|
masterDB *sqlx.DB
|
|
container *docker.Container
|
|
)
|
|
if !DisableDb {
|
|
var err error
|
|
container, err = docker.StartPostgres(log)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
// ============================================================
|
|
// Configuration
|
|
|
|
dbHost := fmt.Sprintf("postgres://%s:%s@127.0.0.1:%s/%s?timezone=UTC&sslmode=disable", container.User, container.Pass, container.Port, container.Database)
|
|
|
|
// ============================================================
|
|
// Start Postgres
|
|
|
|
log.Println("main : Started : Initialize Postgres")
|
|
for i := 0; i <= 20; i++ {
|
|
masterDB, err = sqlx.Open("postgres", dbHost)
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
// Make sure the database is ready for queries.
|
|
_, err = masterDB.Exec("SELECT 1")
|
|
if err != nil {
|
|
if err != io.EOF && !strings.Contains(err.Error(), "connection reset by peer") {
|
|
break
|
|
}
|
|
time.Sleep(time.Second)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
log.Fatalf("startup : Register DB : %v", err)
|
|
}
|
|
|
|
// Set the context with the required values to
|
|
// process the request.
|
|
v := webcontext.Values{
|
|
Now: time.Now(),
|
|
Env: webcontext.Env_Dev,
|
|
}
|
|
ctx := context.WithValue(context.Background(), webcontext.KeyValues, &v)
|
|
|
|
// Execute the migrations
|
|
if err = schema.Migrate(ctx, masterDB, log, true); err != nil {
|
|
log.Fatalf("main : Migrate : %v", err)
|
|
}
|
|
log.Printf("main : Migrate : Completed")
|
|
}
|
|
|
|
return &Test{log, masterDB, container, awsSession}
|
|
}
|
|
|
|
// TearDown is used for shutting down tests. Calling this should be
|
|
// done in a defer immediately after calling New.
|
|
func (t *Test) TearDown() {
|
|
if t.MasterDB != nil {
|
|
t.MasterDB.Close()
|
|
}
|
|
|
|
if t.container != nil {
|
|
if err := docker.StopPostgres(t.Log, t.container); err != nil {
|
|
t.Log.Println(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recover is used to prevent panics from allowing the test to cleanup.
|
|
func Recover(t *testing.T) {
|
|
if r := recover(); r != nil {
|
|
t.Fatal("Unhandled Exception:", string(debug.Stack()))
|
|
}
|
|
}
|
|
|
|
// Context returns an app level context for testing.
|
|
func Context() context.Context {
|
|
values := webcontext.Values{
|
|
TraceID: uint64(time.Now().UnixNano()),
|
|
Now: time.Now(),
|
|
RequestIP: "68.69.35.104",
|
|
Env: "dev",
|
|
}
|
|
|
|
return context.WithValue(context.Background(), webcontext.KeyValues, &values)
|
|
}
|