Skip to content
Closed
42 changes: 42 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Test

on:
pull_request: {}
push:
branches:
- main

permissions:
actions: read
checks: write
contents: read
pull-requests: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v4

- name: Install BATS
run: |
sudo apt-get update
sudo apt-get install -y bats

- name: Install Task
uses: arduino/setup-task@v2
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
Comment thread
westonplatter marked this conversation as resolved.
Outdated

- name: Run BATS tests
run: bats tests/

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: tests/
retention-days: 30
6 changes: 2 additions & 4 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ includes:
aqua: lib/aqua
aws: lib/aws
components: lib/components
cursor: lib/cursor
mixins: lib/mixins
os: lib/os-modules
snaplet: lib/snaplet
terraform:
taskfile: lib/terraform
aliases: [tf]
tf:
taskfile: lib/tf
toolbox: lib/toolbox

tasks: {}
10 changes: 5 additions & 5 deletions aqua.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
registries:
- type: standard
ref: v4.78.0 # renovate: depName=aquaproj/aqua-registry
ref: v4.433.0 # renovate: depName=aquaproj/aqua-registry
packages:
- name: go-task/task@v3.31.0
- name: TomWright/dasel@v2.4.1
- name: stedolan/jq@jq-1.7.1
- name: go-task/task@v3.45.4
- name: TomWright/dasel@v2.8.1
- name: stedolan/jq@jq-1.8.1
tags: [cursor]
- name: cli/cli@v2.74.2
- name: cli/cli@v2.83.0
tags: [gh]
16 changes: 9 additions & 7 deletions lib/terraform/Taskfile.yml → lib/tf/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ tasks:
sh: echo "{{.BACKEND_CONFIG_PATH}}/{{.WORKSPACE}}.backend.tf"
TFVARS_FILE:
sh: echo "{{.TFVARS_PATH}}/{{.WORKSPACE}}.tfvars"
TF_CMD:
sh: '[ "${USE_TERRAFORM}" = "true" ] && echo "terraform" || echo "tofu"'

init:
desc: Initialize Terraform working directory with a backend configuration file.
Expand All @@ -39,7 +41,7 @@ tasks:
- sh: test -f {{.BACKEND_CONFIG_FILE}}
msg: "Backend configuration file does not exist: {{.BACKEND_CONFIG_FILE}}"
cmds:
- terraform init -backend-config {{.BACKEND_CONFIG_FILE}} {{.TF_ARGS}}
- "{{.TF_CMD}} init -backend-config {{.BACKEND_CONFIG_FILE}} {{.TF_ARGS}}"

plan:
desc: Generate a Terraform execution plan Terraform loading variable values from the given file.
Expand All @@ -56,8 +58,8 @@ tasks:
- sh: test -f {{.TFVARS_FILE}}
msg: "Variables file does not exist: {{.TFVARS_FILE}}"
cmds:
- terraform workspace select -or-create {{.WORKSPACE}}
- terraform plan -var-file {{.TFVARS_FILE}} {{.TF_ARGS}}
- "{{.TF_CMD}} workspace select -or-create {{.WORKSPACE}}"
- "{{.TF_CMD}} plan -var-file {{.TFVARS_FILE}} {{.TF_ARGS}}"

apply:
desc: Create or update infrastructure according to Terraform configuration files in the current directory.
Expand All @@ -74,8 +76,8 @@ tasks:
- sh: test -f {{.TFVARS_FILE}}
msg: "Variables file does not exist: {{.TFVARS_FILE}}"
cmds:
- terraform workspace select -or-create {{.WORKSPACE}}
- terraform apply -var-file {{.TFVARS_FILE}} {{.TF_ARGS}}
- "{{.TF_CMD}} workspace select -or-create {{.WORKSPACE}}"
- "{{.TF_CMD}} apply -var-file {{.TFVARS_FILE}} {{.TF_ARGS}}"

refresh:
desc: Refresh the Terraform state to match the real-world infrastructure (safer via apply -refresh-only).
Expand All @@ -92,5 +94,5 @@ tasks:
- sh: test -f {{.TFVARS_FILE}}
msg: "Variables file does not exist: {{.TFVARS_FILE}}"
cmds:
- terraform workspace select -or-create {{.WORKSPACE}}
- terraform apply -refresh-only -var-file {{.TFVARS_FILE}} {{.TF_ARGS}}
- "{{.TF_CMD}} workspace select -or-create {{.WORKSPACE}}"
- "{{.TF_CMD}} apply -refresh-only -var-file {{.TFVARS_FILE}} {{.TF_ARGS}}"
127 changes: 127 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Taskit Test Suite

## Purpose

This directory contains the BATS (Bash Automated Testing System) test suite for Taskit. Tests validate Task command functionality using dry-run mode, ensuring reliability without requiring actual tool installations.

## Prerequisites

Install BATS:

```bash
# macOS
brew install bats-core

# Debian/Ubuntu
apt-get install bats

# Manual installation
git clone https://github.com/bats-core/bats-core.git
cd bats-core
./install.sh /usr/local
```
Comment thread
westonplatter marked this conversation as resolved.
Outdated

## Running Tests

### Local Development

```bash
# Run all tests
bats tests/

# Run specific test file
bats tests/terraform_plan.bats

# Run tests by tag
bats --filter-tags basic tests/
bats --filter-tags precondition tests/
```

### Continuous Integration

Tests run automatically on:

- **Pull Requests**: All PRs trigger the test suite
- **Main Branch**: Tests run on every push to main

The GitHub Actions workflow (`.github/workflows/test.yaml`) handles:

1. BATS and Task installation
2. Test execution across the `tests/` directory
3. Result artifacts upload (30-day retention)

View results in the GitHub Actions tab or PR checks section.

## Writing Tests

Tests leverage Task's dry-run mode (`task -v -n`) to verify command construction without execution:

```bash
@test "terraform:plan validates environment argument" {
# Arrange: Create test fixture
touch "tfvars/test-env.tfvars"

# Act: Execute task in dry-run mode
run task -v -n terraform:plan -- test-env

# Assert: Verify output
[ "$status" -eq 0 ]
[[ "$output" =~ "expected text" ]]
}
```

### Test Structure

- `setup()`: Pre-test fixture creation and environment setup
- `@test "description"`: Individual test case
- `teardown()`: Post-test cleanup

### Test Tags

Use tags for selective test execution:

- `terraform`: Terraform-related functionality
- `plan`: Plan command behavior
- `basic`: Core functionality
- `args`: Argument handling
- `precondition`: Validation logic
- `config`: Configuration management
- `error`: Error handling

## Best Practices

1. **Dry-run Testing**: Use `task -v -n` to test without requiring actual tool installations
2. **Debug Output**: Include `echo "Output: $output" >&3` for troubleshooting failed tests
3. **Fixture Management**: Create temporary files in `setup()`, clean up in `teardown()`
4. **Assertions**: Use `[ ]` for exit codes, `[[ =~ ]]` for pattern matching

## Troubleshooting

**Tests fail in CI but pass locally:**

- Ensure proper cleanup in `teardown()`
- Verify tests don't depend on local environment state
- Confirm all required files are committed

**Workflow doesn't run:**

- Verify `.github/workflows/test.yaml` exists
- Check GitHub Actions are enabled for the repository
- Review branch protection rules

## Workflow Permissions

The CI workflow uses minimal permissions:

- `actions: read` - Access workflow logs
- `checks: write` - Report check results
- `contents: read` - Read repository
- `pull-requests: read` - Access PR information

No secrets or credentials required for test execution.

## Resources

- [BATS Documentation](https://bats-core.readthedocs.io/en/stable/)
- [Writing BATS Tests](https://bats-core.readthedocs.io/en/stable/writing-tests.html)
- [Task Documentation](https://taskfile.dev/)
60 changes: 60 additions & 0 deletions tests/terraform_plan.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env bats
# Test suite for terraform:plan task command

setup() {
cd "$(dirname "$BATS_TEST_FILENAME")/.."
export TEST_WORKSPACE="test-env"
mkdir -p tfvars backend-configurations
touch "tfvars/${TEST_WORKSPACE}.tfvars"
touch "backend-configurations/${TEST_WORKSPACE}.backend.tf"
}

teardown() {
rm -rf tfvars backend-configurations
}

# bats test_tags=terraform,plan,basic
@test "terraform:plan generates correct command with workspace" {
run task -v -n tf:plan -- "$TEST_WORKSPACE"

echo "Output: $output" >&3

[ "$status" -eq 0 ]
[[ "$output" =~ terraform.*workspace.*select.*-or-create.*${TEST_WORKSPACE} ]]
[[ "$output" =~ terraform.*plan.*-var-file.*tfvars/${TEST_WORKSPACE}.tfvars ]]
}

# bats test_tags=terraform,plan,args
@test "terraform:plan passes additional arguments to terraform" {
run task -v -n tf:plan -- "$TEST_WORKSPACE" -out=tfplan.out -lock=false

echo "Output: $output" >&3

[ "$status" -eq 0 ]
[[ "$output" =~ -out=tfplan.out ]]
[[ "$output" =~ -lock=false ]]
[[ "$output" =~ -var-file.*tfvars/${TEST_WORKSPACE}.tfvars ]]
}

# bats test_tags=terraform,plan,precondition
@test "terraform:plan fails when tfvars file does not exist" {
rm -f "tfvars/${TEST_WORKSPACE}.tfvars"

run task -v -n tf:plan -- "$TEST_WORKSPACE"

echo "Output: $output" >&3

[ "$status" -ne 0 ]
[[ "$output" =~ "Variables file does not exist" ]]
}

# bats test_tags=terraform,plan,config
@test "terraform:plan uses terraform when USE_TERRAFORM=true" {
run env USE_TERRAFORM=true task -v -n tf:plan -- "$TEST_WORKSPACE"

echo "Output: $output" >&3

[ "$status" -eq 0 ]
[[ "$output" =~ \[tf:plan\]\ terraform\ workspace\ select ]]
[[ "$output" =~ \[tf:plan\]\ terraform\ plan ]]
}