package e2eutil import ( "context" "fmt" "os" "os/exec" "path/filepath" "testing" "time" "github.com/docker/go-connections/nat" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) // StartHakoContainer starts a Hako container for E2E testing. // It builds the webapp, compiles the binary, creates a container, and returns // the container instance and base URL for accessing the server. func StartHakoContainer(ctx context.Context, t *testing.T) (testcontainers.Container, string, error) { t.Helper() // Get the project root (one level up from e2e) projectRoot, err := filepath.Abs(filepath.Join("..", "..")) if err != nil { return nil, "", fmt.Errorf("failed to get project root: %w", err) } // Build webapp t.Log("Building webapp...") cmd := exec.CommandContext(ctx, "make", "build-webapp") cmd.Dir = projectRoot cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return nil, "", fmt.Errorf("failed to build webapp: %w", err) } // Build Hako binary for the container (Linux) t.Log("Building Hako binary...") binaryPath := filepath.Join(projectRoot, "e2e", "hako") cmd = exec.CommandContext(ctx, "go", "build", "-o", binaryPath, "./cmd/hako") cmd.Dir = projectRoot cmd.Env = append(os.Environ(), "CGO_ENABLED=0", "GOOS=linux", "GOARCH=amd64") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return nil, "", fmt.Errorf("failed to build binary: %w", err) } // Ensure binary is removed on cleanup t.Cleanup(func() { os.Remove(binaryPath) }) // Create container request req := testcontainers.ContainerRequest{ FromDockerfile: testcontainers.FromDockerfile{ Context: filepath.Join(projectRoot, "e2e"), Dockerfile: "Dockerfile.e2e", PrintBuildLog: true, }, ExposedPorts: []string{"8080/tcp"}, WaitingFor: wait.ForLog("Starting HTTP server"). WithStartupTimeout(30 * time.Second), } // Start container t.Log("Starting Hako container...") container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) if err != nil { return nil, "", fmt.Errorf("failed to start container: %w", err) } // Register cleanup t.Cleanup(func() { if err := container.Terminate(ctx); err != nil { t.Logf("Failed to terminate container: %v", err) } }) // Get the mapped port mappedPort, err := container.MappedPort(ctx, nat.Port("8080/tcp")) if err != nil { return nil, "", fmt.Errorf("failed to get mapped port: %w", err) } // Get the host host, err := container.Host(ctx) if err != nil { return nil, "", fmt.Errorf("failed to get container host: %w", err) } // Construct base URL baseURL := fmt.Sprintf("http://%s:%s", host, mappedPort.Port()) t.Logf("Hako container started at %s", baseURL) return container, baseURL, nil }