Skip to content

Commit d304336

Browse files
committed
Add basic BATS tests
1 parent b22081c commit d304336

File tree

8 files changed

+418
-9
lines changed

8 files changed

+418
-9
lines changed

.github/workflows/bats.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Bats Integration Tests
2+
3+
on: [pull_request]
4+
5+
jobs:
6+
bats:
7+
runs-on: ubuntu-latest
8+
9+
env:
10+
DB: postgresql
11+
12+
steps:
13+
- uses: actions/checkout@v5
14+
15+
- name: Install bats
16+
run: |
17+
git clone --depth 1 --branch v1.11.0 https://github.com/bats-core/bats-core.git /tmp/bats-core
18+
sudo /tmp/bats-core/install.sh /usr/local
19+
bats --version
20+
21+
- name: Install podman
22+
run: |
23+
sudo apt-get update
24+
sudo apt-get -y install podman
25+
podman --version
26+
27+
- id: ruby_version
28+
uses: voxpupuli/ruby-version@v1
29+
- id: min_ruby
30+
run: echo "version=$(echo '${{ steps.ruby_version.outputs.versions }}' | jq -r '.[-1]')" >> $GITHUB_OUTPUT
31+
- name: Setup Ruby
32+
uses: ruby/setup-ruby@v1
33+
with:
34+
# Use the minimum supported Ruby version for rubocop (last in descending list)
35+
ruby-version: ${{ steps.min_ruby.outputs.version }}
36+
bundler-cache: true
37+
38+
- name: Pull container images
39+
run: |
40+
podman pull docker.io/library/postgres:15
41+
podman pull docker.io/library/redis:7-alpine
42+
43+
- name: Run bats tests
44+
run: bats -x --verbose-run --print-output-on-failure test/bats/
45+
46+
- name: Cleanup containers (if tests fail)
47+
if: always()
48+
run: |
49+
podman stop dynflow-test-postgres dynflow-test-redis 2>/dev/null || true
50+
podman rm -f dynflow-test-postgres dynflow-test-redis 2>/dev/null || true

examples/example_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def persistence_adapter
4848
end
4949

5050
def logger_adapter
51-
Dynflow::LoggerAdapters::Simple.new $stderr, Logger::FATAL
51+
Dynflow::LoggerAdapters::Simple.new $stdout, Logger::DEBUG
5252
end
5353

5454
def run_web_console(world = ExampleHelper.world)

examples/remote_executor.rb

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,14 @@ def connector
117117
Proc.new { |world| Dynflow::Connectors::Database.new(world) }
118118
end
119119

120-
def run_client
120+
def run_client(count)
121121
world = ExampleHelper.create_world do |config|
122122
config.persistence_adapter = persistence_adapter
123123
config.executor = false
124124
config.connector = connector
125125
end
126126

127-
world.trigger(OrchestrateEvented::CreateInfrastructure)
128-
world.trigger(OrchestrateEvented::CreateInfrastructure, true)
129-
130-
loop do
127+
(count || 1000).times do
131128
start_time = Time.now
132129
world.trigger(SampleAction).finished.wait
133130
finished_in = Time.now - start_time
@@ -150,13 +147,13 @@ def run_client
150147
when 'server'
151148
puts <<~MSG
152149
The server is starting…. You can send the work to it by running:
153-
150+
154151
#{$0} client
155-
152+
156153
MSG
157154
RemoteExecutorExample.run_server
158155
when 'client'
159-
RemoteExecutorExample.run_client
156+
RemoteExecutorExample.run_client(ARGV[1]&.to_i)
160157
else
161158
puts "Unknown command #{comment}"
162159
exit 1

test/bats/helpers/common.bash

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env bash
2+
# Common helper functions for bats tests
3+
4+
# Get the project root directory
5+
get_project_root() {
6+
local dir="${BATS_TEST_DIRNAME}"
7+
while [ "${dir}" != "/" ]; do
8+
if [ -f "${dir}/dynflow.gemspec" ]; then
9+
echo "${dir}"
10+
return 0
11+
fi
12+
dir="$(dirname "${dir}")"
13+
done
14+
echo "ERROR: Could not find project root" >&2
15+
return 1
16+
}
17+
18+
# Setup test environment variables
19+
setup_test_env() {
20+
export PROJECT_ROOT="$(get_project_root)"
21+
export BUNDLE_GEMFILE="${PROJECT_ROOT}/Gemfile"
22+
23+
# Set database URLs for tests
24+
export DATABASE_URL="$(get_postgres_url)"
25+
export REDIS_URL="$(get_redis_url)"
26+
export DB_CONN_STRING="$DATABASE_URL"
27+
28+
# Test directories
29+
export TEST_PIDDIR="${BATS_TEST_TMPDIR}/pids"
30+
}
31+
32+
run_background() {
33+
local label="$1"
34+
shift
35+
36+
local log_file="$(bg_output_file "$label")"
37+
mkdir -p "$TEST_PIDDIR"
38+
(
39+
"$@" 2>&1 &
40+
echo $! >"${TEST_PIDDIR}/${label}.pid"
41+
) | tee "$log_file" | sed "s/^/${label}: /" &
42+
}
43+
44+
bg_output_file() {
45+
local label="$1"
46+
47+
echo "${BATS_TEST_TMPDIR}/${label}.log"
48+
}
49+
50+
# A function that polls a given command until it succeeds or until it runs out
51+
wait_for() {
52+
local timeout="$1"
53+
local interval="$2"
54+
shift 2
55+
56+
local elapsed=0
57+
while [ "$elapsed" -lt "$timeout" ]; do
58+
if "$@" >/dev/null 2>&1; then
59+
return 0
60+
fi
61+
sleep "$interval"
62+
elapsed=$((elapsed + interval))
63+
done
64+
65+
echo "Timeout after ${timeout}s waiting for: $*" >&2
66+
return 1
67+
}

test/bats/helpers/containers.bash

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/usr/bin/env bash
2+
# Container helper functions for bats tests
3+
4+
# Default container names
5+
POSTGRES_CONTAINER_NAME="${POSTGRES_CONTAINER_NAME:-dynflow-test-postgres}"
6+
REDIS_CONTAINER_NAME="${REDIS_CONTAINER_NAME:-dynflow-test-redis}"
7+
8+
# Default ports
9+
POSTGRES_PORT="${POSTGRES_PORT:-15432}"
10+
REDIS_PORT="${REDIS_PORT:-16379}"
11+
12+
# Database credentials
13+
POSTGRES_USER="${POSTGRES_USER:-dynflow_test}"
14+
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-dynflow_test_pass}"
15+
POSTGRES_DB="${POSTGRES_DB:-dynflow_test}"
16+
17+
# Container images
18+
POSTGRES_IMAGE="${POSTGRES_IMAGE:-docker.io/library/postgres:15}"
19+
REDIS_IMAGE="${REDIS_IMAGE:-docker.io/library/redis:7-alpine}"
20+
21+
# Start PostgreSQL container
22+
start_postgres() {
23+
echo "Starting PostgreSQL container: ${POSTGRES_CONTAINER_NAME}" >&2
24+
25+
podman run -d \
26+
--name "${POSTGRES_CONTAINER_NAME}" \
27+
-e POSTGRES_USER="${POSTGRES_USER}" \
28+
-e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
29+
-e POSTGRES_DB="${POSTGRES_DB}" \
30+
-p "${POSTGRES_PORT}:5432" \
31+
"${POSTGRES_IMAGE}" \
32+
postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off
33+
34+
# Wait for PostgreSQL to be ready
35+
echo "Waiting for PostgreSQL to be ready..." >&2
36+
local max_attempts=30
37+
local attempt=0
38+
39+
while [ $attempt -lt $max_attempts ]; do
40+
if podman exec "${POSTGRES_CONTAINER_NAME}" pg_isready -U "${POSTGRES_USER}" > /dev/null 2>&1; then
41+
echo "PostgreSQL is ready" >&2
42+
return 0
43+
fi
44+
attempt=$((attempt + 1))
45+
sleep 1
46+
done
47+
48+
echo "ERROR: PostgreSQL failed to start within ${max_attempts} seconds" >&2
49+
return 1
50+
}
51+
52+
stop_container() {
53+
local container="$1"
54+
local with_volumes="$2"
55+
56+
echo "Stopping container: ${container}" >&2
57+
if podman ps -a --format "{{.Names}}" | grep -q "^${container}$"; then
58+
podman stop -t 2 "${container}" > /dev/null 2>&1 || true
59+
if [ "$with_volumes" = "1" ]; then
60+
podman rm -v -f "${container}" > /dev/null 2>&1 || true
61+
else
62+
podman rm -f "${container}" > /dev/null 2>&1 || true
63+
fi
64+
fi
65+
}
66+
67+
# Stop PostgreSQL container
68+
stop_postgres() {
69+
stop_container "$POSTGRES_CONTAINER_NAME" "$1"
70+
}
71+
72+
# Start Redis container
73+
start_redis() {
74+
echo "Starting Redis container: ${REDIS_CONTAINER_NAME}" >&2
75+
76+
podman run -d \
77+
--name "${REDIS_CONTAINER_NAME}" \
78+
-p "${REDIS_PORT}:6379" \
79+
"${REDIS_IMAGE}"
80+
81+
# Wait for Redis to be ready
82+
echo "Waiting for Redis to be ready..." >&2
83+
local max_attempts=30
84+
local attempt=0
85+
86+
while [ $attempt -lt $max_attempts ]; do
87+
if podman exec "${REDIS_CONTAINER_NAME}" redis-cli ping > /dev/null 2>&1; then
88+
echo "Redis is ready" >&2
89+
return 0
90+
fi
91+
attempt=$((attempt + 1))
92+
sleep 1
93+
done
94+
95+
echo "ERROR: Redis failed to start within ${max_attempts} seconds" >&2
96+
return 1
97+
}
98+
99+
# Stop Redis container
100+
stop_redis() {
101+
stop_container "$REDIS_CONTAINER_NAME" "$1"
102+
}
103+
104+
# Check if PostgreSQL container is running
105+
is_postgres_running() {
106+
podman ps --format "{{.Names}}" | grep -q "^${POSTGRES_CONTAINER_NAME}$"
107+
}
108+
109+
# Check if Redis container is running
110+
is_redis_running() {
111+
podman ps --format "{{.Names}}" | grep -q "^${REDIS_CONTAINER_NAME}$"
112+
}
113+
114+
# Get PostgreSQL connection string
115+
get_postgres_url() {
116+
echo "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${POSTGRES_PORT}/${POSTGRES_DB}"
117+
}
118+
119+
# Get Redis URL
120+
get_redis_url() {
121+
echo "redis://localhost:${REDIS_PORT}/0"
122+
}
123+
124+
# Execute SQL in PostgreSQL container
125+
exec_sql() {
126+
local sql="$1"
127+
podman exec "${POSTGRES_CONTAINER_NAME}" \
128+
psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -c "${sql}"
129+
}
130+
131+
# Execute Redis command
132+
exec_redis() {
133+
podman exec "${REDIS_CONTAINER_NAME}" redis-cli "$@"
134+
}
135+
136+
# Clean up all test containers
137+
cleanup_containers() {
138+
stop_postgres 1
139+
stop_redis 1
140+
}
141+
142+
# Start all test containers
143+
start_containers() {
144+
start_postgres || return 1
145+
start_redis || return 1
146+
}

test/bats/setup_suite.bash

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env bash
2+
# Suite-level setup - runs once before all tests
3+
4+
# Load container helpers
5+
source "$(dirname "${BASH_SOURCE[0]}")/helpers/containers.bash"
6+
source "$(dirname "${BASH_SOURCE[0]}")/helpers/common.bash"
7+
8+
# This function runs once before all tests in the suite
9+
setup_suite() {
10+
echo "=== Setting up bats test suite ===" >&2
11+
12+
# Verify podman is available
13+
if ! command -v podman &> /dev/null; then
14+
echo "ERROR: podman is not installed or not in PATH" >&2
15+
exit 1
16+
fi
17+
18+
# Check if bundle is available
19+
PROJECT_ROOT="$(get_project_root)"
20+
if ! command -v bundle &> /dev/null; then
21+
echo "WARNING: bundler is not installed" >&2
22+
else
23+
# Install dependencies if needed
24+
echo "Checking bundle dependencies..." >&2
25+
cd "${PROJECT_ROOT}" && bundle check > /dev/null 2>&1 || bundle install
26+
fi
27+
28+
# Pull container images if not already present
29+
echo "Checking container images..." >&2
30+
31+
if ! podman image exists "${POSTGRES_IMAGE}"; then
32+
echo "Pulling PostgreSQL image: ${POSTGRES_IMAGE}" >&2
33+
podman pull "${POSTGRES_IMAGE}"
34+
fi
35+
36+
if ! podman image exists "${REDIS_IMAGE}"; then
37+
echo "Pulling Redis image: ${REDIS_IMAGE}" >&2
38+
podman pull "${REDIS_IMAGE}"
39+
fi
40+
41+
# Clean up any existing test containers from previous runs
42+
echo "Cleaning up any existing test containers..." >&2
43+
cleanup_containers
44+
45+
echo "=== Test suite setup complete ===" >&2
46+
}

0 commit comments

Comments
 (0)