Skip to content

Commit 34a5108

Browse files
authored
FEAT: Add integration tests (#3)
* feat: add integration tests * fix: restructure build * test: run build in container * fix: shell setup * fix: improve package logging
1 parent 849557e commit 34a5108

7 files changed

Lines changed: 203 additions & 7 deletions

File tree

.dockerignore

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Include any files or directories that you don't want to be copied to your
2+
# container here (e.g., local build artifacts, temporary files, etc.).
3+
#
4+
# For more help, visit the .dockerignore file reference guide at
5+
# https://docs.docker.com/go/build-context-dockerignore/
6+
7+
**/.DS_Store
8+
**/.classpath
9+
**/.dockerignore
10+
**/.env*
11+
**/.git
12+
**/.github
13+
**/.gitignore
14+
**/.project
15+
**/.settings
16+
**/.toolstarget
17+
**/.vs
18+
**/.vscode
19+
**/*.*proj.user
20+
**/*.dbmdl
21+
**/*.jfm
22+
**/bin
23+
**/charts
24+
**/docker-compose*
25+
**/*Dockerfile
26+
**/docs
27+
LICENSE
28+
README.md
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
name: Integration Test
3+
"on":
4+
workflow_dispatch:
5+
pull_request:
6+
branches:
7+
- main
8+
paths:
9+
- "integration/**"
10+
- "**/*.go"
11+
push:
12+
branches:
13+
- main
14+
15+
permissions:
16+
id-token: write
17+
contents: read
18+
19+
concurrency:
20+
group: ${{ github.workflow }}-${{ github.ref }}
21+
cancel-in-progress: true
22+
23+
jobs:
24+
integration-test:
25+
name: Run integration tests
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v4
30+
31+
- name: Install Just
32+
uses: ./.github/actions/setup-workspace
33+
34+
- name: Set up Docker Buildx
35+
uses: docker/setup-buildx-action@v3
36+
37+
- name: Run integration tests
38+
working-directory: ./integration
39+
run: |
40+
just build
41+
just test-all

cli/internal/templating/engine.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,33 @@ import (
1414

1515
"github.com/jgfranco17/hackstack/cli/internal/fileutils"
1616
"github.com/jgfranco17/hackstack/cli/internal/logging"
17+
"github.com/sirupsen/logrus"
1718
)
1819

20+
const (
21+
extensionTemplate = ".j2"
22+
extensionRawCopy = ".copy"
23+
)
24+
25+
// Engine is the entity responsible for rendering embedded template
26+
// files with provided source data.
1927
type Engine struct {
2028
Files fs.FS
2129
Data CLIProject
2230
}
2331

32+
// NewEngine creates a new templating engine instance with the provided
33+
// embedded files and source data.
2434
func NewEngine(files fs.FS, data CLIProject) *Engine {
2535
return &Engine{
2636
Files: files,
2737
Data: data,
2838
}
2939
}
3040

41+
// Render processes the embedded template files and writes the output to the specified directory.
42+
// It walks through all files in the embedded FS, rendering templates and copying raw files as
43+
// needed. The function returns an error if any step of the rendering process fails.
3144
func (e *Engine) Render(ctx context.Context, outputPath string) error {
3245
logger := logging.FromContext(ctx).WithField("module", "templating")
3346

@@ -36,7 +49,7 @@ func (e *Engine) Render(ctx context.Context, outputPath string) error {
3649

3750
walker := func(path string, d fs.DirEntry, err error) error {
3851
if err != nil {
39-
return fmt.Errorf("walk error at %q: %w", path, err)
52+
return fmt.Errorf("walk error at %s: %w", path, err)
4053
}
4154
if d.IsDir() {
4255
return nil
@@ -46,16 +59,22 @@ func (e *Engine) Render(ctx context.Context, outputPath string) error {
4659

4760
var work func() error
4861
switch {
49-
case strings.HasSuffix(path, ".j2"):
50-
destPath = strings.TrimSuffix(destPath, ".j2")
62+
case strings.HasSuffix(path, extensionTemplate):
63+
destPath = strings.TrimSuffix(destPath, extensionTemplate)
5164
work = func() error {
52-
logger.WithField("file", path).Trace("Rendering from template")
65+
logger.WithFields(logrus.Fields{
66+
"source": path,
67+
"destination": destPath,
68+
}).Trace("Rendering from template")
5369
return renderTemplate(e.Files, path, destPath, e.Data)
5470
}
55-
case strings.HasSuffix(path, ".copy"):
56-
destPath = strings.TrimSuffix(destPath, ".copy")
71+
case strings.HasSuffix(path, extensionRawCopy):
72+
destPath = strings.TrimSuffix(destPath, extensionRawCopy)
5773
work = func() error {
58-
logger.WithField("file", path).Trace("Copying file")
74+
logger.WithFields(logrus.Fields{
75+
"source": path,
76+
"destination": destPath,
77+
}).Trace("Copying raw file")
5978
return fileutils.CopyFile(e.Files, path, destPath)
6079
}
6180
default:

integration/Dockerfile

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# ========== CLI BUILD STAGE ==========
2+
ARG GO_VERSION=1.25
3+
FROM golang:${GO_VERSION}-alpine AS build
4+
5+
WORKDIR /src
6+
RUN --mount=type=cache,target=/go/pkg/mod/ \
7+
--mount=type=bind,source=go.mod,target=go.mod \
8+
go mod download -x
9+
COPY . .
10+
RUN --mount=type=cache,target=/go/pkg/mod/ \
11+
CGO_ENABLED=0 go build -o /bin/hackstack .
12+
13+
# ========== CLI SETUP STAGE ==========
14+
FROM ubuntu:22.04 AS env-setup
15+
SHELL ["/bin/bash", "-c"]
16+
17+
ENV DEBIAN_FRONTEND=noninteractive
18+
RUN apt-get update && \
19+
apt-get install -y --no-install-recommends curl ca-certificates xz-utils && \
20+
rm -rf /var/lib/apt/lists/*
21+
22+
COPY --from=build /bin/hackstack /bin/hackstack
23+
24+
# Tests should use non-root to simulate a real user.
25+
ARG USERNAME=testuser
26+
ARG USER_ID=1000
27+
ARG GROUP_ID=1000
28+
RUN addgroup --gid $GROUP_ID ${USERNAME} && \
29+
adduser --disabled-password --gecos '' --uid ${USER_ID} --gid ${GROUP_ID} ${USERNAME}
30+
31+
FROM env-setup AS workspace-setup
32+
USER ${USERNAME}
33+
ENV HOME=/home/${USERNAME}
34+
ARG PROJECT_DIR="${HOME}/projects"
35+
WORKDIR ${PROJECT_DIR}
36+
37+
COPY examples/ ./examples/
38+
RUN mkdir -p "${PROJECT_DIR}/backend" "${PROJECT_DIR}/cli"
39+
40+
# ========== APP RUN STAGE ==========
41+
FROM workspace-setup AS app
42+
SHELL ["/usr/bin/env", "bash", "-c"]
43+
44+
ENTRYPOINT [ "/bin/hackstack" ]
45+
CMD ["--help"]

integration/docker-compose.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Integration test Docker suite
2+
services:
3+
hackstack:
4+
container_name: hackstack
5+
image: hackstack:latest
6+
build:
7+
context: ..
8+
dockerfile: integration/Dockerfile

integration/justfile

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# INTEGRATION TEST SCRIPTS
2+
3+
CAPTURE_OUTPUT_FILE := "output.txt"
4+
5+
# Default target to list available commands
6+
_default:
7+
@just --list --unsorted
8+
9+
##########################################################
10+
# TEST WORKSPACE SETUP
11+
##########################################################
12+
13+
# Build the services in Docker
14+
build:
15+
docker compose build
16+
17+
# Workspace teardown
18+
clean:
19+
-docker compose down
20+
-rm {{ CAPTURE_OUTPUT_FILE }}
21+
22+
##########################################################
23+
# INTERACTIVE COMMANDS
24+
##########################################################
25+
26+
# Run the CLI image with envs
27+
hackstack *args:
28+
@docker compose run \
29+
--remove-orphans --rm \
30+
hackstack {{ args }}
31+
32+
##########################################################
33+
# TEST EXECUTION
34+
##########################################################
35+
36+
# Run the full test suite
37+
test-all: test-base test-build
38+
@echo "Hackstack CLI integration test completed!"
39+
40+
# Base tests to verify the CLI is working and accessible
41+
test-base:
42+
#!/usr/bin/env bash
43+
just hackstack --version | grep -n "hackstack"
44+
just hackstack --help 2>&1 | grep -n "hackstack"
45+
46+
# Test the build command with a sample datasource
47+
test-build:
48+
#!/usr/bin/env bash
49+
just hackstack build --help
50+
just hackstack -vvv build backend \
51+
--output "/home/testuser/projects/backend" \
52+
--source examples/datasource.yaml
53+
just hackstack -vvv build cli \
54+
--output "/home/testuser/projects/cli" \
55+
--source examples/datasource.yaml

0 commit comments

Comments
 (0)