Merged in bugfix/cleanup (pull request #10)
Clean Up Testing and Linting * somescriptcleanup * mostgolangci * deplatest * imageversions * usingscratch * movedqueuefrtesting * finishedunittesting * linting * taskfilecontext
This commit is contained in:
@@ -4,6 +4,7 @@ export DB_PASS="${DB_PASS:-pass}"
|
||||
export DB_HOST="${DB_HOST:-localhost}"
|
||||
export DB_PORT="${DB_PORT:-5432}"
|
||||
export DB_NAME="${DB_NAME:-query_orchestration}"
|
||||
export DB_URI="${DB_URI:-"postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"}"
|
||||
|
||||
dotenv_if_exists .env
|
||||
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
---
|
||||
with-expecter: true
|
||||
packages:
|
||||
queryorchestration/internal:
|
||||
queryorchestration/internal/database/repository:
|
||||
config:
|
||||
recursive: true
|
||||
all: true
|
||||
dir: "mocks/{{.PackageName}}"
|
||||
dir: "mocks/{{.PackageName}}"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
---
|
||||
extends: default
|
||||
ignore: |
|
||||
vendor/
|
||||
vendor/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
---
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"queryorchestration/internal/queue"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs"
|
||||
)
|
||||
|
||||
type Controllers struct {
|
||||
Document DocumentController
|
||||
}
|
||||
|
||||
type Queue struct {
|
||||
Config *queue.QueueConfig
|
||||
Controllers *Controllers
|
||||
}
|
||||
|
||||
func PollMessages(ctx context.Context, queue *Queue) {
|
||||
for {
|
||||
result, err := queue.Config.Client.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{
|
||||
QueueUrl: &queue.Config.URL,
|
||||
MaxNumberOfMessages: 1,
|
||||
WaitTimeSeconds: 2,
|
||||
VisibilityTimeout: 2,
|
||||
MessageAttributeNames: []string{
|
||||
"type",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Message Fetch Fail: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, message := range result.Messages {
|
||||
go func() {
|
||||
toProcess, err := processMessage(ctx, queue, message)
|
||||
if !toProcess {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Message Process Fail: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||
)
|
||||
|
||||
func processMessage(ctx context.Context, queue *Queue, message types.Message) (bool, error) {
|
||||
// Process the message here and return false if not message to process
|
||||
switch *message.MessageAttributes["type"].StringValue {
|
||||
case "DOCTEXT":
|
||||
err := queue.Controllers.Document.Sync(ctx, queue.Config, &message)
|
||||
return true, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package queue
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -8,17 +8,18 @@ import (
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type DocumentController struct {
|
||||
type QueryRunner struct {
|
||||
validator *validator.Validate
|
||||
document document.Service
|
||||
document *document.Service
|
||||
}
|
||||
|
||||
func NewDocumentController(svc document.Service, validator *validator.Validate) *DocumentController {
|
||||
return &DocumentController{
|
||||
func NewQueryRunner(svc *document.Service, validator *validator.Validate) QueryRunner {
|
||||
return QueryRunner{
|
||||
validator: validator,
|
||||
document: svc,
|
||||
}
|
||||
@@ -28,7 +29,7 @@ type DocumentQueryEvent struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
}
|
||||
|
||||
func (s *DocumentController) Sync(ctx context.Context, config *queue.QueueConfig, msg *types.Message) error {
|
||||
func (s QueryRunner) Process(ctx context.Context, config *queue.Config, msg *types.Message) error {
|
||||
var body document.Document
|
||||
err := json.Unmarshal([]byte(*msg.Body), &body)
|
||||
if err != nil {
|
||||
@@ -49,12 +50,12 @@ func (s *DocumentController) Sync(ctx context.Context, config *queue.QueueConfig
|
||||
ID: body.ID,
|
||||
}
|
||||
|
||||
err = queue.Send(ctx, config, "DOCQUERY", queryEvent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = queue.Delete(ctx, config, msg)
|
||||
err = queue.Send(ctx, config, queryEvent, map[string]types.MessageAttributeValue{
|
||||
"type": {
|
||||
DataType: aws.String("String"),
|
||||
StringValue: aws.String("DOCQUERY"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
+3
-66
@@ -1,85 +1,22 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Comments are provided throughout this file to help you get started.
|
||||
# If you need more help, visit the Dockerfile reference guide at
|
||||
# https://docs.docker.com/go/dockerfile-reference/
|
||||
|
||||
# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7
|
||||
|
||||
################################################################################
|
||||
# Create a stage for building the application.
|
||||
ARG GO_VERSION=1.23
|
||||
#FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS build
|
||||
FROM golang:${GO_VERSION} AS build
|
||||
WORKDIR /src
|
||||
|
||||
# Download dependencies as a separate step to take advantage of Docker's caching.
|
||||
# Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds.
|
||||
# Leverage bind mounts to go.sum and go.mod to avoid having to copy them into
|
||||
# the container.
|
||||
RUN --mount=type=cache,target=/go/pkg/mod/ \
|
||||
--mount=type=bind,source=go.sum,target=go.sum \
|
||||
--mount=type=bind,source=go.mod,target=go.mod \
|
||||
go mod download -x
|
||||
|
||||
# This is the architecture you're building for, which is passed in by the builder.
|
||||
# Placing it here allows the previous steps to be cached across architectures.
|
||||
ARG TARGETARCH
|
||||
|
||||
ARG TARGETCMD
|
||||
|
||||
RUN if [ -z "$TARGETCMD" ]; then \
|
||||
echo "Error: TARGETCMD is not set."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
# Build the application.
|
||||
# Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds.
|
||||
# Leverage a bind mount to the current directory to avoid having to copy the
|
||||
# source code into the container.
|
||||
RUN --mount=type=cache,target=/go/pkg/mod/ \
|
||||
--mount=type=bind,target=. \
|
||||
CGO_ENABLED=0 GOARCH=$TARGETARCH go build -o /bin/server ./cmd/$TARGETCMD/main.go
|
||||
CGO_ENABLED=0 GOARCH=$TARGETARCH go build -o /bin ./cmd/...
|
||||
|
||||
################################################################################
|
||||
# Create a new stage for running the application that contains the minimal
|
||||
# runtime dependencies for the application. This often uses a different base
|
||||
# image from the build stage where the necessary files are copied from the build
|
||||
# stage.
|
||||
#
|
||||
# The example below uses the alpine image as the foundation for running the app.
|
||||
# By specifying the "latest" tag, it will also use whatever happens to be the
|
||||
# most recent version of that image when you build your Dockerfile. If
|
||||
# reproducability is important, consider using a versioned tag
|
||||
# (e.g., alpine:3.17.2) or SHA (e.g., alpine@sha256:c41ab5c992deb4fe7e5da09f67a8804a46bd0592bfdf0b1847dde0e0889d2bff).
|
||||
FROM alpine:latest AS final
|
||||
|
||||
# Install any runtime dependencies that are needed to run your application.
|
||||
# Leverage a cache mount to /var/cache/apk/ to speed up subsequent builds.
|
||||
RUN --mount=type=cache,target=/var/cache/apk \
|
||||
apk --update add \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
&& \
|
||||
update-ca-certificates
|
||||
|
||||
# Create a non-privileged user that the app will run under.
|
||||
# See https://docs.docker.com/go/dockerfile-user-best-practices/
|
||||
ARG UID=10001
|
||||
RUN adduser \
|
||||
--disabled-password \
|
||||
--gecos "" \
|
||||
--home "/nonexistent" \
|
||||
--shell "/sbin/nologin" \
|
||||
--no-create-home \
|
||||
--uid "${UID}" \
|
||||
appuser
|
||||
USER appuser
|
||||
FROM scratch AS final
|
||||
|
||||
COPY database/migrations/ database/migrations/
|
||||
|
||||
# Copy the executable from the "build" stage.
|
||||
COPY --from=build /bin/server /bin/
|
||||
|
||||
# What the container should run when it is started.
|
||||
ENTRYPOINT [ "/bin/server" ]
|
||||
COPY --from=build /bin/ /bin/
|
||||
|
||||
+10
-42
@@ -2,56 +2,24 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"queryorchestration/api/queue"
|
||||
"queryorchestration/internal/database"
|
||||
"queryorchestration/internal/database/repository"
|
||||
controllers "queryorchestration/api/queue"
|
||||
"queryorchestration/internal/document"
|
||||
"queryorchestration/internal/env"
|
||||
"queryorchestration/internal/otel"
|
||||
queueSVC "queryorchestration/internal/queue"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"queryorchestration/internal/queue"
|
||||
"queryorchestration/internal/server"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
closeTracer := otel.New(ctx)
|
||||
defer closeTracer()
|
||||
queryrunner := func(cfg *server.Config) queue.Controller {
|
||||
svc := document.New(cfg.Database)
|
||||
|
||||
database.RunMigrations(&database.MigrationConfig{})
|
||||
|
||||
cfg, err := config.LoadDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
log.Panicf("Unable to load SDK config: %v", err)
|
||||
return controllers.NewQueryRunner(svc, cfg.Validator)
|
||||
}
|
||||
|
||||
queueURL := env.GetPanic("QUEUE_URL")
|
||||
server := queue.NewServer(ctx, &queue.ListenerConfig{
|
||||
Controller: queryrunner,
|
||||
})
|
||||
|
||||
sqsClient := sqs.NewFromConfig(cfg)
|
||||
|
||||
dbPool := database.GetDBPool(ctx)
|
||||
dbQueries := repository.New(dbPool)
|
||||
db := &database.Connection{
|
||||
Pool: dbPool,
|
||||
Queries: dbQueries,
|
||||
}
|
||||
valid := validator.New()
|
||||
|
||||
controllers := queue.Controllers{
|
||||
Document: *queue.NewDocumentController(*document.New(db), valid),
|
||||
}
|
||||
|
||||
config := queue.Queue{
|
||||
Controllers: &controllers,
|
||||
Config: &queueSVC.QueueConfig{
|
||||
URL: queueURL,
|
||||
Client: sqsClient,
|
||||
},
|
||||
}
|
||||
|
||||
queue.PollMessages(ctx, &config)
|
||||
server.Listen(ctx)
|
||||
}
|
||||
|
||||
+13
-35
@@ -2,19 +2,14 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net"
|
||||
"queryorchestration/api/controllers"
|
||||
controllers "queryorchestration/api/api"
|
||||
serviceinterfaces "queryorchestration/api/serviceInterfaces"
|
||||
"queryorchestration/internal/api"
|
||||
"queryorchestration/internal/collector"
|
||||
"queryorchestration/internal/database"
|
||||
"queryorchestration/internal/database/repository"
|
||||
"queryorchestration/internal/export"
|
||||
"queryorchestration/internal/otel"
|
||||
"queryorchestration/internal/query"
|
||||
"strconv"
|
||||
"queryorchestration/internal/server"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
_ "github.com/lib/pq"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
@@ -22,35 +17,18 @@ import (
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
closeTracer := otel.New(ctx)
|
||||
defer closeTracer()
|
||||
controllers := func(cfg *server.Config) *grpc.Server {
|
||||
grpcServer := grpc.NewServer()
|
||||
serviceinterfaces.RegisterQueryServiceServer(grpcServer, controllers.NewQueryController(*query.New(cfg.Database), cfg.Validator))
|
||||
serviceinterfaces.RegisterExportServiceServer(grpcServer, controllers.NewExportController(*export.New(cfg.Database), cfg.Validator))
|
||||
serviceinterfaces.RegisterJobCollectorServiceServer(grpcServer, controllers.NewJobCollectorController(*collector.New(cfg.Database), cfg.Validator))
|
||||
|
||||
database.RunMigrations(&database.MigrationConfig{})
|
||||
|
||||
host := "0.0.0.0"
|
||||
port := 8080
|
||||
address := net.JoinHostPort(host, strconv.Itoa(port))
|
||||
|
||||
lis, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
log.Panicf("failed to listen: %v", err)
|
||||
return grpcServer
|
||||
}
|
||||
|
||||
dbPool := database.GetDBPool(ctx)
|
||||
dbQueries := repository.New(dbPool)
|
||||
db := &database.Connection{
|
||||
Pool: dbPool,
|
||||
Queries: dbQueries,
|
||||
}
|
||||
valid := validator.New()
|
||||
server := api.New(ctx, &api.Config{
|
||||
GetControllers: controllers,
|
||||
})
|
||||
|
||||
grpcServer := grpc.NewServer()
|
||||
serviceinterfaces.RegisterQueryServiceServer(grpcServer, controllers.NewQueryController(*query.New(db), valid))
|
||||
serviceinterfaces.RegisterExportServiceServer(grpcServer, controllers.NewExportController(*export.New(db), valid))
|
||||
serviceinterfaces.RegisterJobCollectorServiceServer(grpcServer, controllers.NewJobCollectorController(*collector.New(db), valid))
|
||||
|
||||
log.Printf("Listening on port %d", port)
|
||||
if err := grpcServer.Serve(lis); err != nil {
|
||||
log.Panicf("Failed to serve: %v", err)
|
||||
}
|
||||
server.Listen()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
services:
|
||||
db:
|
||||
image: postgres:latest
|
||||
image: postgres:17.2-alpine3.21
|
||||
ports:
|
||||
- 5432:5432
|
||||
environment:
|
||||
@@ -12,7 +13,7 @@ services:
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -U ${DB_USER}" ]
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net"
|
||||
"queryorchestration/internal/server"
|
||||
"strconv"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
GetControllers func(*server.Config) *grpc.Server
|
||||
BasePath string
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
controllers *grpc.Server
|
||||
port int
|
||||
host string
|
||||
address string
|
||||
}
|
||||
|
||||
func New(ctx context.Context, cfg *Config) *Server {
|
||||
serverCfg := server.New(ctx, &server.NewConfig{
|
||||
BasePath: cfg.BasePath,
|
||||
})
|
||||
|
||||
host := "0.0.0.0"
|
||||
port := 8080
|
||||
address := net.JoinHostPort(host, strconv.Itoa(port))
|
||||
|
||||
controllers := cfg.GetControllers(serverCfg)
|
||||
|
||||
return &Server{
|
||||
controllers: controllers,
|
||||
port: port,
|
||||
host: host,
|
||||
address: address,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Listen() {
|
||||
lis, err := net.Listen("tcp", s.address)
|
||||
if err != nil {
|
||||
log.Panicf("failed to listen: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Listening on port %d", s.port)
|
||||
|
||||
err = s.controllers.Serve(lis)
|
||||
if err != nil {
|
||||
log.Panicf("Failed to serve: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"queryorchestration/internal/database"
|
||||
"queryorchestration/internal/server"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
_, cleanup := createDB(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
getControllers := func(c *server.Config) *grpc.Server {
|
||||
return nil
|
||||
}
|
||||
cfg := &Config{
|
||||
GetControllers: getControllers,
|
||||
BasePath: "../..",
|
||||
}
|
||||
|
||||
server := New(ctx, cfg)
|
||||
assert.Equal(t, 8080, server.port)
|
||||
assert.Equal(t, "0.0.0.0", server.host)
|
||||
assert.Equal(t, "0.0.0.0:8080", server.address)
|
||||
}
|
||||
|
||||
func TestListen(t *testing.T) {
|
||||
t.Skip("Must test appropriately")
|
||||
server := Server{}
|
||||
|
||||
assert.Panics(t, func() { server.Listen() })
|
||||
}
|
||||
|
||||
type db struct {
|
||||
pool *pgxpool.Pool
|
||||
container *testcontainers.Container
|
||||
}
|
||||
|
||||
func createDB(t *testing.T, ctx context.Context) (*db, func()) {
|
||||
port, err := nat.NewPort("tcp", "5432")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create port: %v", err)
|
||||
}
|
||||
|
||||
name := "queryorchestration"
|
||||
pass := "pass"
|
||||
user := "postgres"
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "postgres:17.2-alpine3.21",
|
||||
Env: map[string]string{
|
||||
"POSTGRES_DB": name,
|
||||
"POSTGRES_USER": user,
|
||||
"POSTGRES_PASSWORD": pass,
|
||||
},
|
||||
ExposedPorts: []string{port.Port()},
|
||||
WaitingFor: wait.ForListeningPort(port),
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
t.Setenv("DB_USER", user)
|
||||
t.Setenv("DB_PASS", pass)
|
||||
t.Setenv("DB_HOST", host)
|
||||
t.Setenv("DB_PORT", fmt.Sprint(mappedPort.Int()))
|
||||
t.Setenv("DB_NAME", name)
|
||||
t.Setenv("DB_NOSSL", "1")
|
||||
|
||||
pool := database.GetDBPool(ctx)
|
||||
|
||||
return &db{
|
||||
pool: pool,
|
||||
container: &container,
|
||||
}, func() {
|
||||
err := container.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package contextfull
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
queryprocessor "queryorchestration/internal/queryProcessor"
|
||||
"queryorchestration/internal/result"
|
||||
)
|
||||
@@ -16,7 +16,7 @@ func NewExtractor() Extractor {
|
||||
|
||||
func (e Extractor) Process(ctx context.Context, query *queryprocessor.Query, values []result.Value) (string, error) {
|
||||
if len(values) > 0 {
|
||||
return "", fmt.Errorf("no requirements expected")
|
||||
return "", errors.New("no requirements expected")
|
||||
}
|
||||
// TODO
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
|
||||
func TestDBConn(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, container := createDB(t, ctx)
|
||||
defer container.Terminate(ctx)
|
||||
_, cleanup := createDB(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
conn := database.GetDBConn(ctx)
|
||||
assert.NotNil(t, conn)
|
||||
@@ -34,8 +34,8 @@ func TestDBConnNoDB(t *testing.T) {
|
||||
|
||||
func TestDBPool(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, container := createDB(t, ctx)
|
||||
defer container.Terminate(ctx)
|
||||
_, cleanup := createDB(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
pool := database.GetDBPool(ctx)
|
||||
assert.NotNil(t, pool)
|
||||
@@ -49,7 +49,12 @@ type dbConfig struct {
|
||||
Password string
|
||||
}
|
||||
|
||||
func createDB(t *testing.T, ctx context.Context) (*dbConfig, testcontainers.Container) {
|
||||
type db struct {
|
||||
config *dbConfig
|
||||
container *testcontainers.Container
|
||||
}
|
||||
|
||||
func createDB(t *testing.T, ctx context.Context) (*db, func()) {
|
||||
alias := "postgres"
|
||||
port, err := nat.NewPort("tcp", "5432")
|
||||
if err != nil {
|
||||
@@ -65,7 +70,7 @@ func createDB(t *testing.T, ctx context.Context) (*dbConfig, testcontainers.Cont
|
||||
}
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "postgres:latest",
|
||||
Image: "postgres:17.2-alpine3.21",
|
||||
Env: map[string]string{
|
||||
"POSTGRES_DB": config.Name,
|
||||
"POSTGRES_USER": config.User,
|
||||
@@ -102,5 +107,13 @@ func createDB(t *testing.T, ctx context.Context) (*dbConfig, testcontainers.Cont
|
||||
t.Setenv("DB_PORT", fmt.Sprint(config.Port))
|
||||
t.Setenv("DB_NAME", config.Name)
|
||||
|
||||
return &config, container
|
||||
return &db{
|
||||
config: &config,
|
||||
container: &container,
|
||||
}, func() {
|
||||
err := container.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
func TestRunMigrations(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, container := createDB(t, ctx)
|
||||
defer container.Terminate(ctx)
|
||||
_, cleanup := createDB(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
t.Setenv("DB_NOSSL", "1")
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
@@ -16,7 +18,11 @@ func MustToDBUUIDArray(ids []uuid.UUID) []pgtype.UUID {
|
||||
|
||||
func MustToDBUUID(id uuid.UUID) pgtype.UUID {
|
||||
var dbID pgtype.UUID
|
||||
dbID.Scan(id.String())
|
||||
err := dbID.Scan(id.String())
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
return dbID
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestMustToDBUUIDArray(t *testing.T) {
|
||||
|
||||
dbIDs := database.MustToDBUUIDArray(ids)
|
||||
|
||||
assert.Equal(t, len(ids), len(dbIDs))
|
||||
assert.Len(t, dbIDs, len(ids))
|
||||
for index, id := range dbIDs {
|
||||
assert.Equal(t, database.MustToDBUUID(ids[index]), id)
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func TestMustToUUIDArray(t *testing.T) {
|
||||
|
||||
ids := database.MustToUUIDArray(dbIDs)
|
||||
|
||||
assert.Equal(t, len(ids), len(dbIDs))
|
||||
assert.Len(t, ids, len(dbIDs))
|
||||
for index, id := range dbIDs {
|
||||
assert.Equal(t, database.MustToDBUUID(ids[index]), id)
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@ import (
|
||||
|
||||
func TestCollector(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
pool, container := createDB(t, ctx)
|
||||
defer container.Terminate(ctx)
|
||||
queries := repository.New(pool)
|
||||
db, cleanup := createDB(t, ctx)
|
||||
defer cleanup()
|
||||
queries := repository.New(db.pool)
|
||||
|
||||
collectorID := database.MustToDBUUID(uuid.New())
|
||||
|
||||
collectorQueries, err := queries.GetCollectorQueries(ctx, collectorID)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, len(collectorQueries))
|
||||
assert.Len(t, collectorQueries, 0)
|
||||
assert.ElementsMatch(t, []repository.GetCollectorQueriesRow{}, collectorQueries)
|
||||
|
||||
jobID := database.MustToDBUUID(uuid.New())
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
|
||||
func TestQueries(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
pool, container := createDB(t, ctx)
|
||||
defer container.Terminate(ctx)
|
||||
queries := repository.New(pool)
|
||||
db, cleanup := createDB(t, ctx)
|
||||
defer cleanup()
|
||||
queries := repository.New(db.pool)
|
||||
|
||||
contextQueryID, err := queries.CreateQuery(ctx, repository.Querytype(repository.QuerytypeContextFull))
|
||||
assert.Nil(t, err)
|
||||
@@ -60,7 +60,7 @@ func TestQueries(t *testing.T) {
|
||||
|
||||
qs, err := queries.ListQueries(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(qs))
|
||||
assert.Len(t, qs, 2)
|
||||
log.Print(qs)
|
||||
assert.ElementsMatch(t, []repository.Fullactivequery{
|
||||
{ID: jsonQueryID,
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
|
||||
func TestResults(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
pool, container := createDB(t, ctx)
|
||||
defer container.Terminate(ctx)
|
||||
queries := repository.New(pool)
|
||||
db, cleanup := createDB(t, ctx)
|
||||
defer cleanup()
|
||||
queries := repository.New(db.pool)
|
||||
|
||||
jsonQueryID, err := queries.CreateQuery(ctx, repository.Querytype(repository.QuerytypeJsonExtractor))
|
||||
assert.Nil(t, err)
|
||||
@@ -46,7 +46,7 @@ func TestResults(t *testing.T) {
|
||||
Textversion: textVersion,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(resultsByDoc))
|
||||
assert.Len(t, resultsByDoc, 1)
|
||||
assert.ElementsMatch(t, []repository.ListResultsByDocumentIDRow{
|
||||
{
|
||||
ID: jsonResultID,
|
||||
@@ -57,7 +57,7 @@ func TestResults(t *testing.T) {
|
||||
|
||||
results, err := queries.ListResultValuesByID(ctx, []pgtype.UUID{jsonResultID})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(results))
|
||||
assert.Len(t, results, 1)
|
||||
assert.ElementsMatch(t, []repository.ListResultValuesByIDRow{
|
||||
{
|
||||
ID: jsonResultID,
|
||||
|
||||
@@ -14,7 +14,12 @@ import (
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
func createDB(t *testing.T, ctx context.Context) (*pgxpool.Pool, testcontainers.Container) {
|
||||
type db struct {
|
||||
pool *pgxpool.Pool
|
||||
container *testcontainers.Container
|
||||
}
|
||||
|
||||
func createDB(t *testing.T, ctx context.Context) (*db, func()) {
|
||||
port, err := nat.NewPort("tcp", "5432")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create port: %v", err)
|
||||
@@ -25,7 +30,7 @@ func createDB(t *testing.T, ctx context.Context) (*pgxpool.Pool, testcontainers.
|
||||
user := "postgres"
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "postgres:latest",
|
||||
Image: "postgres:17.2-alpine3.21",
|
||||
Env: map[string]string{
|
||||
"POSTGRES_DB": name,
|
||||
"POSTGRES_USER": user,
|
||||
@@ -65,5 +70,13 @@ func createDB(t *testing.T, ctx context.Context) (*pgxpool.Pool, testcontainers.
|
||||
|
||||
pool := database.GetDBPool(ctx)
|
||||
|
||||
return pool, container
|
||||
return &db{
|
||||
pool: pool,
|
||||
container: &container,
|
||||
}, func() {
|
||||
err := container.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,13 +98,13 @@ func TestSyncDBFail(t *testing.T) {
|
||||
pgxmock.NewRows([]string{"id", "jobId", "minCleanVersion", "minTextVersion"}).
|
||||
AddRow(dbCollectorId, dbJobID, minCleanVersion, minTextVersion),
|
||||
)
|
||||
errr := "database failure"
|
||||
dbErr := "database failure"
|
||||
pool.ExpectQuery("name: ListResultsByDocumentID :many").WithArgs(database.MustToDBUUID(doc.ID), minCleanVersion, minTextVersion).
|
||||
WillReturnError(errors.New(errr))
|
||||
WillReturnError(errors.New(dbErr))
|
||||
|
||||
docSvc = document.New(db)
|
||||
err = docSvc.Sync(ctx, &doc)
|
||||
assert.EqualError(t, err, errr)
|
||||
assert.EqualError(t, err, dbErr)
|
||||
|
||||
pool.ExpectQuery("name: GetCollectorFromJobID :one").WithArgs(dbJobID).
|
||||
WillReturnRows(
|
||||
@@ -115,13 +115,13 @@ func TestSyncDBFail(t *testing.T) {
|
||||
WillReturnRows(
|
||||
pgxmock.NewRows([]string{"id", "queryId", "queryVersion"}),
|
||||
)
|
||||
errr = "database failure"
|
||||
dbErr = "database failure"
|
||||
pool.ExpectQuery("name: GetCollectorQueries :many").WithArgs(dbCollectorId).
|
||||
WillReturnError(errors.New(errr))
|
||||
WillReturnError(errors.New(dbErr))
|
||||
|
||||
docSvc = document.New(db)
|
||||
err = docSvc.Sync(ctx, &doc)
|
||||
assert.EqualError(t, err, errr)
|
||||
assert.EqualError(t, err, dbErr)
|
||||
|
||||
pool.ExpectQuery("name: GetCollectorFromJobID :one").WithArgs(dbJobID).
|
||||
WillReturnRows(
|
||||
@@ -132,18 +132,18 @@ func TestSyncDBFail(t *testing.T) {
|
||||
WillReturnRows(
|
||||
pgxmock.NewRows([]string{"id", "queryId", "queryVersion"}),
|
||||
)
|
||||
errr = "database failure"
|
||||
dbErr = "database failure"
|
||||
pool.ExpectQuery("name: GetCollectorQueries :many").WithArgs(dbCollectorId).
|
||||
WillReturnRows(
|
||||
pgxmock.NewRows([]string{"collectorId", "queryId", "type", "queryVersion", "requiredIds"}).
|
||||
AddRow(dbCollectorId, dbQueryID, repository.NullQuerytype{Querytype: repository.QuerytypeJsonExtractor, Valid: true}, pgtype.Int4{Int32: int32(1), Valid: true}, []pgtype.UUID{}),
|
||||
)
|
||||
pool.ExpectQuery("name: ListResultValuesByID :many").WithArgs([]pgtype.UUID{}).
|
||||
WillReturnError(errors.New(errr))
|
||||
WillReturnError(errors.New(dbErr))
|
||||
|
||||
docSvc = document.New(db)
|
||||
err = docSvc.Sync(ctx, &doc)
|
||||
assert.EqualError(t, err, errr)
|
||||
assert.EqualError(t, err, dbErr)
|
||||
}
|
||||
|
||||
func TestSync(t *testing.T) {
|
||||
|
||||
@@ -164,8 +164,6 @@ func TestJSONProcessJSON(t *testing.T) {
|
||||
assert.EqualError(t, err, "invalid character '}' looking for beginning of value")
|
||||
assert.Empty(t, value)
|
||||
|
||||
config = "{\"path\":\"key\"}"
|
||||
|
||||
pool.ExpectQuery("name: GetQueryConfig :one").WithArgs(database.MustToDBUUID(query.ID), query.Version).
|
||||
WillReturnRows(
|
||||
pgxmock.NewRows([]string{"id", "config"}),
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
func New(ctx context.Context) func() {
|
||||
if os.Getenv("ENABLE_OTEL") != "1" {
|
||||
log.Println("OpenTelemetry is disabled. Set ENABLE_OTEL to enable.")
|
||||
return func() {} // No-op shutdown function
|
||||
return func() {}
|
||||
}
|
||||
|
||||
exporter, err := otlptracegrpc.New(ctx)
|
||||
@@ -28,6 +28,9 @@ func New(ctx context.Context) func() {
|
||||
otel.SetTracerProvider(tp)
|
||||
|
||||
return func() {
|
||||
_ = tp.Shutdown(ctx)
|
||||
err := tp.Shutdown(ctx)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/pashagolub/pgxmock/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -40,10 +39,7 @@ func TestGet(t *testing.T) {
|
||||
Config: config,
|
||||
}
|
||||
|
||||
dbReqIDs := make([]pgtype.UUID, len(query.RequiredQueryIDs))
|
||||
for index, id := range query.RequiredQueryIDs {
|
||||
dbReqIDs[index] = database.MustToDBUUID(id)
|
||||
}
|
||||
dbReqIDs := database.MustToDBUUIDArray(query.RequiredQueryIDs)
|
||||
|
||||
pool.ExpectQuery("name: GetQuery :one").WithArgs(database.MustToDBUUID(query.ID)).WillReturnRows(
|
||||
pgxmock.NewRows([]string{"id", "type", "activeVersion", "latestVersion", "config", "requiredIds"}).
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/pashagolub/pgxmock/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -40,10 +39,7 @@ func TestList(t *testing.T) {
|
||||
Config: config,
|
||||
}
|
||||
|
||||
dbReqIDs := make([]pgtype.UUID, len(q.RequiredQueryIDs))
|
||||
for index, id := range q.RequiredQueryIDs {
|
||||
dbReqIDs[index] = database.MustToDBUUID(id)
|
||||
}
|
||||
dbReqIDs := database.MustToDBUUIDArray(q.RequiredQueryIDs)
|
||||
|
||||
filters := query.ListFilters{}
|
||||
|
||||
|
||||
@@ -76,7 +76,8 @@ func TestGetCollectorQueries(t *testing.T) {
|
||||
|
||||
pool.ExpectQuery("name: GetCollectorQueries :many").WithArgs(dbCollectorID).WillReturnRows(rows)
|
||||
|
||||
svc.getCollectorQueries(ctx)
|
||||
err = svc.getCollectorQueries(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.EqualExportedValues(t, collectorQueries, svc.collectorQueries)
|
||||
}
|
||||
|
||||
|
||||
@@ -130,9 +130,9 @@ func TestQueueFail(t *testing.T) {
|
||||
coll, err := collector.NewByJobId(ctx, db, jobID)
|
||||
assert.Nil(t, err)
|
||||
|
||||
errr := "database failure"
|
||||
dbErr := "database failure"
|
||||
pool.ExpectQuery("name: GetCollectorQueries :many").WithArgs(dbCollectorID).
|
||||
WillReturnError(errors.New(errr))
|
||||
WillReturnError(errors.New(dbErr))
|
||||
|
||||
results := []*result.Result{}
|
||||
|
||||
@@ -141,5 +141,5 @@ func TestQueueFail(t *testing.T) {
|
||||
textVersion := int32(1)
|
||||
|
||||
_, err = queryqueue.New(ctx, db, coll, results, docID, cleanVersion, textVersion)
|
||||
assert.EqualError(t, err, errr)
|
||||
assert.EqualError(t, err, dbErr)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||
)
|
||||
|
||||
type QueueConfig struct {
|
||||
type Config struct {
|
||||
URL string
|
||||
Client *sqs.Client
|
||||
}
|
||||
|
||||
type Controller interface {
|
||||
Process(ctx context.Context, config *Config, msg *types.Message) error
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||
)
|
||||
|
||||
func Delete(ctx context.Context, config *QueueConfig, msg *types.Message) error {
|
||||
func Delete(ctx context.Context, config *Config, msg *types.Message) error {
|
||||
_, err := config.Client.DeleteMessage(ctx, &sqs.DeleteMessageInput{
|
||||
QueueUrl: aws.String(config.URL),
|
||||
ReceiptHandle: msg.ReceiptHandle,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package queue_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"queryorchestration/internal/queue"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
queueConfig, cleanup := createQueue(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
err := queue.Send(ctx, queueConfig.Config, "{}", map[string]types.MessageAttributeValue{})
|
||||
assert.Nil(t, err)
|
||||
|
||||
result, err := queue.Receive(ctx, queueConfig.Config, []string{})
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Len(t, result.Messages, 1)
|
||||
message := result.Messages[0]
|
||||
|
||||
err = queue.Delete(ctx, queueConfig.Config, &message)
|
||||
assert.Nil(t, err)
|
||||
|
||||
result, err = queue.Receive(ctx, queueConfig.Config, []string{})
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, result.Messages, 0)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"queryorchestration/internal/env"
|
||||
"queryorchestration/internal/server"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs"
|
||||
)
|
||||
|
||||
type ListenerConfig struct {
|
||||
Controller func(*server.Config) Controller
|
||||
BasePath string
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
controller Controller
|
||||
queueConnection *Config
|
||||
}
|
||||
|
||||
func NewServer(ctx context.Context, lConfig *ListenerConfig) *Server {
|
||||
serverCfg := server.New(ctx, &server.NewConfig{
|
||||
BasePath: lConfig.BasePath,
|
||||
})
|
||||
|
||||
cfg, err := config.LoadDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
log.Panicf("Unable to load SDK config: %v", err)
|
||||
}
|
||||
|
||||
queueURL := env.GetPanic("QUEUE_URL")
|
||||
|
||||
sqsClient := sqs.NewFromConfig(cfg)
|
||||
|
||||
return &Server{
|
||||
controller: lConfig.Controller(serverCfg),
|
||||
queueConnection: &Config{
|
||||
URL: queueURL,
|
||||
Client: sqsClient,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Listen(ctx context.Context) {
|
||||
config := &PollConfig{
|
||||
Controller: s.controller,
|
||||
Config: s.queueConnection,
|
||||
}
|
||||
|
||||
log.Print("Listening to queue")
|
||||
PollMessages(ctx, config)
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package queue_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"queryorchestration/internal/database"
|
||||
"queryorchestration/internal/queue"
|
||||
"queryorchestration/internal/server"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, cleanup := createQueue(t, ctx)
|
||||
defer cleanup()
|
||||
_, cleanup = createDB(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||
defer cancel()
|
||||
|
||||
controller := func(cfg *server.Config) queue.Controller {
|
||||
return MockController{}
|
||||
}
|
||||
|
||||
queue.NewServer(ctx, &queue.ListenerConfig{
|
||||
Controller: controller,
|
||||
BasePath: "../..",
|
||||
})
|
||||
}
|
||||
|
||||
func TestListen(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
queue := queue.Server{}
|
||||
|
||||
assert.Panics(t, func() { queue.Listen(ctx) })
|
||||
}
|
||||
|
||||
type db struct {
|
||||
pool *pgxpool.Pool
|
||||
container *testcontainers.Container
|
||||
}
|
||||
|
||||
func createDB(t *testing.T, ctx context.Context) (*db, func()) {
|
||||
port, err := nat.NewPort("tcp", "5432")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create port: %v", err)
|
||||
}
|
||||
|
||||
name := "queryorchestration"
|
||||
pass := "pass"
|
||||
user := "postgres"
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "postgres:17.2-alpine3.21",
|
||||
Env: map[string]string{
|
||||
"POSTGRES_DB": name,
|
||||
"POSTGRES_USER": user,
|
||||
"POSTGRES_PASSWORD": pass,
|
||||
},
|
||||
ExposedPorts: []string{port.Port()},
|
||||
WaitingFor: wait.ForListeningPort(port),
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
t.Setenv("DB_USER", user)
|
||||
t.Setenv("DB_PASS", pass)
|
||||
t.Setenv("DB_HOST", host)
|
||||
t.Setenv("DB_PORT", fmt.Sprint(mappedPort.Int()))
|
||||
t.Setenv("DB_NAME", name)
|
||||
t.Setenv("DB_NOSSL", "1")
|
||||
|
||||
pool := database.GetDBPool(ctx)
|
||||
|
||||
return &db{
|
||||
pool: pool,
|
||||
container: &container,
|
||||
}, func() {
|
||||
err := container.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type PollConfig struct {
|
||||
Config *Config
|
||||
Controller Controller
|
||||
}
|
||||
|
||||
func PollMessages(ctx context.Context, queueConfig *PollConfig) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
err := PollMessage(ctx, queueConfig)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func PollMessage(ctx context.Context, queueConfig *PollConfig) error {
|
||||
result, err := Receive(ctx, queueConfig.Config, []string{
|
||||
"type",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("message fetch fail: %v", err)
|
||||
}
|
||||
|
||||
for _, message := range result.Messages {
|
||||
go func() {
|
||||
err := queueConfig.Controller.Process(ctx, queueConfig.Config, &message)
|
||||
if err != nil {
|
||||
log.Printf("message process fail: %v", err)
|
||||
}
|
||||
|
||||
err = Delete(ctx, queueConfig.Config, &message)
|
||||
if err != nil {
|
||||
log.Printf("message delete fail: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package queue_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"queryorchestration/internal/queue"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type MockController struct{}
|
||||
|
||||
func (s MockController) Process(ctx context.Context, config *queue.Config, msg *types.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPollMessages(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
queueConfig, cleanup := createQueue(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
controller := MockController{}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||
defer cancel()
|
||||
|
||||
queue.PollMessages(ctx, &queue.PollConfig{
|
||||
Config: queueConfig.Config,
|
||||
Controller: controller,
|
||||
})
|
||||
}
|
||||
|
||||
func TestPollMessage(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
queueConfig, cleanup := createQueue(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
controller := MockController{}
|
||||
|
||||
err := queue.PollMessage(ctx, &queue.PollConfig{
|
||||
Config: queueConfig.Config,
|
||||
Controller: controller,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
type queueConfig struct {
|
||||
Container *testcontainers.Container
|
||||
Config *queue.QueueConfig
|
||||
Config *queue.Config
|
||||
}
|
||||
|
||||
func createQueue(t *testing.T, ctx context.Context) (*queueConfig, func()) {
|
||||
@@ -32,7 +32,7 @@ func createQueue(t *testing.T, ctx context.Context) (*queueConfig, func()) {
|
||||
}
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "localstack/localstack:latest",
|
||||
Image: "localstack/localstack:4.0.3",
|
||||
Env: map[string]string{
|
||||
"AWS_ACCESS_KEY_ID": provider.Value.AccessKeyID,
|
||||
"AWS_SECRET_ACCESS_KEY": provider.Value.SecretAccessKey,
|
||||
@@ -82,11 +82,14 @@ func createQueue(t *testing.T, ctx context.Context) (*queueConfig, func()) {
|
||||
|
||||
return &queueConfig{
|
||||
Container: &container,
|
||||
Config: &queue.QueueConfig{
|
||||
Config: &queue.Config{
|
||||
Client: client,
|
||||
URL: *queueM.QueueUrl,
|
||||
},
|
||||
}, func() {
|
||||
container.Terminate(ctx)
|
||||
err := container.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs"
|
||||
)
|
||||
|
||||
func Receive(ctx context.Context, config *Config, attributes []string) (*sqs.ReceiveMessageOutput, error) {
|
||||
return config.Client.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{
|
||||
QueueUrl: &config.URL,
|
||||
MaxNumberOfMessages: 1,
|
||||
WaitTimeSeconds: 2,
|
||||
VisibilityTimeout: 2,
|
||||
MessageAttributeNames: attributes,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package queue_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"queryorchestration/internal/queue"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReceive(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
queueConfig, cleanup := createQueue(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
attributes := map[string]types.MessageAttributeValue{}
|
||||
|
||||
err := queue.Send(ctx, queueConfig.Config, "example_body", attributes)
|
||||
assert.Nil(t, err)
|
||||
|
||||
result, err := queue.Receive(ctx, queueConfig.Config, []string{})
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Len(t, result.Messages, 1)
|
||||
message := result.Messages[0]
|
||||
|
||||
log.Print(message.Attributes)
|
||||
|
||||
assert.Equal(t, "\"example_body\"", *message.Body)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||
)
|
||||
|
||||
func Send(ctx context.Context, config *QueueConfig, typeName string, body interface{}) error {
|
||||
func Send(ctx context.Context, config *Config, body interface{}, attributes map[string]types.MessageAttributeValue) error {
|
||||
jsonBytes, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -18,14 +18,9 @@ func Send(ctx context.Context, config *QueueConfig, typeName string, body interf
|
||||
strBody := string(jsonBytes)
|
||||
|
||||
_, err = config.Client.SendMessage(ctx, &sqs.SendMessageInput{
|
||||
MessageAttributes: map[string]types.MessageAttributeValue{
|
||||
"type": {
|
||||
DataType: aws.String("String"),
|
||||
StringValue: aws.String(typeName),
|
||||
},
|
||||
},
|
||||
QueueUrl: aws.String(config.URL),
|
||||
MessageBody: aws.String(strBody),
|
||||
MessageAttributes: attributes,
|
||||
QueueUrl: aws.String(config.URL),
|
||||
MessageBody: aws.String(strBody),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package queue_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"queryorchestration/internal/queue"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
queueConfig, cleanup := createQueue(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
err := queue.Send(ctx, queueConfig.Config, "{}", map[string]types.MessageAttributeValue{
|
||||
"type": {
|
||||
DataType: aws.String("String"),
|
||||
StringValue: aws.String("EXAMPLE_TYPE"),
|
||||
},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package result_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"queryorchestration/internal/database"
|
||||
"queryorchestration/internal/database/repository"
|
||||
"queryorchestration/internal/result"
|
||||
@@ -40,11 +40,11 @@ func TestStore(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, id)
|
||||
|
||||
errr := "database failing"
|
||||
dbErr := "database failing"
|
||||
pool.ExpectQuery("name: SetResult :one").WithArgs(database.MustToDBUUID(resultStore.QueryID), database.MustToDBUUID(resultStore.DocumentID), resultStore.Value, resultStore.CleanVersion, resultStore.TextVersion, resultStore.QueryVersion).
|
||||
WillReturnError(fmt.Errorf(errr))
|
||||
WillReturnError(errors.New(dbErr))
|
||||
|
||||
id, err = result.Store(ctx, queries, &resultStore)
|
||||
assert.EqualError(t, err, errr)
|
||||
assert.EqualError(t, err, dbErr)
|
||||
assert.Equal(t, uuid.Nil, id)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"queryorchestration/internal/database"
|
||||
"queryorchestration/internal/database/repository"
|
||||
"queryorchestration/internal/otel"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Database *database.Connection
|
||||
Validator *validator.Validate
|
||||
}
|
||||
|
||||
type NewConfig struct {
|
||||
BasePath string
|
||||
}
|
||||
|
||||
func New(ctx context.Context, cfg *NewConfig) *Config {
|
||||
closeTracer := otel.New(ctx)
|
||||
defer closeTracer()
|
||||
|
||||
database.RunMigrations(&database.MigrationConfig{
|
||||
BasePath: cfg.BasePath,
|
||||
})
|
||||
|
||||
dbPool := database.GetDBPool(ctx)
|
||||
dbQueries := repository.New(dbPool)
|
||||
db := &database.Connection{
|
||||
Pool: dbPool,
|
||||
Queries: dbQueries,
|
||||
}
|
||||
valid := validator.New()
|
||||
|
||||
return &Config{
|
||||
Database: db,
|
||||
Validator: valid,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package server_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"queryorchestration/internal/database"
|
||||
"queryorchestration/internal/server"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
_, cleanup := createDB(t, ctx)
|
||||
defer cleanup()
|
||||
|
||||
newCfg := &server.NewConfig{
|
||||
BasePath: "../..",
|
||||
}
|
||||
|
||||
cfg := server.New(ctx, newCfg)
|
||||
assert.NotNil(t, cfg)
|
||||
}
|
||||
|
||||
type db struct {
|
||||
pool *pgxpool.Pool
|
||||
container *testcontainers.Container
|
||||
}
|
||||
|
||||
func createDB(t *testing.T, ctx context.Context) (*db, func()) {
|
||||
port, err := nat.NewPort("tcp", "5432")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create port: %v", err)
|
||||
}
|
||||
|
||||
name := "queryorchestration"
|
||||
pass := "pass"
|
||||
user := "postgres"
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "postgres:17.2-alpine3.21",
|
||||
Env: map[string]string{
|
||||
"POSTGRES_DB": name,
|
||||
"POSTGRES_USER": user,
|
||||
"POSTGRES_PASSWORD": pass,
|
||||
},
|
||||
ExposedPorts: []string{port.Port()},
|
||||
WaitingFor: wait.ForListeningPort(port),
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
t.Setenv("DB_USER", user)
|
||||
t.Setenv("DB_PASS", pass)
|
||||
t.Setenv("DB_HOST", host)
|
||||
t.Setenv("DB_PORT", fmt.Sprint(mappedPort.Int()))
|
||||
t.Setenv("DB_NAME", name)
|
||||
t.Setenv("DB_NOSSL", "1")
|
||||
|
||||
pool := database.GetDBPool(ctx)
|
||||
|
||||
return &db{
|
||||
pool: pool,
|
||||
container: &container,
|
||||
}, func() {
|
||||
err := container.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.49.1. DO NOT EDIT.
|
||||
// Code generated by mockery v2.50.0. DO NOT EDIT.
|
||||
|
||||
package repository
|
||||
|
||||
|
||||
+14
-1
@@ -1,19 +1,26 @@
|
||||
---
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
includes:
|
||||
deps:
|
||||
dir: "{{.CONTEXT}}"
|
||||
taskfile: dependencies.yml
|
||||
test:
|
||||
dir: "{{.CONTEXT}}"
|
||||
taskfile: tests.yml
|
||||
docker:
|
||||
dir: "{{.CONTEXT}}"
|
||||
taskfile: docker.yml
|
||||
proto:
|
||||
dir: "{{.CONTEXT}}"
|
||||
taskfile: proto.yml
|
||||
compose:
|
||||
dir: "{{.CONTEXT}}"
|
||||
taskfile: compose.yml
|
||||
db:
|
||||
dir: "{{.CONTEXT}}"
|
||||
taskfile: database.yml
|
||||
|
||||
vars:
|
||||
@@ -39,12 +46,18 @@ tasks:
|
||||
cmds:
|
||||
- task code:lint:fix
|
||||
- task proto:lint:fix
|
||||
- task db:lint
|
||||
- task yaml:lint
|
||||
- task docker:lint
|
||||
- task compose:lint
|
||||
- task shell:lint
|
||||
code:lint:
|
||||
cmds:
|
||||
- golangci-lint run
|
||||
code:lint:fix:
|
||||
cmds:
|
||||
- gofmt -w .
|
||||
- golangci-lint run
|
||||
yaml:lint:
|
||||
cmds:
|
||||
- yamllint . -s
|
||||
@@ -53,4 +66,4 @@ tasks:
|
||||
- shellcheck --shell=sh scripts/install-deps.sh
|
||||
docs:
|
||||
cmds:
|
||||
- godoc -http=:6060
|
||||
- godoc -http=:6060
|
||||
|
||||
+10
-3
@@ -1,9 +1,10 @@
|
||||
---
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
vars:
|
||||
COMPOSE_FILE: "{{.CONTEXT}}/deployments/compose.yaml"
|
||||
COMPOSE_FILE: "deployments/compose.yaml"
|
||||
|
||||
tasks:
|
||||
build:
|
||||
@@ -11,7 +12,13 @@ tasks:
|
||||
- docker compose -f {{.COMPOSE_FILE}} build
|
||||
up:
|
||||
cmds:
|
||||
- docker compose -f {{.COMPOSE_FILE}} up
|
||||
- docker compose -f {{.COMPOSE_FILE}} up --no-recreate
|
||||
up:bg:
|
||||
cmds:
|
||||
- docker compose -f {{.COMPOSE_FILE}} up --no-recreate -d
|
||||
down:
|
||||
cmds:
|
||||
- docker compose -f {{.COMPOSE_FILE}} down
|
||||
lint:
|
||||
cmds:
|
||||
- docker compose -f {{.COMPOSE_FILE}} config
|
||||
- docker compose -f {{.COMPOSE_FILE}} config
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
---
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
vars:
|
||||
MIGRATIONS: "{{.CONTEXT}}/database/migrations"
|
||||
DATABASE_URI: "postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
|
||||
MIGRATIONS: "database/migrations"
|
||||
|
||||
tasks:
|
||||
generate:
|
||||
cmds:
|
||||
- sqlc generate --file ../sqlc.yml
|
||||
- |
|
||||
task compose:up:bg
|
||||
task db:mig:run
|
||||
sqlc generate --file ../sqlc.yml
|
||||
lint:
|
||||
cmds:
|
||||
- sqlc vet --file ../sqlc.yml
|
||||
@@ -21,4 +24,4 @@ tasks:
|
||||
migrate create -ext sql -dir {{.MIGRATIONS}} $name
|
||||
mig:run:
|
||||
cmds:
|
||||
- migrate -path {{.MIGRATIONS}} -database {{.DATABASE_URI}} up
|
||||
- migrate -path {{.MIGRATIONS}} -database {{.DB_URI}} up
|
||||
|
||||
+14
-11
@@ -1,3 +1,4 @@
|
||||
---
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
@@ -5,20 +6,22 @@ version: '3'
|
||||
tasks:
|
||||
install:
|
||||
cmds:
|
||||
- go install golang.org/x/tools/cmd/godoc@latest
|
||||
- go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
- go install github.com/yoheimuta/protolint/cmd/protolint@latest
|
||||
- go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
- go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
- go install github.com/zabio3/godolint@latest
|
||||
- go install github.com/wasilibs/go-yamllint/cmd/yamllint@latest
|
||||
- go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
|
||||
- go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
|
||||
- go install github.com/vektra/mockery/v2@latest
|
||||
- go install golang.org/x/tools/cmd/godoc@v0.29.0
|
||||
- go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
|
||||
- go install github.com/yoheimuta/protolint/cmd/protolint@v0.52.0
|
||||
- go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.0
|
||||
- go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1
|
||||
- go install github.com/zabio3/godolint@v1.0.3
|
||||
- go install github.com/wasilibs/go-yamllint/cmd/yamllint@v1.35.1
|
||||
- go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.27.0
|
||||
- |
|
||||
go install -tags 'postgres' \
|
||||
github.com/golang-migrate/migrate/v4/cmd/migrate@v4.18.1
|
||||
- go install github.com/vektra/mockery/v2@v2.50.0
|
||||
- curl -sfL https://direnv.net/install.sh | bash
|
||||
- direnv allow
|
||||
- go mod download
|
||||
tidy:
|
||||
cmds:
|
||||
- go mod tidy
|
||||
- go mod vendor
|
||||
- go mod vendor
|
||||
|
||||
+3
-3
@@ -1,9 +1,10 @@
|
||||
---
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
vars:
|
||||
DOCKERFILE: "{{.CONTEXT}}/build/Dockerfile"
|
||||
DOCKERFILE: "build/Dockerfile"
|
||||
|
||||
tasks:
|
||||
lint:
|
||||
@@ -11,5 +12,4 @@ tasks:
|
||||
- godolint {{.DOCKERFILE}}
|
||||
build:
|
||||
cmds:
|
||||
- docker build --build-arg TARGETCMD=queryService -t {{.IMAGE_NAME}}_queryservice -f {{.DOCKERFILE}} {{.CONTEXT}}
|
||||
- docker build --build-arg TARGETCMD=queryRunner -t {{.IMAGE_NAME}}_queryrunner -f {{.DOCKERFILE}} {{.CONTEXT}}
|
||||
- docker build -t {{.IMAGE_NAME}} -f {{.DOCKERFILE}} .
|
||||
|
||||
+8
-3
@@ -1,10 +1,11 @@
|
||||
---
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
vars:
|
||||
PROTO_DIR: "{{.CONTEXT}}/serviceInterfaces"
|
||||
API_DIR: "{{.CONTEXT}}/api"
|
||||
PROTO_DIR: serviceInterfaces
|
||||
API_DIR: api
|
||||
|
||||
tasks:
|
||||
lint:
|
||||
@@ -15,4 +16,8 @@ tasks:
|
||||
- protolint lint -fix {{.PROTO_DIR}}
|
||||
generate:
|
||||
cmds:
|
||||
- protoc --proto_path={{.PROTO_DIR}} --go_out={{.API_DIR}}/serviceInterfaces --go-grpc_out={{.API_DIR}} --go_opt=paths=source_relative --experimental_allow_proto3_optional main.proto
|
||||
- |
|
||||
protoc --proto_path={{.PROTO_DIR}} \
|
||||
--go_out={{.API_DIR}}/serviceInterfaces --go-grpc_out={{.API_DIR}} \
|
||||
--go_opt=paths=source_relative --experimental_allow_proto3_optional \
|
||||
main.proto
|
||||
|
||||
+15
-8
@@ -1,3 +1,4 @@
|
||||
---
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
@@ -8,25 +9,31 @@ includes:
|
||||
internal: true
|
||||
|
||||
vars:
|
||||
COVERAGE_FILE: "{{.CONTEXT}}/{{.OUT_DIR}}/coverage.out"
|
||||
COVERAGE_FILE: "{{.OUT_DIR}}/coverage.out"
|
||||
|
||||
tasks:
|
||||
mocks:
|
||||
cmds:
|
||||
- mockery
|
||||
unit:
|
||||
vars:
|
||||
INTERNAL: "{{.CONTEXT}}/internal/..."
|
||||
API: "{{.CONTEXT}}/api/..."
|
||||
INTERNAL: "./internal/..."
|
||||
API: "./api/..."
|
||||
cmds:
|
||||
- go test {{.INTERNAL}} {{.API}} -coverpkg={{.INTERNAL}},{{.API}} -coverprofile={{.COVERAGE_FILE}}
|
||||
- |
|
||||
go test {{.INTERNAL}} {{.API}} \
|
||||
-coverpkg={{.INTERNAL}},{{.API}} -coverprofile={{.COVERAGE_FILE}}
|
||||
unit:coverage:
|
||||
deps:
|
||||
- unit
|
||||
vars:
|
||||
TMP_FILE: "{{.CONTEXT}}/{{.OUT_DIR}}/coverage.tmp"
|
||||
TMP_FILE: "{{.OUT_DIR}}/coverage.tmp"
|
||||
cmds:
|
||||
- go tool cover -func={{.COVERAGE_FILE}} > {{.TMP_FILE}}
|
||||
- cat {{.TMP_FILE}}
|
||||
- |
|
||||
COVERAGE=$(grep total: {{.TMP_FILE}} | awk '{print $3}' | sed 's/%//' | bc)
|
||||
COVERAGE=$(grep total: {{.TMP_FILE}} \
|
||||
| awk '{print $3}' | sed 's/%//' | bc)
|
||||
echo ""
|
||||
echo "Coverage Threshold: {{.COVERAGE_THRESHOLD}}%"
|
||||
echo "Total Coverage: $COVERAGE%"
|
||||
@@ -44,7 +51,7 @@ tasks:
|
||||
deps:
|
||||
- docker:build
|
||||
cmds:
|
||||
- go test -count=1 -v {{.CONTEXT}}/test/...
|
||||
- go test -count=1 -v test/...
|
||||
integration:nobuild:
|
||||
cmds:
|
||||
- go test -v {{.CONTEXT}}/test/...
|
||||
- go test -v test/...
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
version: "2"
|
||||
servers:
|
||||
- engine: postgresql
|
||||
uri: "postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
|
||||
uri: "${DB_URI}"
|
||||
sql:
|
||||
- name: "db"
|
||||
engine: "postgresql"
|
||||
@@ -13,4 +14,4 @@ sql:
|
||||
go:
|
||||
package: "repository"
|
||||
out: "internal/database/repository"
|
||||
sql_package: "pgx/v5"
|
||||
sql_package: "pgx/v5"
|
||||
|
||||
+24
-10
@@ -26,17 +26,19 @@ func createAPIContainer(t *testing.T, ctx context.Context, config *apiContainerC
|
||||
}
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: fmt.Sprintf("queryorchestration_%s:latest", config.ServiceName),
|
||||
Image: "queryorchestration:latest",
|
||||
Env: map[string]string{
|
||||
"DB_USER": config.DB.User,
|
||||
"DB_PASS": config.DB.Password,
|
||||
"DB_HOST": config.DB.Host,
|
||||
"DB_NAME": config.DB.Name,
|
||||
"DB_PORT": strconv.Itoa(config.DB.Port),
|
||||
"DB_USER": config.DB.User,
|
||||
"DB_PASS": config.DB.Password,
|
||||
"DB_HOST": config.DB.Host,
|
||||
"DB_NAME": config.DB.Name,
|
||||
"DB_PORT": strconv.Itoa(config.DB.Port),
|
||||
"DB_NOSSL": "1",
|
||||
},
|
||||
ExposedPorts: []string{port.Port()},
|
||||
WaitingFor: wait.ForListeningPort(port),
|
||||
WaitingFor: wait.ForLog("Listening on port 8080"),
|
||||
Networks: []string{config.Network.Name},
|
||||
Entrypoint: []string{fmt.Sprintf("./bin/%s", config.ServiceName)},
|
||||
}
|
||||
|
||||
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
@@ -65,7 +67,10 @@ func createAPIContainer(t *testing.T, ctx context.Context, config *apiContainerC
|
||||
}
|
||||
|
||||
return conn, func() {
|
||||
container.Terminate(ctx)
|
||||
err := container.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +87,17 @@ func createAPIDependencies(t *testing.T, ctx context.Context, serviceName string
|
||||
|
||||
return conn, func() {
|
||||
testcontainers.CleanupNetwork(t, network)
|
||||
dbContainer.Terminate(ctx)
|
||||
|
||||
err := dbContainer.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
containerCleanup()
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func createDB(t *testing.T, ctx context.Context, network *testcontainers.DockerN
|
||||
}
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "postgres:latest",
|
||||
Image: "postgres:17.2-alpine3.21",
|
||||
Env: map[string]string{
|
||||
"POSTGRES_DB": config.Name,
|
||||
"POSTGRES_USER": config.User,
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
func TestQueryRunner(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
queue, cleanup := createQueueDependencies(t, ctx, "queryrunner")
|
||||
queue, cleanup := createQueueDependencies(t, ctx, "queryRunner")
|
||||
defer cleanup()
|
||||
|
||||
document := document.Document{
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
func TestQueryService(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
conn, cleanup := createAPIDependencies(t, ctx, "queryservice")
|
||||
conn, cleanup := createAPIDependencies(t, ctx, "queryService")
|
||||
defer cleanup()
|
||||
|
||||
client := serviceinterfaces.NewQueryServiceClient(conn)
|
||||
|
||||
@@ -32,7 +32,7 @@ func createQueueContainer(t *testing.T, ctx context.Context, config *queueContai
|
||||
}
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: fmt.Sprintf("queryorchestration_%s:latest", config.ServiceName),
|
||||
Image: "queryorchestration:latest",
|
||||
Env: map[string]string{
|
||||
"QUEUE_URL": config.Queue.URL,
|
||||
"AWS_DEFAULT_REGION": config.Queue.Region,
|
||||
@@ -46,8 +46,11 @@ func createQueueContainer(t *testing.T, ctx context.Context, config *queueContai
|
||||
"DB_HOST": config.DB.Host,
|
||||
"DB_NAME": config.DB.Name,
|
||||
"DB_PORT": strconv.Itoa(config.DB.Port),
|
||||
"DB_NOSSL": "1",
|
||||
},
|
||||
Networks: []string{config.Network.Name},
|
||||
Networks: []string{config.Network.Name},
|
||||
WaitingFor: wait.ForLog("Listening to queue"),
|
||||
Entrypoint: []string{fmt.Sprintf("./bin/%s", config.ServiceName)},
|
||||
}
|
||||
|
||||
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
@@ -86,7 +89,7 @@ func createQueue(t *testing.T, ctx context.Context, network *testcontainers.Dock
|
||||
}
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "localstack/localstack:latest",
|
||||
Image: "localstack/localstack:4.0.3",
|
||||
Env: map[string]string{
|
||||
"AWS_ACCESS_KEY_ID": provider.Value.AccessKeyID,
|
||||
"AWS_SECRET_ACCESS_KEY": provider.Value.SecretAccessKey,
|
||||
@@ -210,8 +213,20 @@ func createQueueDependencies(t *testing.T, ctx context.Context, serviceName stri
|
||||
|
||||
return queue, func() {
|
||||
testcontainers.CleanupNetwork(t, network)
|
||||
dbContainer.Terminate(ctx)
|
||||
(*queue.Container).Terminate(ctx)
|
||||
container.Terminate(ctx)
|
||||
|
||||
err := dbContainer.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = (*queue.Container).Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = container.Terminate(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user