Skip to content

Migrate tests to Terraform for jetbrains, zed, and code-server #307

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The Coder Registry is a collection of Terraform modules and templates for Coder

### Install Dependencies

Install Bun:
Install Bun (for formatting and scripts):

```bash
curl -fsSL https://bun.sh/install | bash
Expand Down Expand Up @@ -124,19 +124,23 @@ This script generates:
- Accurate description and usage examples
- Correct icon path (usually `../../../../.icons/your-icon.svg`)
- Proper tags that describe your module
3. **Create `main.test.ts`** to test your module
3. **Create at least one `.tftest.hcl`** to test your module with `terraform test`
4. **Add any scripts** or additional files your module needs

### 4. Test and Submit

```bash
# Test your module
bun test -t 'module-name'
# Test your module (from the module directory)
terraform init -upgrade
terraform test -verbose

# Or run all tests in the repo
./scripts/terraform_test_all.sh

# Format code
bun fmt
bun run fmt

# Commit and create PR
# Commit and create PR (do not push to main directly)
git add .
git commit -m "Add [module-name] module"
git push origin your-branch
Expand Down Expand Up @@ -335,11 +339,12 @@ coder templates push test-[template-name] -d .
### 2. Test Your Changes

```bash
# Test a specific module
bun test -t 'module-name'
# Test a specific module (from the module directory)
terraform init -upgrade
terraform test -verbose

# Test all modules
bun test
./scripts/terraform_test_all.sh
```

### 3. Maintain Backward Compatibility
Expand Down Expand Up @@ -388,7 +393,7 @@ Example: `https://github.com/coder/registry/compare/main...your-branch?template=
### Every Module Must Have

- `main.tf` - Terraform code
- `main.test.ts` - Working tests
- One or more `.tftest.hcl` files - Working tests with `terraform test`
- `README.md` - Documentation with frontmatter

### Every Template Must Have
Expand Down Expand Up @@ -488,6 +493,6 @@ When reporting bugs, include:
2. **No tests** or broken tests
3. **Hardcoded values** instead of variables
4. **Breaking changes** without defaults
5. **Not running** `bun fmt` before submitting
5. **Not running** formatting (`bun run fmt`) and tests (`terraform test`) before submitting

Happy contributing! 🚀
4 changes: 2 additions & 2 deletions MAINTAINER.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ sudo apt install golang-go

Check that PRs have:

- [ ] All required files (`main.tf`, `main.test.ts`, `README.md`)
- [ ] All required files (`main.tf`, `README.md`, at least one `.tftest.hcl`)
- [ ] Proper frontmatter in README
- [ ] Working tests (`bun test`)
- [ ] Working tests (`terraform test`)
- [ ] Formatted code (`bun run fmt`)
- [ ] Avatar image for new namespaces (`avatar.png` or `avatar.svg` in `.images/`)

Expand Down
21 changes: 21 additions & 0 deletions examples/modules/MODULE_NAME.tftest.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
run "plan_with_required_vars" {
command = plan

variables {
agent_id = "example-agent-id"
}
}

run "app_url_uses_port" {
command = plan

variables {
agent_id = "example-agent-id"
port = 19999
}

assert {
condition = resource.coder_app.MODULE_NAME.url == "http://localhost:19999"
error_message = "Expected MODULE_NAME app URL to include configured port"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"fmt": "bun x prettier --write **/*.sh **/*.ts **/*.md *.md && terraform fmt -recursive -diff",
"fmt:ci": "bun x prettier --check **/*.sh **/*.ts **/*.md *.md && terraform fmt -check -recursive -diff",
"terraform-validate": "./scripts/terraform_validate.sh",
"test": "bun test",
"test": "./scripts/terraform_test_all.sh",
"update-version": "./update-version.sh"
},
"devDependencies": {
Expand Down
50 changes: 50 additions & 0 deletions registry/coder/modules/code-server/code-server.tftest.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
run "required_vars" {
command = plan

variables {
agent_id = "foo"
}
}

run "offline_and_use_cached_conflict" {
command = plan

variables {
agent_id = "foo"
use_cached = true
offline = true
}

expect_failures = [
resource.coder_script.code-server
]
}

run "offline_disallows_extensions" {
command = plan

variables {
agent_id = "foo"
offline = true
extensions = ["ms-python.python", "golang.go"]
}

expect_failures = [
resource.coder_script.code-server
]
}

run "url_with_folder_query" {
command = plan

variables {
agent_id = "foo"
folder = "/home/coder/project"
port = 13337
}

assert {
condition = resource.coder_app.code-server.url == "http://localhost:13337/?folder=%2Fhome%2Fcoder%2Fproject"
error_message = "coder_app URL must include encoded folder query param"
}
}
131 changes: 131 additions & 0 deletions registry/coder/modules/jetbrains/jetbrains.tftest.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
run "requires_agent_and_folder" {
command = plan

# Setting both required vars should plan
variables {
agent_id = "foo"
folder = "/home/coder"
}
}

run "creates_parameter_when_default_empty_latest" {
command = plan

variables {
agent_id = "foo"
folder = "/home/coder"
major_version = "latest"
}

# When default is empty, a coder_parameter should be created
assert {
condition = can(data.coder_parameter.jetbrains_ides[0].type)
error_message = "Expected data.coder_parameter.jetbrains_ides to exist when default is empty"
}
}

run "no_apps_when_default_empty" {
command = plan

variables {
agent_id = "foo"
folder = "/home/coder"
}

assert {
condition = length(resource.coder_app.jetbrains) == 0
error_message = "Expected no coder_app resources when default is empty"
}
}

run "single_app_when_default_GO" {
command = plan

variables {
agent_id = "foo"
folder = "/home/coder"
default = ["GO"]
}

assert {
condition = length(resource.coder_app.jetbrains) == 1
error_message = "Expected exactly one coder_app when default contains GO"
}
}

run "url_contains_required_params" {
command = apply

variables {
agent_id = "test-agent-123"
folder = "/custom/project/path"
default = ["GO"]
}

assert {
condition = anytrue([for app in values(resource.coder_app.jetbrains) : length(regexall("jetbrains://gateway/coder", app.url)) > 0])
error_message = "URL must contain jetbrains scheme"
}

assert {
condition = anytrue([for app in values(resource.coder_app.jetbrains) : length(regexall("&folder=/custom/project/path", app.url)) > 0])
error_message = "URL must include folder path"
}

assert {
condition = anytrue([for app in values(resource.coder_app.jetbrains) : length(regexall("ide_product_code=GO", app.url)) > 0])
error_message = "URL must include product code"
}

assert {
condition = anytrue([for app in values(resource.coder_app.jetbrains) : length(regexall("ide_build_number=", app.url)) > 0])
error_message = "URL must include build number"
}
}

run "includes_agent_name_when_set" {
command = apply

variables {
agent_id = "test-agent-123"
agent_name = "main-agent"
folder = "/custom/project/path"
default = ["GO"]
}

assert {
condition = anytrue([for app in values(resource.coder_app.jetbrains) : length(regexall("&agent_name=main-agent", app.url)) > 0])
error_message = "URL must include agent_name when provided"
}
}

run "parameter_order_when_default_empty" {
command = plan

variables {
agent_id = "foo"
folder = "/home/coder"
coder_parameter_order = 5
}

assert {
condition = data.coder_parameter.jetbrains_ides[0].order == 5
error_message = "Expected coder_parameter order to be set to 5"
}
}

run "app_order_when_default_not_empty" {
command = plan

variables {
agent_id = "foo"
folder = "/home/coder"
default = ["GO"]
coder_app_order = 10
}

assert {
condition = anytrue([for app in values(resource.coder_app.jetbrains) : app.order == 10])
error_message = "Expected coder_app order to be set to 10"
}
}
40 changes: 40 additions & 0 deletions registry/coder/modules/zed/zed.tftest.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
run "default_output" {
command = apply

variables {
agent_id = "foo"
}

assert {
condition = output.zed_url == "zed://ssh/default.coder"
error_message = "zed_url did not match expected default URL"
}
}

run "adds_folder" {
command = apply

variables {
agent_id = "foo"
folder = "/foo/bar"
}

assert {
condition = output.zed_url == "zed://ssh/default.coder/foo/bar"
error_message = "zed_url did not include provided folder path"
}
}

run "adds_agent_name" {
command = apply

variables {
agent_id = "foo"
agent_name = "myagent"
}

assert {
condition = output.zed_url == "zed://ssh/myagent.default.default.coder"
error_message = "zed_url did not include agent_name in hostname"
}
}
26 changes: 26 additions & 0 deletions scripts/terraform_test_all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail

# Find all directories that contain any .tftest.hcl files and run terraform test in each

run_dir() {
local dir="$1"
echo "==> Running terraform test in $dir"
(cd "$dir" && terraform init -upgrade -input=false -no-color > /dev/null && terraform test -no-color -verbose)
}

mapfile -t test_dirs < <(find . -type f -name "*.tftest.hcl" -print0 | xargs -0 -I{} dirname {} | sort -u)

if [[ ${#test_dirs[@]} -eq 0 ]]; then
echo "No .tftest.hcl tests found."
exit 0
fi

status=0
for d in "${test_dirs[@]}"; do
if ! run_dir "$d"; then
status=1
fi
done

exit $status