Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit cb72a16

Browse files
authored
feat: add test framework (#48)
* Add test framework * Add aws-region tests * Add azure-region tests * Update CONTRIBUTING.md * Add formatting * Improve fmt * Format Terraform
1 parent 84bad15 commit cb72a16

24 files changed

Lines changed: 900 additions & 510 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
pull_request:
9+
workflow_dispatch:
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.ref }}
13+
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
14+
15+
jobs:
16+
test:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v2
20+
- uses: oven-sh/setup-bun@v1
21+
with:
22+
bun-version: latest
23+
- run: bun test
24+
fmt:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v2
28+
- uses: oven-sh/setup-bun@v1
29+
with:
30+
bun-version: latest
31+
- run: bun fmt:ci

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
.terraform*
1+
.terraform*
2+
node_modules
3+
*.tfstate
4+
*.tfstate.lock.info

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"files.exclude": {
3+
"**/terraform.tfstate": true,
4+
"**/.terraform": true
5+
}
6+
}

CONTRIBUTING.md

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,13 @@ To create a new module, clone this repository and run:
66
./new.sh MOUDLE_NAME
77
```
88

9-
Test a module by running an instance of Coder on your local machine:
10-
11-
```shell
12-
coder server --in-memory
13-
```
14-
15-
This will create a new module in the modules directory with the given name and scaffolding.
16-
Edit the files, adding your module's implementation, documentation and screenshots.
17-
189
## Testing a Module
1910

20-
Create a template and edit it to include your development module:
11+
A suite of test-helpers exists to run `terraform apply` on modules with variables, and test script output against containers.
2112

22-
> [!NOTE]
23-
> The Docker starter template is recommended for quick-iteration!
13+
Reference existing `*.test.ts` files for implementation.
2414

25-
```hcl
26-
module "MOUDLE_NAME" {
27-
source = "/home/user/coder/modules/MOUDLE_NAME"
28-
}
15+
```sh
16+
# Run tests for a specific module!
17+
$ bun test -t '<module>'
2918
```
30-
31-
You can also test your module by specifying the source as a git repository:
32-
33-
```hcl
34-
module "MOUDLE_NAME" {
35-
source = "git::https://github.com/<USERNAME>/<REPO>.git//<FOLDER>?ref=<BRANCH>"
36-
}
37-
```
38-
39-
Build a workspace and your module will be consumed! 🥳
40-
41-
Open a pull-request with your module, a member of the Coder team will
42-
manually test it, and after-merge it will appear on the Registry.

aws-region/main.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, expect, it } from "bun:test";
2+
import {
3+
executeScriptInContainer,
4+
runTerraformApply,
5+
runTerraformInit,
6+
testRequiredVariables,
7+
} from "../test";
8+
9+
describe("aws-region", async () => {
10+
await runTerraformInit(import.meta.dir);
11+
12+
testRequiredVariables(import.meta.dir, {});
13+
14+
it("default output", async () => {
15+
const state = await runTerraformApply(import.meta.dir, {});
16+
expect(state.outputs.value.value).toBe("us-east-1");
17+
});
18+
19+
it("customized default", async () => {
20+
const state = await runTerraformApply(import.meta.dir, {
21+
default: "us-west-2",
22+
});
23+
expect(state.outputs.value.value).toBe("us-west-2");
24+
});
25+
});

aws-region/main.tf

Lines changed: 101 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,45 @@ terraform {
1010
}
1111

1212
variable "display_name" {
13-
default = "AWS Region"
14-
description = "The display name of the parameter."
15-
type = string
13+
default = "AWS Region"
14+
description = "The display name of the parameter."
15+
type = string
1616
}
1717

1818
variable "description" {
19-
default = "The region to deploy workspace infrastructure."
20-
description = "The description of the parameter."
21-
type = string
19+
default = "The region to deploy workspace infrastructure."
20+
description = "The description of the parameter."
21+
type = string
2222
}
2323

2424
variable "default" {
25-
default = "us-east-1"
26-
description = "The default region to use if no region is specified."
27-
type = string
25+
default = "us-east-1"
26+
description = "The default region to use if no region is specified."
27+
type = string
2828
}
2929

3030
variable "mutable" {
31-
default = false
32-
description = "Whether the parameter can be changed after creation."
33-
type = bool
31+
default = false
32+
description = "Whether the parameter can be changed after creation."
33+
type = bool
3434
}
3535

3636
variable "custom_names" {
37-
default = {}
38-
description = "A map of custom display names for region IDs."
39-
type = map(string)
37+
default = {}
38+
description = "A map of custom display names for region IDs."
39+
type = map(string)
4040
}
4141

4242
variable "custom_icons" {
43-
default = {}
44-
description = "A map of custom icons for region IDs."
45-
type = map(string)
43+
default = {}
44+
description = "A map of custom icons for region IDs."
45+
type = map(string)
4646
}
4747

4848
variable "exclude" {
49-
default = []
50-
description = "A list of region IDs to exclude."
51-
type = list(string)
49+
default = []
50+
description = "A list of region IDs to exclude."
51+
type = list(string)
5252
}
5353

5454
locals {
@@ -57,92 +57,92 @@ locals {
5757
# the provider, which requires a region.
5858
regions = {
5959
"ap-northeast-1" = {
60-
name = "Asia Pacific (Tokyo)"
61-
icon = "/emojis/1f1ef-1f1f5.png"
62-
}
63-
"ap-northeast-2" = {
64-
name = "Asia Pacific (Seoul)"
65-
icon = "/emojis/1f1f0-1f1f7.png"
66-
}
67-
"ap-northeast-3" = {
68-
name = "Asia Pacific (Osaka)"
69-
icon = "/emojis/1f1ef-1f1f5.png"
70-
}
71-
"ap-south-1" = {
72-
name = "Asia Pacific (Mumbai)"
73-
icon = "/emojis/1f1ee-1f1f3.png"
74-
}
75-
"ap-southeast-1" = {
76-
name = "Asia Pacific (Singapore)"
77-
icon = "/emojis/1f1f8-1f1ec.png"
78-
}
79-
"ap-southeast-2" = {
80-
name = "Asia Pacific (Sydney)"
81-
icon = "/emojis/1f1e6-1f1fa.png"
82-
}
83-
"ca-central-1" = {
84-
name = "Canada (Central)"
85-
icon = "/emojis/1f1e8-1f1e6.png"
86-
}
87-
"eu-central-1" = {
88-
name = "EU (Frankfurt)"
89-
icon = "/emojis/1f1ea-1f1fa.png"
90-
}
91-
"eu-north-1" = {
92-
name = "EU (Stockholm)"
93-
icon = "/emojis/1f1ea-1f1fa.png"
94-
}
95-
"eu-west-1" = {
96-
name = "EU (Ireland)"
97-
icon = "/emojis/1f1ea-1f1fa.png"
98-
}
99-
"eu-west-2" = {
100-
name = "EU (London)"
101-
icon = "/emojis/1f1ea-1f1fa.png"
102-
}
103-
"eu-west-3" = {
104-
name = "EU (Paris)"
105-
icon = "/emojis/1f1ea-1f1fa.png"
106-
}
107-
"sa-east-1" = {
108-
name = "South America (São Paulo)"
109-
icon = "/emojis/1f1e7-1f1f7.png"
110-
}
111-
"us-east-1" = {
112-
name = "US East (N. Virginia)"
113-
icon = "/emojis/1f1fa-1f1f8.png"
114-
}
115-
"us-east-2" = {
116-
name = "US East (Ohio)"
117-
icon = "/emojis/1f1fa-1f1f8.png"
118-
}
119-
"us-west-1" = {
120-
name = "US West (N. California)"
121-
icon = "/emojis/1f1fa-1f1f8.png"
122-
}
123-
"us-west-2" = {
124-
name = "US West (Oregon)"
125-
icon = "/emojis/1f1fa-1f1f8.png"
126-
}
60+
name = "Asia Pacific (Tokyo)"
61+
icon = "/emojis/1f1ef-1f1f5.png"
62+
}
63+
"ap-northeast-2" = {
64+
name = "Asia Pacific (Seoul)"
65+
icon = "/emojis/1f1f0-1f1f7.png"
66+
}
67+
"ap-northeast-3" = {
68+
name = "Asia Pacific (Osaka)"
69+
icon = "/emojis/1f1ef-1f1f5.png"
70+
}
71+
"ap-south-1" = {
72+
name = "Asia Pacific (Mumbai)"
73+
icon = "/emojis/1f1ee-1f1f3.png"
74+
}
75+
"ap-southeast-1" = {
76+
name = "Asia Pacific (Singapore)"
77+
icon = "/emojis/1f1f8-1f1ec.png"
78+
}
79+
"ap-southeast-2" = {
80+
name = "Asia Pacific (Sydney)"
81+
icon = "/emojis/1f1e6-1f1fa.png"
82+
}
83+
"ca-central-1" = {
84+
name = "Canada (Central)"
85+
icon = "/emojis/1f1e8-1f1e6.png"
86+
}
87+
"eu-central-1" = {
88+
name = "EU (Frankfurt)"
89+
icon = "/emojis/1f1ea-1f1fa.png"
90+
}
91+
"eu-north-1" = {
92+
name = "EU (Stockholm)"
93+
icon = "/emojis/1f1ea-1f1fa.png"
94+
}
95+
"eu-west-1" = {
96+
name = "EU (Ireland)"
97+
icon = "/emojis/1f1ea-1f1fa.png"
98+
}
99+
"eu-west-2" = {
100+
name = "EU (London)"
101+
icon = "/emojis/1f1ea-1f1fa.png"
102+
}
103+
"eu-west-3" = {
104+
name = "EU (Paris)"
105+
icon = "/emojis/1f1ea-1f1fa.png"
106+
}
107+
"sa-east-1" = {
108+
name = "South America (São Paulo)"
109+
icon = "/emojis/1f1e7-1f1f7.png"
110+
}
111+
"us-east-1" = {
112+
name = "US East (N. Virginia)"
113+
icon = "/emojis/1f1fa-1f1f8.png"
114+
}
115+
"us-east-2" = {
116+
name = "US East (Ohio)"
117+
icon = "/emojis/1f1fa-1f1f8.png"
118+
}
119+
"us-west-1" = {
120+
name = "US West (N. California)"
121+
icon = "/emojis/1f1fa-1f1f8.png"
122+
}
123+
"us-west-2" = {
124+
name = "US West (Oregon)"
125+
icon = "/emojis/1f1fa-1f1f8.png"
126+
}
127127
}
128128
}
129129

130130
data "coder_parameter" "region" {
131-
name = "aws_region"
132-
display_name = var.display_name
133-
description = var.description
134-
default = var.default
135-
mutable = var.mutable
136-
dynamic "option" {
137-
for_each = { for k, v in local.regions : k => v if !(contains(var.exclude, k)) }
138-
content {
139-
name = try(var.custom_names[option.key], option.value.name)
140-
icon = try(var.custom_icons[option.key], option.value.icon)
141-
value = option.key
142-
}
131+
name = "aws_region"
132+
display_name = var.display_name
133+
description = var.description
134+
default = var.default
135+
mutable = var.mutable
136+
dynamic "option" {
137+
for_each = { for k, v in local.regions : k => v if !(contains(var.exclude, k)) }
138+
content {
139+
name = try(var.custom_names[option.key], option.value.name)
140+
icon = try(var.custom_icons[option.key], option.value.icon)
141+
value = option.key
143142
}
143+
}
144144
}
145145

146146
output "value" {
147-
value = data.coder_parameter.region.value
147+
value = data.coder_parameter.region.value
148148
}

azure-region/main.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, expect, it } from "bun:test";
2+
import {
3+
executeScriptInContainer,
4+
runTerraformApply,
5+
runTerraformInit,
6+
testRequiredVariables,
7+
} from "../test";
8+
9+
describe("azure-region", async () => {
10+
await runTerraformInit(import.meta.dir);
11+
12+
testRequiredVariables(import.meta.dir, {});
13+
14+
it("default output", async () => {
15+
const state = await runTerraformApply(import.meta.dir, {});
16+
expect(state.outputs.value.value).toBe("eastus");
17+
});
18+
19+
it("customized default", async () => {
20+
const state = await runTerraformApply(import.meta.dir, {
21+
default: "westus",
22+
});
23+
expect(state.outputs.value.value).toBe("westus");
24+
});
25+
});

0 commit comments

Comments
 (0)