Files
query-orchestration/internal/test/ecosystem.go
T
Jay Brown ebf47c6013 Merged in feature/track-filesize (pull request #200)
track file sizes for all documents in system

* feature complete

needs dev testing
2026-01-12 17:46:07 +00:00

236 lines
5.6 KiB
Go

package test
import (
"context"
"fmt"
"strings"
"sync"
"testing"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/objectstore"
queryapi "queryorchestration/pkg/queryAPI"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
)
type Network struct {
Dependencies Dependencies
APIs map[APIName]*Container
Runners map[RunnerName]*Container
Client *queryapi.ClientWithResponses
}
func CreateFullNetwork(t testing.TB, ctx context.Context, cfg FullDependenciesConfig) (Network, func()) {
deps := CreateFullDependencies(t, ctx, cfg)
var wg sync.WaitGroup
apiContainers := make(map[APIName]*Container, len(apis))
apiClean := make([]func(), len(apis))
var apiMu sync.Mutex
for i, s := range apis {
wg.Add(1)
go func() {
c, ccleanup := CreateAPI(t, ctx, cfg, deps.Network, &APIConfig{
API: s,
MockHTTP: string(deps.MockServer.Internal),
})
apiMu.Lock()
apiContainers[s.Name] = c
apiMu.Unlock()
apiClean[i] = ccleanup
wg.Done()
}()
}
runnerContainers := make(map[RunnerName]*Container, len(runners))
runnerClean := make([]func(), len(runners))
var runnerMu sync.Mutex
for i, r := range runners {
wg.Add(1)
go func() {
c, ccleanup := CreateRunner(t, ctx, cfg, deps.Network, &RunnerConfig{
Runner: r,
MockHTTP: string(deps.MockServer.Internal),
})
runnerMu.Lock()
runnerContainers[r.Name] = c
runnerMu.Unlock()
runnerClean[i] = ccleanup
wg.Done()
}()
}
wg.Wait()
// Wait for external readiness before creating the client.
// This addresses the race condition where containers report healthy internally
// but the external port mapping isn't fully ready, causing 404 errors in CI.
err := WaitForExternalReadiness(apiContainers[QueryAPIName].URI, "/health", GetPollingTimeout())
require.NoError(t, err, "QueryAPI not externally reachable")
// Wait for API routes to be registered.
// The health endpoint may return 200 before all routes are registered,
// causing "404 page not found" errors on actual API calls.
err = WaitForAPIRoutes(apiContainers[QueryAPIName].URI, GetPollingTimeout())
require.NoError(t, err, "QueryAPI routes not registered")
// Create API client with bounded HTTP timeout to prevent indefinite hangs
// that were causing CI failures (goroutines blocking until 15-minute global timeout)
qService, err := queryapi.NewClientWithResponses(
apiContainers[QueryAPIName].URI,
queryapi.WithHTTPClient(GetHTTPClient()),
)
require.NoError(t, err)
return Network{
Dependencies: deps,
APIs: apiContainers,
Runners: runnerContainers,
Client: qService,
}, func() {
var wg sync.WaitGroup
for _, c := range apiClean {
wg.Add(1)
go func() {
c()
wg.Done()
}()
}
for _, c := range runnerClean {
wg.Add(1)
go func() {
c()
wg.Done()
}()
}
wg.Wait()
}
}
type FullDependenciesConfig interface {
serviceconfig.ConfigProvider
objectstore.ConfigProvider
}
type Dependencies struct {
QueueURLs map[RunnerName]string
AWSConfig *AWSContainerConfig
DBConfig testcontainers.Container
MockServer *MockServer
Network string
}
type FullDependenciesParams struct {
NoObjectStore bool
Runners *[]RunnerName
}
func CreateFullDependenciesWithParams(t testing.TB, cfg FullDependenciesConfig, params *FullDependenciesParams) Dependencies {
network := GetNetwork(t)
deps := Dependencies{
Network: network,
QueueURLs: map[RunnerName]string{},
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
deps.AWSConfig = CreateAWSContainer(t, cfg)
SetQueueClient(t, t.Context(), cfg, deps.AWSConfig.ExternalEndpoint)
var lock sync.Mutex
runs := params.Runners
if runs == nil {
allRuns := []RunnerName{}
for _, run := range runners {
allRuns = append(allRuns, run.Name)
}
runs = &allRuns
}
for _, runner := range *runs {
wg.Add(1)
go func() {
queue := CreateQueue(t, cfg, runner)
lock.Lock()
deps.QueueURLs[runner] = queue
lock.Unlock()
wg.Done()
}()
}
if !params.NoObjectStore {
SetStoreClient(t, t.Context(), cfg, deps.AWSConfig.ExternalEndpoint)
CreateBucket(t, cfg)
SetBucketNotifs(t, t.Context(), cfg)
}
wg.Done()
}()
mockServer := CreateMockServer(t)
deps.MockServer = mockServer
CreateDBWithParams(t, cfg, &CreateDatabaseConfig{
NoMigrations: true,
})
wg.Wait()
return deps
}
func CreateFullDependencies(t testing.TB, ctx context.Context, cfg FullDependenciesConfig) Dependencies {
return CreateFullDependenciesWithParams(t, cfg, &FullDependenciesParams{})
}
type APINetwork struct {
Dependencies Dependencies
API *Container
}
func CreateAPINetworkWithParams(t testing.TB, cfg FullDependenciesConfig, api API, params *FullDependenciesParams) (*APINetwork, func()) {
deps := CreateFullDependenciesWithParams(t, cfg, params)
c, ccleanup := CreateAPI(t, t.Context(), cfg, deps.Network, &APIConfig{
API: api,
MockHTTP: string(deps.MockServer.Internal),
})
return &APINetwork{
Dependencies: deps,
API: c,
}, ccleanup
}
func CreateAPINetwork(t testing.TB, ctx context.Context, cfg FullDependenciesConfig, api API) (*APINetwork, func()) {
return CreateAPINetworkWithParams(t, cfg, api, &FullDependenciesParams{})
}
func GetAlias(t testing.TB, baseName string) string {
name := fmt.Sprintf("%s_%s", baseName, t.Name())
name = strings.ToLower(name)
name = strings.ReplaceAll(name, "/", "")
name = strings.ReplaceAll(name, " ", "")
name = strings.ReplaceAll(name, "-", "")
name = strings.ReplaceAll(name, "_", "")
return name
}