package provider_test import ( "fmt" "os" "testing" "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/maxvojtkov/terraform-provider-dokploy/internal/provider" ) // Acceptance tests talk to a real Dokploy instance. They create resources with // a `tfacc-` prefix and destroy them again, so point them at a scratch // instance rather than production: // // TF_ACC=1 \ // DOKPLOY_HOST=https://dokploy.example.com \ // DOKPLOY_API_KEY=... \ // go test ./internal/provider/ -v -timeout 30m var protoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ "dokploy": providerserver.NewProtocol6WithError(provider.New("test")()), } func testAccPreCheck(t *testing.T) { t.Helper() for _, key := range []string{"DOKPLOY_HOST", "DOKPLOY_API_KEY"} { if os.Getenv(key) == "" { t.Fatalf("%s must be set for acceptance tests", key) } } } func TestAccProject(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: protoV6ProviderFactories, Steps: []resource.TestStep{ { Config: ` resource "dokploy_project" "test" { name = "tfacc-project" description = "created by acceptance tests" }`, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("dokploy_project.test", "name", "tfacc-project"), resource.TestCheckResourceAttr("dokploy_project.test", "description", "created by acceptance tests"), resource.TestCheckResourceAttrSet("dokploy_project.test", "id"), // Dokploy creates a default environment with every project. resource.TestCheckResourceAttrSet("dokploy_project.test", "default_environment_id"), resource.TestCheckResourceAttrSet("dokploy_project.test", "organization_id"), ), }, { ResourceName: "dokploy_project.test", ImportState: true, ImportStateVerify: true, }, { Config: ` resource "dokploy_project" "test" { name = "tfacc-project-renamed" description = "updated" env = "SHARED=1" }`, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("dokploy_project.test", "name", "tfacc-project-renamed"), resource.TestCheckResourceAttr("dokploy_project.test", "description", "updated"), resource.TestCheckResourceAttr("dokploy_project.test", "env", "SHARED=1"), ), }, }, }) } func TestAccEnvironment(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: protoV6ProviderFactories, Steps: []resource.TestStep{ { Config: ` resource "dokploy_project" "test" { name = "tfacc-env-project" } resource "dokploy_environment" "test" { name = "tfacc-staging" project_id = dokploy_project.test.id env = "STAGE=1" }`, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("dokploy_environment.test", "name", "tfacc-staging"), // env is only writable through a follow-up update; assert it stuck. resource.TestCheckResourceAttr("dokploy_environment.test", "env", "STAGE=1"), resource.TestCheckResourceAttr("dokploy_environment.test", "is_default", "false"), ), }, }, }) } // Applications are created with a minimal payload and then configured through // application.update, so this asserts that update-only fields survive create. func TestAccApplicationDockerSource(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: protoV6ProviderFactories, Steps: []resource.TestStep{ { Config: testAccApplicationConfig("512m", 1), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("dokploy_application.test", "source_type", "docker"), resource.TestCheckResourceAttr("dokploy_application.test", "docker_image", "traefik/whoami:latest"), resource.TestCheckResourceAttr("dokploy_application.test", "memory_limit", "512m"), resource.TestCheckResourceAttr("dokploy_application.test", "replicas", "1"), resource.TestCheckResourceAttr("dokploy_application.test", "env", "GREETING=hello"), resource.TestCheckResourceAttrSet("dokploy_application.test", "app_name"), ), }, { ResourceName: "dokploy_application.test", ImportState: true, ImportStateVerify: true, }, { // An in-place update must not force replacement. Config: testAccApplicationConfig("1g", 3), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("dokploy_application.test", "memory_limit", "1g"), resource.TestCheckResourceAttr("dokploy_application.test", "replicas", "3"), ), }, }, }) } func testAccApplicationConfig(memoryLimit string, replicas int) string { return fmt.Sprintf(` resource "dokploy_project" "test" { name = "tfacc-app-project" } resource "dokploy_application" "test" { name = "tfacc-app" environment_id = dokploy_project.test.default_environment_id source_type = "docker" docker_image = "traefik/whoami:latest" env = "GREETING=hello" memory_limit = %q replicas = %d }`, memoryLimit, replicas) } func TestAccComposeAndDomain(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: protoV6ProviderFactories, Steps: []resource.TestStep{ { Config: ` resource "dokploy_project" "test" { name = "tfacc-compose-project" } resource "dokploy_compose" "test" { name = "tfacc-stack" environment_id = dokploy_project.test.default_environment_id compose_type = "docker-compose" source_type = "raw" compose_file = <<-YAML services: whoami: image: traefik/whoami:latest YAML } resource "dokploy_domain" "test" { compose_id = dokploy_compose.test.id domain_type = "compose" service_name = "whoami" host = "tfacc.example.invalid" port = 80 https = false certificate_type = "none" }`, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("dokploy_compose.test", "compose_type", "docker-compose"), resource.TestCheckResourceAttrSet("dokploy_compose.test", "app_name"), resource.TestCheckResourceAttr("dokploy_domain.test", "host", "tfacc.example.invalid"), resource.TestCheckResourceAttr("dokploy_domain.test", "service_name", "whoami"), ), }, }, }) } // Databases follow the same create-then-update path as applications. func TestAccPostgres(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: protoV6ProviderFactories, Steps: []resource.TestStep{ { Config: ` resource "dokploy_project" "test" { name = "tfacc-pg-project" } resource "dokploy_postgres" "test" { name = "tfacc-pg" environment_id = dokploy_project.test.default_environment_id docker_image = "postgres:16-alpine" database_name = "acc" database_user = "acc" database_password = "acc-password" memory_limit = "512m" }`, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("dokploy_postgres.test", "database_name", "acc"), // memory_limit is rejected by postgres.create and must be // written by the follow-up update. resource.TestCheckResourceAttr("dokploy_postgres.test", "memory_limit", "512m"), ), }, }, }) } // redirects.create returns `true` rather than a row, so the provider finds the // new ID by diffing the application's redirect list. func TestAccRedirectIDDiscovery(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: protoV6ProviderFactories, Steps: []resource.TestStep{ { Config: ` resource "dokploy_project" "test" { name = "tfacc-redirect-project" } resource "dokploy_application" "test" { name = "tfacc-redirect-app" environment_id = dokploy_project.test.default_environment_id source_type = "docker" docker_image = "traefik/whoami:latest" } resource "dokploy_redirect" "test" { application_id = dokploy_application.test.id regex = "^https://old\\.(.*)" replacement = "https://new.$${1}" permanent = true }`, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet("dokploy_redirect.test", "id"), resource.TestCheckResourceAttr("dokploy_redirect.test", "permanent", "true"), ), }, }, }) } func TestAccProjectsDataSource(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: protoV6ProviderFactories, Steps: []resource.TestStep{ { Config: ` resource "dokploy_project" "test" { name = "tfacc-ds-project" } data "dokploy_project" "test" { id = dokploy_project.test.id }`, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("data.dokploy_project.test", "name", "tfacc-ds-project"), resource.TestCheckResourceAttrSet("data.dokploy_project.test", "default_environment_id"), resource.TestCheckResourceAttr("data.dokploy_project.test", "environments.#", "1"), ), }, }, }) }