Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ monorepos
decrypts
deploytf
href
init

^.+[-:_]\w+$
[a-z]+([A-Z0-9]|[A-Z0-9]\w+)
Expand Down
4 changes: 4 additions & 0 deletions src/nav.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,10 @@ const fullNav: FullNav = {
title: 'AWS API Gateway Throttle',
href: '/guides/terraform/api-gateway-throttle',
},
{
title: 'Testing AWS resources with Terratest',
href: '/guides/terraform/terratest',
},
],
},
],
Expand Down
6 changes: 6 additions & 0 deletions src/pages/guides/terraform.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ export const description = 'How to work with the Terraform Providers'
name="AWS API Gateway Throttle"
description="Extend the Nitric AWS Terraform provider to set API Gateway throttling limits"
/>
<Guide
customIcon={SiTerraform}
href="/guides/terraform/terratest"
name="Testing AWS resources with Terratest"
description="Use Terratest to validate the infrastructure of a Nitric GO project deployed with Terraform"
/>
</GuidesGrid>
260 changes: 260 additions & 0 deletions src/pages/guides/terraform/terratest.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
export const description =
'Use Terratest to validate the infrastructure of a Nitric GO project deployed with Terraform'

# Testing AWS resources with Terratest

This guide will walk you through adding Terratest to a Nitric project.

## How Terratest works

Terratest is designed to automate the entire process of testing your Terraform code with the following steps:

- Initialize: Terratest will automatically run terraform init to initialize the Terraform working directory.
- Apply: It will then run terraform apply to deploy the infrastructure as defined in your Terraform code.
- Assert: The test script will then run assertions to check that the infrastructure was created as expected.
- Teardown: Finally, it will run terraform destroy to tear down the infrastructure after the test completes.

## What we'll be doing

1. Create and set up your application.
2. Deploying to AWS with a Terraform provider.
3. Add and execute Terratest

## Create and set up your application.

Our sample project creates a real-time communication service using websockets and a key-value store for connections.

We intend to deploy to AWS and will use Terratest to ensure that the following:

- **API Gateway WebSocket**: Confirm that the webSocket endpoint is correctly configured for real-time communication.
- **DynamoDB Table**: Verify that the key-value store for connections is created and operational.
- **IAM Roles**: Ensure that permissions for interacting with AWS services are correctly set up.

Let's start by creating a new project for our application.

```bash
nitric new my-websocket-app go-starter
```

### Application code

Replace the contents of `services\hello.go` with our websockets application.

```go
package main

import (
"context"
"fmt"

"github.com/nitrictech/go-sdk/handler"
"github.com/nitrictech/go-sdk/nitric"
)

func main() {
// Create a WebSocket endpoint named "public".
ws, err := nitric.NewWebsocket("public")
if err != nil {
fmt.Println("Error creating WebSocket:", err)
return
}

// Initialize a KV store named "connections" with Get, Set, and Delete permissions.
connections, err := nitric.NewKv("connections").Allow(nitric.KvStoreGet, nitric.KvStoreSet, nitric.KvStoreDelete)
if err != nil {
fmt.Println("Error creating KV store:", err)
return
}

// Handle new WebSocket connections by storing the connection ID in the KV store.
ws.On(handler.WebsocketConnect, func(ctx *handler.WebsocketContext, next handler.WebsocketHandler) (*handler.WebsocketContext, error) {
err := connections.Set(context.TODO(), ctx.Request.ConnectionID(), map[string]interface{}{
"connectionId": ctx.Request.ConnectionID(),
})
if err != nil {
return ctx, err
}

return next(ctx)
})

// Handle WebSocket disconnections by removing the connection ID from the KV store.
ws.On(handler.WebsocketDisconnect, func(ctx *handler.WebsocketContext, next handler.WebsocketHandler) (*handler.WebsocketContext, error) {
err := connections.Delete(context.TODO(), ctx.Request.ConnectionID())
if err != nil {
return ctx, err
}

return next(ctx)
})

// Handle incoming messages by broadcasting them to all other connections.
ws.On(handler.WebsocketMessage, func(ctx *handler.WebsocketContext, next handler.WebsocketHandler) (*handler.WebsocketContext, error) {
connectionStream, err := connections.Keys(context.TODO())
if err != nil {
return ctx, err
}

senderId := ctx.Request.ConnectionID()

for {
connectionId, err := connectionStream.Recv()
if err != nil {
break
}

if connectionId == senderId {
continue
}

message := fmt.Sprintf("%s: %s", senderId, ctx.Request.Message())
err = ws.Send(context.TODO(), connectionId, []byte(message))
if err != nil {
return ctx, err
}
}

return next(ctx)
})

// Start the Nitric service to handle WebSocket events.
if err := nitric.Run(); err != nil {
fmt.Println("Error running Nitric service:", err)
}
}
```

## Deploying to AWS with a Terraform provider

To deploy your application with Terraform you'll need to use Nitric's Terraform providers. You can learn more about using Nitric with Terraform [here](/reference/providers/terraform).

```
nitric stack new dev aws-tf
```

Update this newly created stack file to include your target region:

```yaml
# The nitric provider to use
provider: nitric/[email protected]

# The target aws region to deploy to
region: us-east-2
```
The Nitric Terraform providers are currently in preview, to enable them you'll need to enable beta-providers in your Nitric project. You can do this by adding the following to your project's nitric.yaml file:
```
preview:
- beta-providers
```
Once you've created your Nitric stack, you can generate the Terraform code by running the following command:
```
nitric up
```

This will generate the Terraform code for your Nitric application into a folder named cdktf.out by default.

## Add and execute Terratest

Add the necessary dependencies for Terratest:

```bash
go get github.com/gruntwork-io/terratest/modules/terraform
go get github.com/stretchr/testify/assert
go get github.com/aws/aws-sdk-go/aws
go get github.com/aws/aws-sdk-go/aws/session
go get github.com/aws/aws-sdk-go/service/apigatewayv2
go get github.com/aws/aws-sdk-go/service/dynamodb
go get github.com/aws/aws-sdk-go/service/iam
```

### Create the Test File

Create a new file named `test_terraform_resources.go` in your project’s test directory:

```bash
mkdir -p test
touch test/test_terraform_resources.go
```

Add the following code to `test_terraform_resources.go`:

```go
package test

import (
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/apigatewayv2"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)

func TestTerraformResources(t *testing.T) {
// Set Terraform options, specifying the directory with your Terraform configuration
terraformOptions := &terraform.Options{
TerraformDir: "../cdktf.out/stacks/go-realtime-dev",
}

// Ensure resources are destroyed after test completion
defer terraform.Destroy(t, terraformOptions)

// Initialize and apply the Terraform configuration
terraform.InitAndApply(t, terraformOptions)

// Initialize AWS session for interacting with AWS services
sess := session.Must(session.NewSession(&aws.Config{Region: aws.String("us-east-2")}))

// Test DynamoDB table creation (key-value store)
dynamoClient := dynamodb.New(sess)
tableName := "connections" // Name of the DynamoDB table to check
_, err := dynamoClient.DescribeTable(&dynamodb.DescribeTableInput{
TableName: aws.String(tableName),
})
assert.NoError(t, err, "Expected DynamoDB table 'connections' to be created")

// Test IAM role creation
iamClient := iam.New(sess)
roleName := "go-realtime_services-hello" // Name of the IAM role to check
_, err = iamClient.GetRole(&iam.GetRoleInput{
RoleName: aws.String(roleName),
})
assert.NoError(t, err, "Expected IAM role 'go-realtime_services-hello' to be created")

// Test API gateway webSocket creation
apiClient := apigatewayv2.New(sess)
apiName := "public" // Name of the API Gateway WebSocket to check
result, err := apiClient.GetApis(&apigatewayv2.GetApisInput{})
assert.NoError(t, err)
found := false
for _, api := range result.Items {
if *api.Name == apiName {
found = true
break
}
}
assert.True(t, found, "Expected API Gateway WebSocket 'public' to be created")
}
```

### Run the tests

To run the tests, navigate to your project’s root directory and execute the Go test command:

```bash
go test -v ./test
```

This will:

1. Deploy the infrastructure using Terraform.
2. Validate the creation of the DynamoDB table, IAM role, and API Gateway WebSocket.
3. Clean up the infrastructure after testing.

The output should confirm the successful creation and validation of each resource.