* feat: initialize Terraform Plugin Framework * fix docker-compose path for tests * fix: ensure documentation can be generated with old provider SDK and new plugin framework * lint
193 lines
3.9 KiB
Go
193 lines
3.9 KiB
Go
package v1
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/apparentlymart/go-cidr/cidr"
|
|
"github.com/filipowm/go-unifi/unifi"
|
|
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
|
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
|
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/modules/compose"
|
|
)
|
|
|
|
var providerFactories = map[string]func() (*schema.Provider, error){
|
|
"unifi": func() (*schema.Provider, error) {
|
|
return New("acctest")(), nil
|
|
},
|
|
}
|
|
|
|
var testClient unifi.Client
|
|
|
|
func TestMain(m *testing.M) {
|
|
if os.Getenv("TF_ACC") == "" {
|
|
// short circuit non-acceptance test runs
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
os.Exit(runAcceptanceTests(m))
|
|
}
|
|
|
|
func runAcceptanceTests(m *testing.M) int {
|
|
dc, err := compose.NewDockerCompose("../../../docker-compose.yaml")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
if err = dc.WithOsEnv().Up(ctx, compose.Wait(true)); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
defer func() {
|
|
if err := dc.Down(context.Background(), compose.RemoveOrphans(true), compose.RemoveImagesLocal); err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
|
|
container, err := dc.ServiceContainer(ctx, "unifi")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Dump the container logs on exit.
|
|
//
|
|
// TODO: Use https://pkg.go.dev/github.com/testcontainers/testcontainers-go#LogConsumer instead.
|
|
defer func() {
|
|
if os.Getenv("UNIFI_STDOUT") == "" {
|
|
return
|
|
}
|
|
|
|
stream, err := container.Logs(ctx)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
buffer := new(bytes.Buffer)
|
|
buffer.ReadFrom(stream)
|
|
testcontainers.Logger.Printf("%s", buffer)
|
|
}()
|
|
|
|
endpoint, err := container.PortEndpoint(ctx, "8443/tcp", "https")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
const user = "admin"
|
|
const password = "admin"
|
|
|
|
if err = os.Setenv("UNIFI_USERNAME", user); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err = os.Setenv("UNIFI_PASSWORD", password); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err = os.Setenv("UNIFI_INSECURE", "true"); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err = os.Setenv("UNIFI_API", endpoint); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
testClient, err = unifi.NewClient(&unifi.ClientConfig{
|
|
URL: endpoint,
|
|
User: user,
|
|
Password: password,
|
|
HttpRoundTripperProvider: func() http.RoundTripper {
|
|
return createHTTPTransport(true, "unifi")
|
|
},
|
|
ValidationMode: unifi.DisableValidation,
|
|
Logger: unifi.NewDefaultLogger(unifi.WarnLevel),
|
|
})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return m.Run()
|
|
}
|
|
|
|
func importStep(name string, ignore ...string) resource.TestStep {
|
|
step := resource.TestStep{
|
|
ResourceName: name,
|
|
ImportState: true,
|
|
ImportStateVerify: true,
|
|
}
|
|
|
|
if len(ignore) > 0 {
|
|
step.ImportStateVerifyIgnore = ignore
|
|
}
|
|
|
|
return step
|
|
}
|
|
|
|
func siteAndIDImportStateIDFunc(resourceName string) func(*terraform.State) (string, error) {
|
|
return func(s *terraform.State) (string, error) {
|
|
rs, ok := s.RootModule().Resources[resourceName]
|
|
if !ok {
|
|
return "", fmt.Errorf("not found: %s", resourceName)
|
|
}
|
|
networkID := rs.Primary.Attributes["id"]
|
|
site := rs.Primary.Attributes["site"]
|
|
return site + ":" + networkID, nil
|
|
}
|
|
}
|
|
|
|
func preCheck(t *testing.T) {
|
|
variables := []string{
|
|
"UNIFI_USERNAME",
|
|
"UNIFI_PASSWORD",
|
|
"UNIFI_API",
|
|
}
|
|
|
|
for _, variable := range variables {
|
|
value := os.Getenv(variable)
|
|
if value == "" {
|
|
t.Fatalf("`%s` must be set for acceptance tests!", variable)
|
|
}
|
|
}
|
|
}
|
|
|
|
const (
|
|
vlanMin = 2
|
|
vlanMax = 4095
|
|
)
|
|
|
|
var (
|
|
network = &net.IPNet{
|
|
IP: net.IPv4(10, 0, 0, 0).To4(),
|
|
Mask: net.IPv4Mask(255, 0, 0, 0),
|
|
}
|
|
|
|
vlanLock sync.Mutex
|
|
vlanNext = vlanMin
|
|
)
|
|
|
|
func getTestVLAN(t *testing.T) (*net.IPNet, int) {
|
|
vlanLock.Lock()
|
|
defer vlanLock.Unlock()
|
|
|
|
vlan := vlanNext
|
|
vlanNext++
|
|
|
|
subnet, err := cidr.Subnet(network, int(math.Ceil(math.Log2(vlanMax))), vlan)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
return subnet, vlan
|
|
}
|