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

## Summary
- Introduces Terraform native tests (`terraform test`) alongside
existing Bun tests
- Migrates tests for modules: jetbrains, zed, and code-server
- Removes Bun test files for these migrated modules only
- Adds repo-wide test runner script for Terraform tests
- Updates docs and new-module sample to reflect Terraform tests

## Transition plan
- Mixed mode: Other modules retain Bun tests; CI should run both Bun and
Terraform tests temporarily
- Follow the linked epic to migrate remaining modules

## Test plan
- Run: `./scripts/terraform_test_all.sh` (passes locally)
- Bun tests still available for non-migrated modules

## Affected paths
- registry/coder/modules/jetbrains/jetbrains.tftest.hcl
- registry/coder/modules/zed/zed.tftest.hcl
- registry/coder/modules/code-server/code-server.tftest.hcl
- scripts/terraform_test_all.sh
- examples/modules/MODULE_NAME.tftest.hcl
- CONTRIBUTING.md


Contributes to #308
This commit is contained in:
Atif Ali
2025-08-12 07:09:09 +05:00
committed by GitHub
parent 2646b36cb1
commit 45b72c7241
8 changed files with 287 additions and 14 deletions
+16 -11
View File
@@ -24,7 +24,7 @@ The Coder Registry is a collection of Terraform modules and templates for Coder
### Install Dependencies ### Install Dependencies
Install Bun: Install Bun (for formatting and scripts):
```bash ```bash
curl -fsSL https://bun.sh/install | bash curl -fsSL https://bun.sh/install | bash
@@ -124,19 +124,23 @@ This script generates:
- Accurate description and usage examples - Accurate description and usage examples
- Correct icon path (usually `../../../../.icons/your-icon.svg`) - Correct icon path (usually `../../../../.icons/your-icon.svg`)
- Proper tags that describe your module - 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. **Add any scripts** or additional files your module needs
### 4. Test and Submit ### 4. Test and Submit
```bash ```bash
# Test your module # Test your module (from the module directory)
bun test -t 'module-name' terraform init -upgrade
terraform test -verbose
# Or run all tests in the repo
./scripts/terraform_test_all.sh
# Format code # Format code
bun fmt bun run fmt
# Commit and create PR # Commit and create PR (do not push to main directly)
git add . git add .
git commit -m "Add [module-name] module" git commit -m "Add [module-name] module"
git push origin your-branch git push origin your-branch
@@ -335,11 +339,12 @@ coder templates push test-[template-name] -d .
### 2. Test Your Changes ### 2. Test Your Changes
```bash ```bash
# Test a specific module # Test a specific module (from the module directory)
bun test -t 'module-name' terraform init -upgrade
terraform test -verbose
# Test all modules # Test all modules
bun test ./scripts/terraform_test_all.sh
``` ```
### 3. Maintain Backward Compatibility ### 3. Maintain Backward Compatibility
@@ -388,7 +393,7 @@ Example: `https://github.com/coder/registry/compare/main...your-branch?template=
### Every Module Must Have ### Every Module Must Have
- `main.tf` - Terraform code - `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 - `README.md` - Documentation with frontmatter
### Every Template Must Have ### Every Template Must Have
@@ -488,6 +493,6 @@ When reporting bugs, include:
2. **No tests** or broken tests 2. **No tests** or broken tests
3. **Hardcoded values** instead of variables 3. **Hardcoded values** instead of variables
4. **Breaking changes** without defaults 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! 🚀 Happy contributing! 🚀
+2 -2
View File
@@ -18,9 +18,9 @@ sudo apt install golang-go
Check that PRs have: 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 - [ ] Proper frontmatter in README
- [ ] Working tests (`bun test`) - [ ] Working tests (`terraform test`)
- [ ] Formatted code (`bun run fmt`) - [ ] Formatted code (`bun run fmt`)
- [ ] Avatar image for new namespaces (`avatar.png` or `avatar.svg` in `.images/`) - [ ] Avatar image for new namespaces (`avatar.png` or `avatar.svg` in `.images/`)
+21
View File
@@ -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"
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
"fmt": "bun x prettier --write **/*.sh **/*.ts **/*.md *.md && terraform fmt -recursive -diff", "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", "fmt:ci": "bun x prettier --check **/*.sh **/*.ts **/*.md *.md && terraform fmt -check -recursive -diff",
"terraform-validate": "./scripts/terraform_validate.sh", "terraform-validate": "./scripts/terraform_validate.sh",
"test": "bun test", "test": "./scripts/terraform_test_all.sh",
"update-version": "./update-version.sh" "update-version": "./update-version.sh"
}, },
"devDependencies": { "devDependencies": {
@@ -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"
}
}
@@ -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
View File
@@ -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
View File
@@ -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