diff --git a/internal/provider/fixtures/org_setting_resource/org_setting_config.tf b/internal/provider/fixtures/org_setting_resource/org_setting_config.tf new file mode 100644 index 00000000..cd3a8155 --- /dev/null +++ b/internal/provider/fixtures/org_setting_resource/org_setting_config.tf @@ -0,0 +1,168 @@ + + ap_updown_threshold = 30 + device_updown_threshold = 45 + gateway_updown_threshold = 60 + switch_updown_threshold = 75 + ui_idle_timeout = 120 + disable_pcap = false + disable_remote_shell = false + + cacerts = [ + "-----BEGIN CERTIFICATE-----\nMIIC2jCCAcICCQDXrcsSSdA+CjANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZy\nYWRzZWMtZXhhbXBsZS1kb21haW4xMB4XDTIzMDEwMTAwMDAwMFoXDTI0MDEwMTAw\nMDAwMFowITEfMB0GA1UEAwwWcmFkc2VjLWV4YW1wbGUtZG9tYWluMTCCASIwDQYJ\nKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM3Q2NP+example+cert+data+here\n-----END CERTIFICATE-----" + ] + + api_policy { + no_reveal = true + } + + celona { + api_key = "celona_api_key_example" + api_prefix = "celona_api_prefix_example" + } + + installer { + allow_all_devices = true + allow_all_sites = false + extra_site_ids = ["550e8400-e29b-41d4-a716-446655440001", "550e8400-e29b-41d4-a716-446655440002"] + grace_period = 7200 + } + + jcloud_ra { + org_apitoken_name = "jcloud_ra_org_token_name_example" + org_id = "jcloud_ra_org_id_example" + } + + junos_shell_access { + admin = "admin" + helpdesk = "viewer" + read = "viewer" + write = "admin" + } + + marvis { + auto_operations { + bounce_port_for_abnormal_poe_client = true + disable_port_when_ddos_protocol_violation = true + disable_port_when_rogue_dhcp_server_detected = true + } + } + + mgmt { + mxtunnel_ids = ["550e8400-e29b-41d4-a716-446655440003", "550e8400-e29b-41d4-a716-446655440004"] + use_mxtunnel = true + use_wxtunnel = false + } + + mist_nac { + disable_rsae_algorithms = false + eap_ssl_security_level = 2 + eu_only = false + idp_machine_cert_lookup_field = "cn" + idp_user_cert_lookup_field = "email" + idps = [ + { + id = "550e8400-e29b-41d4-a716-446655440006" + user_realms = ["example.com", "test.org"] + exclude_realms = ["excluded.example.com"] + } + ] + # servier_cert + use_ip_version = "v4" + use_ssl_port = false + } + + mxedge_mgmt { + config_auto_revert = true + fips_enabled = false + oob_ip_type = "dhcp" + oob_ip_type6 = "dhcp" + } + + optic_port_config = { + "et-0/0/47" = { + channelized = true + speed = "25g" + } + "et-0/0/48-49" = { + channelized = false + speed = "50g" + } + } + + password_policy { + enabled = true + expiry_in_days = 90 + min_length = 12 + requires_special_char = true + requires_two_factor_auth = true + } + + security { + disable_local_ssh = false + limit_ssh_access = true + } + + switch_mgmt { + ap_affinity_threshold = 15 + } + + synthetic_test { + aggressiveness = "high" + custom_probes = { + "google_dns" = { + type = "icmp" + host = "8.8.8.8" + threshold = 100 + aggressiveness = "high" + } + "web_check" = { + type = "curl" + url = "https://www.google.com" + threshold = 500 + aggressiveness = "auto" + } + "tcp_check" = { + type = "tcp" + host = "10.1.1.1" + port = 443 + threshold = 200 + aggressiveness = "med" + } + } + disabled = false + lan_networks = [ + { + networks = ["192.168.1.0/24", "10.0.0.0/8"] + probes = ["google_dns", "web_check"] + } + ] + vlans = [ + { + disabled = false + custom_test_urls = ["https://example.com/test"] + probes = ["google_dns", "tcp_check"] + vlan_ids = ["100", "200"] + } + ] + wan_speedtest { + enabled = true + time_of_day = "02:00" + } + } + + vpn_options { + as_base = 65000 + st_subnet = "10.224.0.0/12" + } + + wan_pma { + enabled = true + } + + wired_pma { + enabled = true + } + + wireless_pma { + enabled = true + } diff --git a/internal/provider/fixtures/org_sso_resource/org_sso_config.tf b/internal/provider/fixtures/org_sso_resource/org_sso_config.tf new file mode 100644 index 00000000..ca76c375 --- /dev/null +++ b/internal/provider/fixtures/org_sso_resource/org_sso_config.tf @@ -0,0 +1,13 @@ + + name = "Example SAML SSO" + idp_cert = "-----BEGIN CERTIFICATE-----\nMIIBkTCB+wIJALJ8UUKmgH1GMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMMCXRl\nc3QtY2VydDAeFw0yNDEyMjcwMDAwMDBaFw0yNTEyMjcwMDAwMDBaMBQxEjAQBgNV\nBAMMCXRlc3QtY2VydDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCxUC6+OeSgM1Fh\nOdKqA5C1XQfFdKK0C8JxUQKHjOKE8Q1j8I+FHFOdKGY5TKZrIvOLMbOeXJGF7Wl5\nxD0dVhZdAgMBAAEwDQYJKoZIhvcNAQELBQADQQA3F8+8MzE5E5GHj5E5TQ==\n-----END CERTIFICATE-----" + idp_sign_algo = "sha256" + idp_sso_url = "https://idp.example.com/sso/saml" + issuer = "https://idp.example.com/issuer" + custom_logout_url = "https://idp.example.com/logout" + default_role = "viewer" + ignore_unmatched_roles = false + nameid_format = "email" + role_attr_extraction = "cn" + role_attr_from = "Role" + diff --git a/internal/provider/fixtures/org_sso_role_resource/org_sso_role_config.tf b/internal/provider/fixtures/org_sso_role_resource/org_sso_role_config.tf new file mode 100644 index 00000000..93c7acfa --- /dev/null +++ b/internal/provider/fixtures/org_sso_role_resource/org_sso_role_config.tf @@ -0,0 +1,25 @@ + + name = "Example SSO Role" + privileges = [{ + role = "admin" + scope = "org" + views = ["reporting", "marketing", "super_observer", "location", "security", "switch_admin", "mxedge_admin", "lobby_admin"] + }, + { + role = "read" + scope = "sitegroup" + }, + { + role = "read" + scope = "site" + }, + { + role = "helpdesk" + scope = "org" + views = ["switch_admin", "marketing"] + }, + { + role = "installer" + scope = "org" + }] + diff --git a/internal/provider/fixtures/org_vpn_resource/org_vpn_config.tf b/internal/provider/fixtures/org_vpn_resource/org_vpn_config.tf new file mode 100644 index 00000000..994013cd --- /dev/null +++ b/internal/provider/fixtures/org_vpn_resource/org_vpn_config.tf @@ -0,0 +1,81 @@ + + name = "test-org-vpn2" + type = "hub_spoke" + paths = { + "vpn_path_1" = { + bfd_profile = "broadband" + bfd_use_tunnel_mode = true + ip = "192.168.1.1" + pod = 1 + peer_paths = { + "peer_interface_1" = { + preference = 100 + } + "peer_interface_2" = { + preference = 200 + } + } + traffic_shaping = { + enabled = true + max_tx_kbps = 10000 + class_percentage = [25, 25, 25, 25] + } + } + + "vpn_path_2" = { + bfd_profile = "lte" + bfd_use_tunnel_mode = false + ip = "192.168.2.1" + pod = 2 + peer_paths = { + "peer_interface_3" = { + preference = 150 + } + } + traffic_shaping = { + enabled = false + max_tx_kbps = 5000 + class_percentage = [30, 30, 20, 20] + } + } + } +␞ + name = "test-org-vpn-mesh2" + type = "mesh" + path_selection = { + strategy = "manual" + } + paths = { + "interface_1" = { + bfd_profile = "broadband" + bfd_use_tunnel_mode = true + ip = "10.0.1.1" + pod = 5 + peer_paths = { + "mesh_peer_1" = { + preference = 100 + } + "mesh_peer_2" = { + preference = 200 + } + } + traffic_shaping = { + enabled = true + max_tx_kbps = 20000 + class_percentage = [40, 30, 20, 10] + } + } + + "interface_2" = { + bfd_profile = "lte" + bfd_use_tunnel_mode = false + ip = "10.0.2.1" + pod = 10 + peer_paths = { + "mesh_peer_3" = { + preference = 300 + } + } + } + } + diff --git a/internal/provider/org_setting_resource_test.go b/internal/provider/org_setting_resource_test.go index f7c09f8f..a47c5ad2 100644 --- a/internal/provider/org_setting_resource_test.go +++ b/internal/provider/org_setting_resource_test.go @@ -2,14 +2,17 @@ package provider import ( "fmt" + "os" + "strings" "testing" + "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/v2/gohcl" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) -func TestOrgSetting(t *testing.T) { +func TestOrgSettingModel(t *testing.T) { type testStep struct { config OrgSettingModel } @@ -30,17 +33,46 @@ func TestOrgSetting(t *testing.T) { }, } + b, err := os.ReadFile("fixtures/org_setting_resource/org_setting_config.tf") + if err != nil { + fmt.Print(err) + } + + str := string(b) // convert content to a 'string' + fixtures := strings.Split(str, "␞") + + for i, fixture := range fixtures { + var FixtureOrgSettingModel OrgSettingModel + err = hcl.Decode(&FixtureOrgSettingModel, fixture) + if err != nil { + fmt.Printf("error decoding hcl: %s\n", err) + } + + FixtureOrgSettingModel.OrgId = GetTestOrgId() + + testCases[fmt.Sprintf("fixture_case_%d", i)] = testCase{ + steps: []testStep{ + { + config: FixtureOrgSettingModel, + }, + }, + } + } + + resourceType := "org_setting" for tName, tCase := range testCases { t.Run(tName, func(t *testing.T) { + steps := make([]resource.TestStep, len(tCase.steps)) for i, step := range tCase.steps { + config := step.config f := hclwrite.NewEmptyFile() gohcl.EncodeIntoBody(&config, f.Body()) - configStr := Render("org_setting", tName, string(f.Bytes())) + configStr := Render(resourceType, tName, string(f.Bytes())) - checks := config.testChecks(t, PrefixProviderName("org_setting"), tName) + checks := config.testChecks(t, PrefixProviderName(resourceType), tName) chkLog := checks.string() stepName := fmt.Sprintf("test case %s step %d", tName, i+1) @@ -67,5 +99,493 @@ func (o *OrgSettingModel) testChecks(t testing.TB, rType, rName string) testChec // Check required fields checks.append(t, "TestCheckResourceAttr", "org_id", o.OrgId) + // Check optional integer fields + if o.ApUpdownThreshold != nil { + checks.append(t, "TestCheckResourceAttr", "ap_updown_threshold", fmt.Sprintf("%d", *o.ApUpdownThreshold)) + } + if o.DeviceUpdownThreshold != nil { + checks.append(t, "TestCheckResourceAttr", "device_updown_threshold", fmt.Sprintf("%d", *o.DeviceUpdownThreshold)) + } + if o.GatewayUpdownThreshold != nil { + checks.append(t, "TestCheckResourceAttr", "gateway_updown_threshold", fmt.Sprintf("%d", *o.GatewayUpdownThreshold)) + } + if o.SwitchUpdownThreshold != nil { + checks.append(t, "TestCheckResourceAttr", "switch_updown_threshold", fmt.Sprintf("%d", *o.SwitchUpdownThreshold)) + } + if o.UiIdleTimeout != nil { + checks.append(t, "TestCheckResourceAttr", "ui_idle_timeout", fmt.Sprintf("%d", *o.UiIdleTimeout)) + } + + // Check boolean fields + if o.DisablePcap != nil { + checks.append(t, "TestCheckResourceAttr", "disable_pcap", fmt.Sprintf("%t", *o.DisablePcap)) + } + if o.DisableRemoteShell != nil { + checks.append(t, "TestCheckResourceAttr", "disable_remote_shell", fmt.Sprintf("%t", *o.DisableRemoteShell)) + } + if o.UiNoTracking != nil { + checks.append(t, "TestCheckResourceAttr", "ui_no_tracking", fmt.Sprintf("%t", *o.UiNoTracking)) + } + + // Check array fields + if len(o.Cacerts) > 0 { + checks.append(t, "TestCheckResourceAttr", "cacerts.#", fmt.Sprintf("%d", len(o.Cacerts))) + for i, cert := range o.Cacerts { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("cacerts.%d", i), cert) + } + } + + // Check API Policy nested object + if o.ApiPolicy != nil { + if o.ApiPolicy.NoReveal != nil { + checks.append(t, "TestCheckResourceAttr", "api_policy.no_reveal", fmt.Sprintf("%t", *o.ApiPolicy.NoReveal)) + } + } + + // Check Celona nested object + if o.Celona != nil { + checks.append(t, "TestCheckResourceAttr", "celona.api_key", o.Celona.ApiKey) + checks.append(t, "TestCheckResourceAttr", "celona.api_prefix", o.Celona.ApiPrefix) + } + + // Check Cloudshark nested object + if o.Cloudshark != nil { + if o.Cloudshark.Apitoken != nil { + checks.append(t, "TestCheckResourceAttr", "cloudshark.apitoken", *o.Cloudshark.Apitoken) + } + if o.Cloudshark.Url != nil { + checks.append(t, "TestCheckResourceAttr", "cloudshark.url", *o.Cloudshark.Url) + } + } + + // Check Device Cert nested object + if o.DeviceCert != nil { + checks.append(t, "TestCheckResourceAttr", "device_cert.cert", o.DeviceCert.Cert) + checks.append(t, "TestCheckResourceAttr", "device_cert.key", o.DeviceCert.Key) + } + + // Check Installer nested object + if o.Installer != nil { + if o.Installer.AllowAllDevices != nil { + checks.append(t, "TestCheckResourceAttr", "installer.allow_all_devices", fmt.Sprintf("%t", *o.Installer.AllowAllDevices)) + } + if o.Installer.AllowAllSites != nil { + checks.append(t, "TestCheckResourceAttr", "installer.allow_all_sites", fmt.Sprintf("%t", *o.Installer.AllowAllSites)) + } + if len(o.Installer.ExtraSiteIds) > 0 { + checks.append(t, "TestCheckResourceAttr", "installer.extra_site_ids.#", fmt.Sprintf("%d", len(o.Installer.ExtraSiteIds))) + for i, siteId := range o.Installer.ExtraSiteIds { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("installer.extra_site_ids.%d", i), siteId) + } + } + if o.Installer.GracePeriod != nil { + checks.append(t, "TestCheckResourceAttr", "installer.grace_period", fmt.Sprintf("%d", *o.Installer.GracePeriod)) + } + } + + // Check Jcloud nested object + if o.Jcloud != nil { + checks.append(t, "TestCheckResourceAttr", "jcloud.org_apitoken", o.Jcloud.OrgApitoken) + checks.append(t, "TestCheckResourceAttr", "jcloud.org_apitoken_name", o.Jcloud.OrgApitokenName) + checks.append(t, "TestCheckResourceAttr", "jcloud.org_id", o.Jcloud.OrgId) + } + + // Check JcloudRa nested object + if o.JcloudRa != nil { + if o.JcloudRa.OrgApitoken != nil { + checks.append(t, "TestCheckResourceAttr", "jcloud_ra.org_apitoken", *o.JcloudRa.OrgApitoken) + } + if o.JcloudRa.OrgApitokenName != nil { + checks.append(t, "TestCheckResourceAttr", "jcloud_ra.org_apitoken_name", *o.JcloudRa.OrgApitokenName) + } + if o.JcloudRa.OrgId != nil { + checks.append(t, "TestCheckResourceAttr", "jcloud_ra.org_id", *o.JcloudRa.OrgId) + } + } + + // Check Management nested object + if o.Mgmt != nil { + if len(o.Mgmt.MxtunnelIds) > 0 { + checks.append(t, "TestCheckResourceAttr", "mgmt.mxtunnel_ids.#", fmt.Sprintf("%d", len(o.Mgmt.MxtunnelIds))) + for i, tunnelId := range o.Mgmt.MxtunnelIds { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("mgmt.mxtunnel_ids.%d", i), tunnelId) + } + } + if o.Mgmt.UseMxtunnel != nil { + checks.append(t, "TestCheckResourceAttr", "mgmt.use_mxtunnel", fmt.Sprintf("%t", *o.Mgmt.UseMxtunnel)) + } + if o.Mgmt.UseWxtunnel != nil { + checks.append(t, "TestCheckResourceAttr", "mgmt.use_wxtunnel", fmt.Sprintf("%t", *o.Mgmt.UseWxtunnel)) + } + } + + // Check MistNac nested object - this is a complex nested structure + if o.MistNac != nil { + if len(o.MistNac.Cacerts) > 0 { + checks.append(t, "TestCheckResourceAttr", "mist_nac.cacerts.#", fmt.Sprintf("%d", len(o.MistNac.Cacerts))) + for i, cert := range o.MistNac.Cacerts { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("mist_nac.cacerts.%d", i), cert) + } + } + if o.MistNac.DefaultIdpId != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.default_idp_id", *o.MistNac.DefaultIdpId) + } + if o.MistNac.DisableRsaeAlgorithms != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.disable_rsae_algorithms", fmt.Sprintf("%t", *o.MistNac.DisableRsaeAlgorithms)) + } + if o.MistNac.EapSslSecurityLevel != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.eap_ssl_security_level", fmt.Sprintf("%d", *o.MistNac.EapSslSecurityLevel)) + } + if o.MistNac.EuOnly != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.eu_only", fmt.Sprintf("%t", *o.MistNac.EuOnly)) + } + if o.MistNac.IdpMachineCertLookupField != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.idp_machine_cert_lookup_field", *o.MistNac.IdpMachineCertLookupField) + } + if o.MistNac.IdpUserCertLookupField != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.idp_user_cert_lookup_field", *o.MistNac.IdpUserCertLookupField) + } + if len(o.MistNac.Idps) > 0 { + checks.append(t, "TestCheckResourceAttr", "mist_nac.idps.#", fmt.Sprintf("%d", len(o.MistNac.Idps))) + for i, idp := range o.MistNac.Idps { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("mist_nac.idps.%d.id", i), idp.Id) + if len(idp.ExcludeRealms) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("mist_nac.idps.%d.exclude_realms.#", i), fmt.Sprintf("%d", len(idp.ExcludeRealms))) + for j, realm := range idp.ExcludeRealms { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("mist_nac.idps.%d.exclude_realms.%d", i, j), realm) + } + } + if len(idp.UserRealms) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("mist_nac.idps.%d.user_realms.#", i), fmt.Sprintf("%d", len(idp.UserRealms))) + for j, realm := range idp.UserRealms { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("mist_nac.idps.%d.user_realms.%d", i, j), realm) + } + } + } + } + if o.MistNac.ServerCert != nil { + if o.MistNac.ServerCert.Cert != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.server_cert.cert", *o.MistNac.ServerCert.Cert) + } + if o.MistNac.ServerCert.Key != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.server_cert.key", *o.MistNac.ServerCert.Key) + } + if o.MistNac.ServerCert.Password != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.server_cert.password", *o.MistNac.ServerCert.Password) + } + } + if o.MistNac.UseIpVersion != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.use_ip_version", *o.MistNac.UseIpVersion) + } + if o.MistNac.UseSslPort != nil { + checks.append(t, "TestCheckResourceAttr", "mist_nac.use_ssl_port", fmt.Sprintf("%t", *o.MistNac.UseSslPort)) + } + } + + // Check MxedgeMgmt nested object + if o.MxedgeMgmt != nil { + if o.MxedgeMgmt.ConfigAutoRevert != nil { + checks.append(t, "TestCheckResourceAttr", "mxedge_mgmt.config_auto_revert", fmt.Sprintf("%t", *o.MxedgeMgmt.ConfigAutoRevert)) + } + if o.MxedgeMgmt.FipsEnabled != nil { + checks.append(t, "TestCheckResourceAttr", "mxedge_mgmt.fips_enabled", fmt.Sprintf("%t", *o.MxedgeMgmt.FipsEnabled)) + } + if o.MxedgeMgmt.MistPassword != nil { + checks.append(t, "TestCheckResourceAttr", "mxedge_mgmt.mist_password", *o.MxedgeMgmt.MistPassword) + } + if o.MxedgeMgmt.OobIpType != nil { + checks.append(t, "TestCheckResourceAttr", "mxedge_mgmt.oob_ip_type", *o.MxedgeMgmt.OobIpType) + } + if o.MxedgeMgmt.OobIpType6 != nil { + checks.append(t, "TestCheckResourceAttr", "mxedge_mgmt.oob_ip_type6", *o.MxedgeMgmt.OobIpType6) + } + if o.MxedgeMgmt.RootPassword != nil { + checks.append(t, "TestCheckResourceAttr", "mxedge_mgmt.root_password", *o.MxedgeMgmt.RootPassword) + } + } + + // Check Marvis nested object + if o.Marvis != nil { + if o.Marvis.AutoOperations != nil { + if o.Marvis.AutoOperations.BouncePortForAbnormalPoeClient != nil { + checks.append(t, "TestCheckResourceAttr", "marvis.auto_operations.bounce_port_for_abnormal_poe_client", fmt.Sprintf("%t", *o.Marvis.AutoOperations.BouncePortForAbnormalPoeClient)) + } + if o.Marvis.AutoOperations.DisablePortWhenDdosProtocolViolation != nil { + checks.append(t, "TestCheckResourceAttr", "marvis.auto_operations.disable_port_when_ddos_protocol_violation", fmt.Sprintf("%t", *o.Marvis.AutoOperations.DisablePortWhenDdosProtocolViolation)) + } + if o.Marvis.AutoOperations.DisablePortWhenRogueDhcpServerDetected != nil { + checks.append(t, "TestCheckResourceAttr", "marvis.auto_operations.disable_port_when_rogue_dhcp_server_detected", fmt.Sprintf("%t", *o.Marvis.AutoOperations.DisablePortWhenRogueDhcpServerDetected)) + } + } + } + + // Check JuniperSrx nested object + if o.JuniperSrx != nil { + if o.JuniperSrx.SrxAutoUpgrade != nil { + if o.JuniperSrx.SrxAutoUpgrade.Enabled != nil { + checks.append(t, "TestCheckResourceAttr", "juniper_srx.auto_upgrade.enabled", fmt.Sprintf("%t", *o.JuniperSrx.SrxAutoUpgrade.Enabled)) + } + if o.JuniperSrx.SrxAutoUpgrade.Snapshot != nil { + checks.append(t, "TestCheckResourceAttr", "juniper_srx.auto_upgrade.snapshot", fmt.Sprintf("%t", *o.JuniperSrx.SrxAutoUpgrade.Snapshot)) + } + if len(o.JuniperSrx.SrxAutoUpgrade.CustomVersions) > 0 { + checks.append(t, "TestCheckResourceAttr", "juniper_srx.auto_upgrade.custom_versions.%", fmt.Sprintf("%d", len(o.JuniperSrx.SrxAutoUpgrade.CustomVersions))) + for key, version := range o.JuniperSrx.SrxAutoUpgrade.CustomVersions { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("juniper_srx.auto_upgrade.custom_versions.%s", key), version) + } + } + } + } + + // Check JunosShellAccess nested object + if o.JunosShellAccess != nil { + if o.JunosShellAccess.Admin != nil { + checks.append(t, "TestCheckResourceAttr", "junos_shell_access.admin", *o.JunosShellAccess.Admin) + } + if o.JunosShellAccess.Helpdesk != nil { + checks.append(t, "TestCheckResourceAttr", "junos_shell_access.helpdesk", *o.JunosShellAccess.Helpdesk) + } + if o.JunosShellAccess.Read != nil { + checks.append(t, "TestCheckResourceAttr", "junos_shell_access.read", *o.JunosShellAccess.Read) + } + if o.JunosShellAccess.Write != nil { + checks.append(t, "TestCheckResourceAttr", "junos_shell_access.write", *o.JunosShellAccess.Write) + } + } + + // Check Ssr nested object + if o.Ssr != nil { + if len(o.Ssr.ConductorHosts) > 0 { + checks.append(t, "TestCheckResourceAttr", "ssr.conductor_hosts.#", fmt.Sprintf("%d", len(o.Ssr.ConductorHosts))) + for i, host := range o.Ssr.ConductorHosts { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("ssr.conductor_hosts.%d", i), host) + } + } + if o.Ssr.ConductorToken != nil { + checks.append(t, "TestCheckResourceAttr", "ssr.conductor_token", *o.Ssr.ConductorToken) + } + if o.Ssr.DisableStats != nil { + checks.append(t, "TestCheckResourceAttr", "ssr.disable_stats", fmt.Sprintf("%t", *o.Ssr.DisableStats)) + } + if o.Ssr.Proxy != nil { + if o.Ssr.Proxy.Url != nil { + checks.append(t, "TestCheckResourceAttr", "ssr.proxy.url", *o.Ssr.Proxy.Url) + } + } + if o.Ssr.SsrAutoUpgrade != nil { + if o.Ssr.SsrAutoUpgrade.Enabled != nil { + checks.append(t, "TestCheckResourceAttr", "ssr.auto_upgrade.enabled", fmt.Sprintf("%t", *o.Ssr.SsrAutoUpgrade.Enabled)) + } + if o.Ssr.SsrAutoUpgrade.Channel != nil { + checks.append(t, "TestCheckResourceAttr", "ssr.auto_upgrade.channel", *o.Ssr.SsrAutoUpgrade.Channel) + } + if len(o.Ssr.SsrAutoUpgrade.CustomVersions) > 0 { + checks.append(t, "TestCheckResourceAttr", "ssr.auto_upgrade.custom_versions.%", fmt.Sprintf("%d", len(o.Ssr.SsrAutoUpgrade.CustomVersions))) + for key, version := range o.Ssr.SsrAutoUpgrade.CustomVersions { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("ssr.auto_upgrade.custom_versions.%s", key), version) + } + } + } + } + + // Check Switch nested object + if o.Switch != nil { + if o.Switch.AutoUpgrade != nil { + if o.Switch.AutoUpgrade.Enabled != nil { + checks.append(t, "TestCheckResourceAttr", "switch.auto_upgrade.enabled", fmt.Sprintf("%t", *o.Switch.AutoUpgrade.Enabled)) + } + if o.Switch.AutoUpgrade.Snapshot != nil { + checks.append(t, "TestCheckResourceAttr", "switch.auto_upgrade.snapshot", fmt.Sprintf("%t", *o.Switch.AutoUpgrade.Snapshot)) + } + if len(o.Switch.AutoUpgrade.CustomVersions) > 0 { + checks.append(t, "TestCheckResourceAttr", "switch.auto_upgrade.custom_versions.%", fmt.Sprintf("%d", len(o.Switch.AutoUpgrade.CustomVersions))) + for key, version := range o.Switch.AutoUpgrade.CustomVersions { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("switch.auto_upgrade.custom_versions.%s", key), version) + } + } + } + } + + // Check Password Policy nested object + if o.PasswordPolicy != nil { + if o.PasswordPolicy.Enabled != nil { + checks.append(t, "TestCheckResourceAttr", "password_policy.enabled", fmt.Sprintf("%t", *o.PasswordPolicy.Enabled)) + } + if o.PasswordPolicy.ExpiryInDays != nil { + checks.append(t, "TestCheckResourceAttr", "password_policy.expiry_in_days", fmt.Sprintf("%d", *o.PasswordPolicy.ExpiryInDays)) + } + if o.PasswordPolicy.MinLength != nil { + checks.append(t, "TestCheckResourceAttr", "password_policy.min_length", fmt.Sprintf("%d", *o.PasswordPolicy.MinLength)) + } + if o.PasswordPolicy.RequiresSpecialChar != nil { + checks.append(t, "TestCheckResourceAttr", "password_policy.requires_special_char", fmt.Sprintf("%t", *o.PasswordPolicy.RequiresSpecialChar)) + } + if o.PasswordPolicy.RequiresTwoFactorAuth != nil { + checks.append(t, "TestCheckResourceAttr", "password_policy.requires_two_factor_auth", fmt.Sprintf("%t", *o.PasswordPolicy.RequiresTwoFactorAuth)) + } + } + + // Check Pcap nested object + if o.Pcap != nil { + if o.Pcap.Bucket != nil { + checks.append(t, "TestCheckResourceAttr", "pcap.bucket", *o.Pcap.Bucket) + } + if o.Pcap.MaxPktLen != nil { + checks.append(t, "TestCheckResourceAttr", "pcap.max_pkt_len", fmt.Sprintf("%d", *o.Pcap.MaxPktLen)) + } + } + + // Check Security nested object + if o.Security != nil { + if o.Security.DisableLocalSsh != nil { + checks.append(t, "TestCheckResourceAttr", "security.disable_local_ssh", fmt.Sprintf("%t", *o.Security.DisableLocalSsh)) + } + if o.Security.FipsZeroizePassword != nil { + checks.append(t, "TestCheckResourceAttr", "security.fips_zeroize_password", *o.Security.FipsZeroizePassword) + } + if o.Security.LimitSshAccess != nil { + checks.append(t, "TestCheckResourceAttr", "security.limit_ssh_access", fmt.Sprintf("%t", *o.Security.LimitSshAccess)) + } + } + + // Check SwitchMgmt nested object + if o.SwitchMgmt != nil { + if o.SwitchMgmt.ApAffinityThreshold != nil { + checks.append(t, "TestCheckResourceAttr", "switch_mgmt.ap_affinity_threshold", fmt.Sprintf("%d", *o.SwitchMgmt.ApAffinityThreshold)) + } + } + + // Check SyntheticTest nested object + if o.SyntheticTest != nil { + if o.SyntheticTest.Aggressiveness != nil { + checks.append(t, "TestCheckResourceAttr", "synthetic_test.aggressiveness", *o.SyntheticTest.Aggressiveness) + } + if o.SyntheticTest.Disabled != nil { + checks.append(t, "TestCheckResourceAttr", "synthetic_test.disabled", fmt.Sprintf("%t", *o.SyntheticTest.Disabled)) + } + + // Check CustomProbes map + if len(o.SyntheticTest.CustomProbes) > 0 { + checks.append(t, "TestCheckResourceAttr", "synthetic_test.custom_probes.%", fmt.Sprintf("%d", len(o.SyntheticTest.CustomProbes))) + for key, probe := range o.SyntheticTest.CustomProbes { + if probe.Aggressiveness != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.custom_probes.%s.aggressiveness", key), *probe.Aggressiveness) + } + if probe.Host != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.custom_probes.%s.host", key), *probe.Host) + } + if probe.Port != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.custom_probes.%s.port", key), fmt.Sprintf("%d", *probe.Port)) + } + if probe.Threshold != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.custom_probes.%s.threshold", key), fmt.Sprintf("%d", *probe.Threshold)) + } + if probe.CustomProbesType != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.custom_probes.%s.type", key), *probe.CustomProbesType) + } + if probe.Url != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.custom_probes.%s.url", key), *probe.Url) + } + } + } + + // Check LanNetworks array + if len(o.SyntheticTest.LanNetworks) > 0 { + checks.append(t, "TestCheckResourceAttr", "synthetic_test.lan_networks.#", fmt.Sprintf("%d", len(o.SyntheticTest.LanNetworks))) + for i, lanNetwork := range o.SyntheticTest.LanNetworks { + if len(lanNetwork.Networks) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.lan_networks.%d.networks.#", i), fmt.Sprintf("%d", len(lanNetwork.Networks))) + for j, network := range lanNetwork.Networks { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.lan_networks.%d.networks.%d", i, j), network) + } + } + if len(lanNetwork.Probes) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.lan_networks.%d.probes.#", i), fmt.Sprintf("%d", len(lanNetwork.Probes))) + for j, probe := range lanNetwork.Probes { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.lan_networks.%d.probes.%d", i, j), probe) + } + } + } + } + + if len(o.SyntheticTest.Vlans) > 0 { + checks.append(t, "TestCheckResourceAttr", "synthetic_test.vlans.#", fmt.Sprintf("%d", len(o.SyntheticTest.Vlans))) + for i, vlan := range o.SyntheticTest.Vlans { + if vlan.Disabled != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.vlans.%d.disabled", i), fmt.Sprintf("%t", *vlan.Disabled)) + } + if len(vlan.CustomTestUrls) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.vlans.%d.custom_test_urls.#", i), fmt.Sprintf("%d", len(vlan.CustomTestUrls))) + for j, url := range vlan.CustomTestUrls { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.vlans.%d.custom_test_urls.%d", i, j), url) + } + } + if len(vlan.Probes) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.vlans.%d.probes.#", i), fmt.Sprintf("%d", len(vlan.Probes))) + for j, probe := range vlan.Probes { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.vlans.%d.probes.%d", i, j), probe) + } + } + if len(vlan.VlanIds) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.vlans.%d.vlan_ids.#", i), fmt.Sprintf("%d", len(vlan.VlanIds))) + for j, vlanId := range vlan.VlanIds { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("synthetic_test.vlans.%d.vlan_ids.%d", i, j), vlanId) + } + } + } + } + if o.SyntheticTest.WanSpeedtest != nil { + if o.SyntheticTest.WanSpeedtest.Enabled != nil { + checks.append(t, "TestCheckResourceAttr", "synthetic_test.wan_speedtest.enabled", fmt.Sprintf("%t", *o.SyntheticTest.WanSpeedtest.Enabled)) + } + if o.SyntheticTest.WanSpeedtest.TimeOfDay != nil { + checks.append(t, "TestCheckResourceAttr", "synthetic_test.wan_speedtest.time_of_day", *o.SyntheticTest.WanSpeedtest.TimeOfDay) + } + } + } + + // Check VpnOptions nested object + if o.VpnOptions != nil { + if o.VpnOptions.AsBase != nil { + checks.append(t, "TestCheckResourceAttr", "vpn_options.as_base", fmt.Sprintf("%d", *o.VpnOptions.AsBase)) + } + if o.VpnOptions.EnableIpv6 != nil { + checks.append(t, "TestCheckResourceAttr", "vpn_options.enable_ipv6", fmt.Sprintf("%t", *o.VpnOptions.EnableIpv6)) + } + if o.VpnOptions.StSubnet != nil { + checks.append(t, "TestCheckResourceAttr", "vpn_options.st_subnet", *o.VpnOptions.StSubnet) + } + } + + // Check PMA nested objects + if o.WanPma != nil { + if o.WanPma.Enabled != nil { + checks.append(t, "TestCheckResourceAttr", "wan_pma.enabled", fmt.Sprintf("%t", *o.WanPma.Enabled)) + } + } + if o.WiredPma != nil { + if o.WiredPma.Enabled != nil { + checks.append(t, "TestCheckResourceAttr", "wired_pma.enabled", fmt.Sprintf("%t", *o.WiredPma.Enabled)) + } + } + if o.WirelessPma != nil { + if o.WirelessPma.Enabled != nil { + checks.append(t, "TestCheckResourceAttr", "wireless_pma.enabled", fmt.Sprintf("%t", *o.WirelessPma.Enabled)) + } + } + + // Check map fields - OpticPortConfig + if len(o.OpticPortConfig) > 0 { + checks.append(t, "TestCheckResourceAttr", "optic_port_config.%", fmt.Sprintf("%d", len(o.OpticPortConfig))) + for key, config := range o.OpticPortConfig { + if config.Channelized != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("optic_port_config.%s.channelized", key), fmt.Sprintf("%t", *config.Channelized)) + } + if config.Speed != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("optic_port_config.%s.speed", key), *config.Speed) + } + } + } + return checks } diff --git a/internal/provider/org_sitegroup_resource_test.go b/internal/provider/org_sitegroup_resource_test.go index d6a7925c..a8ce19e7 100644 --- a/internal/provider/org_sitegroup_resource_test.go +++ b/internal/provider/org_sitegroup_resource_test.go @@ -31,6 +31,7 @@ func TestOrgSitegroup(t *testing.T) { }, } + resourceType := "org_sitegroup" for tName, tCase := range testCases { t.Run(tName, func(t *testing.T) { steps := make([]resource.TestStep, len(tCase.steps)) @@ -39,9 +40,9 @@ func TestOrgSitegroup(t *testing.T) { f := hclwrite.NewEmptyFile() gohcl.EncodeIntoBody(&config, f.Body()) - configStr := Render("org_sitegroup", tName, string(f.Bytes())) + configStr := Render(resourceType, tName, string(f.Bytes())) - checks := config.testChecks(t, PrefixProviderName("org_sitegroup"), tName) + checks := config.testChecks(t, PrefixProviderName(resourceType), tName) chkLog := checks.string() stepName := fmt.Sprintf("test case %s step %d", tName, i+1) diff --git a/internal/provider/org_sso_resource_test.go b/internal/provider/org_sso_resource_test.go index 999657e6..a37d9bf4 100644 --- a/internal/provider/org_sso_resource_test.go +++ b/internal/provider/org_sso_resource_test.go @@ -2,14 +2,17 @@ package provider import ( "fmt" + "os" + "strings" "testing" + "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/v2/gohcl" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) -func TestOrgSso(t *testing.T) { +func TestOrgSsoModel(t *testing.T) { type testStep struct { config OrgSsoModel } @@ -35,18 +38,44 @@ func TestOrgSso(t *testing.T) { }, } + b, err := os.ReadFile("fixtures/org_sso_resource/org_sso_config.tf") + if err != nil { + fmt.Print(err) + } + + str := string(b) // convert content to a 'string' + fixtures := strings.Split(str, "␞") + + for i, fixture := range fixtures { + var FixtureOrgSsoModel OrgSsoModel + err = hcl.Decode(&FixtureOrgSsoModel, fixture) + if err != nil { + fmt.Printf("error decoding hcl: %s\n", err) + } + + FixtureOrgSsoModel.OrgId = GetTestOrgId() + + testCases[fmt.Sprintf("fixture_casepaskdvn_%d", i)] = testCase{ + steps: []testStep{ + { + config: FixtureOrgSsoModel, + }, + }, + } + } + + resourceType := "org_sso" for tName, tCase := range testCases { t.Run(tName, func(t *testing.T) { - steps := make([]resource.TestStep, len(tCase.steps)) for i, step := range tCase.steps { config := step.config f := hclwrite.NewEmptyFile() gohcl.EncodeIntoBody(&config, f.Body()) - configStr := Render("org_sso", tName, string(f.Bytes())) + configStr := Render(resourceType, tName, string(f.Bytes())) - checks := config.testChecks(t, PrefixProviderName("org_sso"), tName) + checks := config.testChecks(t, PrefixProviderName(resourceType), tName) chkLog := checks.string() stepName := fmt.Sprintf("test case %s step %d", tName, i+1) @@ -78,5 +107,31 @@ func (o *OrgSsoModel) testChecks(t testing.TB, rType, rName string) testChecks { checks.append(t, "TestCheckResourceAttr", "idp_sso_url", o.IdpSsoUrl) checks.append(t, "TestCheckResourceAttr", "issuer", o.Issuer) + // Check computed-only fields (verify they exist) + checks.append(t, "TestCheckResourceAttrSet", "id") + checks.append(t, "TestCheckResourceAttrSet", "domain") + + // Check optional string fields + if o.CustomLogoutUrl != nil { + checks.append(t, "TestCheckResourceAttr", "custom_logout_url", *o.CustomLogoutUrl) + } + if o.DefaultRole != nil { + checks.append(t, "TestCheckResourceAttr", "default_role", *o.DefaultRole) + } + if o.NameidFormat != nil { + checks.append(t, "TestCheckResourceAttr", "nameid_format", *o.NameidFormat) + } + if o.RoleAttrExtraction != nil { + checks.append(t, "TestCheckResourceAttr", "role_attr_extraction", *o.RoleAttrExtraction) + } + if o.RoleAttrFrom != nil { + checks.append(t, "TestCheckResourceAttr", "role_attr_from", *o.RoleAttrFrom) + } + + // Check optional boolean fields + if o.IgnoreUnmatchedRoles != nil { + checks.append(t, "TestCheckResourceAttr", "ignore_unmatched_roles", fmt.Sprintf("%t", *o.IgnoreUnmatchedRoles)) + } + return checks } diff --git a/internal/provider/org_sso_role_resource_test.go b/internal/provider/org_sso_role_resource_test.go index 17d1ae5e..4c0f2745 100644 --- a/internal/provider/org_sso_role_resource_test.go +++ b/internal/provider/org_sso_role_resource_test.go @@ -2,8 +2,11 @@ package provider import ( "fmt" + "os" + "strings" "testing" + "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/v2/gohcl" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -25,7 +28,7 @@ func TestOrgSsoRole(t *testing.T) { config: OrgSsoRoleModel{ OrgId: GetTestOrgId(), Name: "test-sso-role", - Privileges: []PrivilegesValue{ + Privileges: []OrgSsoRolePrivilegesValue{ { Role: "read", Scope: "org", @@ -37,25 +40,79 @@ func TestOrgSsoRole(t *testing.T) { }, } + b, err := os.ReadFile("fixtures/org_sso_role_resource/org_sso_role_config.tf") + if err != nil { + fmt.Print(err) + } + + str := string(b) // convert content to a 'string' + fixtures := strings.Split(str, "␞") + + for i, fixture := range fixtures { + var FixtureOrgSsoRoleModel OrgSsoRoleModel + err = hcl.Decode(&FixtureOrgSsoRoleModel, fixture) + if err != nil { + fmt.Printf("error decoding hcl: %s\n", err) + } + + FixtureOrgSsoRoleModel.OrgId = GetTestOrgId() + + testCases[fmt.Sprintf("fixture_case_%d", i)] = testCase{ + steps: []testStep{ + { + config: FixtureOrgSsoRoleModel, + }, + }, + } + } + + resourceType := "org_sso_role" for tName, tCase := range testCases { t.Run(tName, func(t *testing.T) { steps := make([]resource.TestStep, len(tCase.steps)) for i, step := range tCase.steps { config := step.config + siteConfig, sitegroupConfig, siteRef, sitegroupRef := "", "", "", "" + + for i, p := range config.Privileges { + switch p.Scope { + case "site": + if siteConfig == "" { // only get once even if multiple privileges use it + siteConfig, siteRef = GetSiteBaseConfig(GetTestOrgId()) + config.Privileges[i].SiteId = stringPtr("{site_id}") + } + case "sitegroup": + if sitegroupConfig == "" { + sitegroupConfig, sitegroupRef = GetSitegroupBaseConfig(GetTestOrgId()) + config.Privileges[i].SitegroupId = stringPtr("{sitegroup_id}") + } + } + } f := hclwrite.NewEmptyFile() gohcl.EncodeIntoBody(&config, f.Body()) - configStr := Render("org_sso_role", tName, string(f.Bytes())) + combinedConfig := Render(resourceType, tName, string(f.Bytes())) + configStr := "" + if siteConfig != "" { + combinedConfig = strings.ReplaceAll(combinedConfig, "\"{site_id}\"", siteRef) + configStr = siteConfig + "\n\n" + configStr + } + if sitegroupConfig != "" { + combinedConfig = strings.ReplaceAll(combinedConfig, "\"{sitegroup_id}\"", sitegroupRef) + configStr = sitegroupConfig + "\n\n" + configStr + } - checks := config.testChecks(t, PrefixProviderName("org_sso_role"), tName) + combinedConfig = configStr + combinedConfig + + checks := config.testChecks(t, PrefixProviderName(resourceType), tName) chkLog := checks.string() stepName := fmt.Sprintf("test case %s step %d", tName, i+1) - t.Logf("\n// ------ begin config for %s ------\n%s// -------- end config for %s ------\n\n", stepName, configStr, stepName) + t.Logf("\n// ------ begin config for %s ------\n%s// -------- end config for %s ------\n\n", stepName, combinedConfig, stepName) t.Logf("\n// ------ begin checks for %s ------\n%s// -------- end config for %s ------\n\n", stepName, chkLog, stepName) steps[i] = resource.TestStep{ - Config: configStr, + Config: combinedConfig, Check: resource.ComposeAggregateTestCheckFunc(checks.checks...), } } @@ -75,11 +132,34 @@ func (o *OrgSsoRoleModel) testChecks(t testing.TB, rType, rName string) testChec checks.append(t, "TestCheckResourceAttr", "org_id", o.OrgId) checks.append(t, "TestCheckResourceAttr", "name", o.Name) - // Check privileges + // Check computed-only fields (verify they exist) + checks.append(t, "TestCheckResourceAttrSet", "id") + + // Check privileges array if len(o.Privileges) > 0 { - // Check the first privilege entry - checks.append(t, "TestCheckResourceAttr", "privileges.0.role", o.Privileges[0].Role) - checks.append(t, "TestCheckResourceAttr", "privileges.0.scope", o.Privileges[0].Scope) + checks.append(t, "TestCheckResourceAttr", "privileges.#", fmt.Sprintf("%d", len(o.Privileges))) + + // Check all privilege entries + for i, privilege := range o.Privileges { + // Check required fields in each privilege + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("privileges.%d.role", i), privilege.Role) + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("privileges.%d.scope", i), privilege.Scope) + + // Check optional fields in each privilege + if privilege.SiteId != nil { + checks.append(t, "TestCheckResourceAttrSet", fmt.Sprintf("privileges.%d.site_id", i)) + } + if privilege.SitegroupId != nil { + checks.append(t, "TestCheckResourceAttrSet", fmt.Sprintf("privileges.%d.sitegroup_id", i)) + } + + if len(privilege.Views) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("privileges.%d.views.#", i), fmt.Sprintf("%d", len(privilege.Views))) + for j, view := range privilege.Views { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("privileges.%d.views.%d", i, j), view) + } + } + } } return checks diff --git a/internal/provider/org_sso_role_test_structs.go b/internal/provider/org_sso_role_test_structs.go index 5d49f7ac..3a5bccf6 100644 --- a/internal/provider/org_sso_role_test_structs.go +++ b/internal/provider/org_sso_role_test_structs.go @@ -1,15 +1,15 @@ package provider type OrgSsoRoleModel struct { - Name string `hcl:"name"` - OrgId string `hcl:"org_id"` - Privileges []PrivilegesValue `hcl:"privileges"` + Name string `hcl:"name"` + OrgId string `hcl:"org_id"` + Privileges []OrgSsoRolePrivilegesValue `hcl:"privileges"` } -// type PrivilegesValue struct { -// Role string `cty:"role"` -// Scope string `cty:"scope"` -// SiteId *string `cty:"site_id"` -// SitegroupId *string `cty:"sitegroup_id"` -// Views []string `cty:"views"` -// } +type OrgSsoRolePrivilegesValue struct { + Role string `cty:"role" hcl:"role"` + Scope string `cty:"scope" hcl:"scope"` + SiteId *string `cty:"site_id" hcl:"site_id"` + SitegroupId *string `cty:"sitegroup_id" hcl:"sitegroup_id"` + Views []string `cty:"views" hcl:"views"` +} diff --git a/internal/provider/org_vpn_resource_test.go b/internal/provider/org_vpn_resource_test.go index 8762df56..b22e3995 100644 --- a/internal/provider/org_vpn_resource_test.go +++ b/internal/provider/org_vpn_resource_test.go @@ -2,14 +2,17 @@ package provider import ( "fmt" + "os" + "strings" "testing" + "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/v2/gohcl" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) -func TestOrgVpn(t *testing.T) { +func TestOrgVpnModel(t *testing.T) { type testStep struct { config OrgVpnModel } @@ -34,6 +37,33 @@ func TestOrgVpn(t *testing.T) { }, } + b, err := os.ReadFile("fixtures/org_vpn_resource/org_vpn_config.tf") + if err != nil { + fmt.Print(err) + } + + str := string(b) // convert content to a 'string' + fixtures := strings.Split(str, "␞") + + for i, fixture := range fixtures { + var FixtureOrgVpnModel OrgVpnModel + err = hcl.Decode(&FixtureOrgVpnModel, fixture) + if err != nil { + fmt.Printf("error decoding hcl: %s\n", err) + } + + FixtureOrgVpnModel.OrgId = stringPtr(GetTestOrgId()) + + testCases[fmt.Sprintf("fixture_case_%d", i)] = testCase{ + steps: []testStep{ + { + config: FixtureOrgVpnModel, + }, + }, + } + } + + resourceType := "org_vpn" for tName, tCase := range testCases { t.Run(tName, func(t *testing.T) { steps := make([]resource.TestStep, len(tCase.steps)) @@ -42,9 +72,9 @@ func TestOrgVpn(t *testing.T) { f := hclwrite.NewEmptyFile() gohcl.EncodeIntoBody(&config, f.Body()) - configStr := Render("org_vpn", tName, string(f.Bytes())) + configStr := Render(resourceType, tName, string(f.Bytes())) - checks := config.testChecks(t, PrefixProviderName("org_vpn"), tName) + checks := config.testChecks(t, PrefixProviderName(resourceType), tName) chkLog := checks.string() stepName := fmt.Sprintf("test case %s step %d", tName, i+1) @@ -71,5 +101,73 @@ func (o *OrgVpnModel) testChecks(t testing.TB, rType, rName string) testChecks { // Check required fields checks.append(t, "TestCheckResourceAttr", "name", o.Name) + // Check computed-only fields (verify they exist) + checks.append(t, "TestCheckResourceAttrSet", "id") + + // Check optional string fields + if o.OrgId != nil { + checks.append(t, "TestCheckResourceAttr", "org_id", *o.OrgId) + } + if o.Type != nil { + checks.append(t, "TestCheckResourceAttr", "type", *o.Type) + } + + // Check PathSelection nested object + if o.PathSelection != nil { + if o.PathSelection.Strategy != nil { + checks.append(t, "TestCheckResourceAttr", "path_selection.strategy", *o.PathSelection.Strategy) + } + } + + // Check Paths map + if len(o.Paths) > 0 { + checks.append(t, "TestCheckResourceAttr", "paths.%", fmt.Sprintf("%d", len(o.Paths))) + for pathKey, path := range o.Paths { + // Check optional string fields in each path + if path.BfdProfile != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.bfd_profile", pathKey), *path.BfdProfile) + } + if path.Ip != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.ip", pathKey), *path.Ip) + } + + // Check optional boolean fields in each path + if path.BfdUseTunnelMode != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.bfd_use_tunnel_mode", pathKey), fmt.Sprintf("%t", *path.BfdUseTunnelMode)) + } + + // Check optional integer fields in each path + if path.Pod != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.pod", pathKey), fmt.Sprintf("%d", *path.Pod)) + } + + // Check PeerPaths nested map + if len(path.PeerPaths) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.peer_paths.%%", pathKey), fmt.Sprintf("%d", len(path.PeerPaths))) + for peerKey, peer := range path.PeerPaths { + if peer.Preference != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.peer_paths.%s.preference", pathKey, peerKey), fmt.Sprintf("%d", *peer.Preference)) + } + } + } + + // Check TrafficShaping nested object + if path.TrafficShaping != nil { + if path.TrafficShaping.Enabled != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.traffic_shaping.enabled", pathKey), fmt.Sprintf("%t", *path.TrafficShaping.Enabled)) + } + if path.TrafficShaping.MaxTxKbps != nil { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.traffic_shaping.max_tx_kbps", pathKey), fmt.Sprintf("%d", *path.TrafficShaping.MaxTxKbps)) + } + if len(path.TrafficShaping.ClassPercentage) > 0 { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.traffic_shaping.class_percentage.#", pathKey), fmt.Sprintf("%d", len(path.TrafficShaping.ClassPercentage))) + for i, percentage := range path.TrafficShaping.ClassPercentage { + checks.append(t, "TestCheckResourceAttr", fmt.Sprintf("paths.%s.traffic_shaping.class_percentage.%d", pathKey, i), fmt.Sprintf("%d", percentage)) + } + } + } + } + } + return checks } diff --git a/internal/provider/org_vpn_test_structs.go b/internal/provider/org_vpn_test_structs.go index 5666d45f..e6c8b008 100644 --- a/internal/provider/org_vpn_test_structs.go +++ b/internal/provider/org_vpn_test_structs.go @@ -9,24 +9,24 @@ type OrgVpnModel struct { } type PathSelectionValue struct { - Strategy *string `cty:"strategy"` + Strategy *string `cty:"strategy" hcl:"strategy"` } type OrgVpnPathsValue struct { - BfdProfile *string `cty:"bfd_profile"` - BfdUseTunnelMode *bool `cty:"bfd_use_tunnel_mode"` - Ip *string `cty:"ip"` - PeerPaths map[string]PeerPathsValue `cty:"peer_paths"` - Pod *int64 `cty:"pod"` - TrafficShaping *OrgVpnTrafficShapingValue `cty:"traffic_shaping"` + BfdProfile *string `cty:"bfd_profile" hcl:"bfd_profile"` + BfdUseTunnelMode *bool `cty:"bfd_use_tunnel_mode" hcl:"bfd_use_tunnel_mode"` + Ip *string `cty:"ip" hcl:"ip"` + PeerPaths map[string]PeerPathsValue `cty:"peer_paths" hcl:"peer_paths"` + Pod *int64 `cty:"pod" hcl:"pod"` + TrafficShaping *OrgVpnTrafficShapingValue `cty:"traffic_shaping" hcl:"traffic_shaping"` } type PeerPathsValue struct { - Preference *int64 `cty:"preference"` + Preference *int64 `cty:"preference" hcl:"preference"` } type OrgVpnTrafficShapingValue struct { - ClassPercentage []int64 `cty:"class_percentage"` - Enabled *bool `cty:"enabled"` - MaxTxKbps *int64 `cty:"max_tx_kbps"` + ClassPercentage []int64 `cty:"class_percentage" hcl:"class_percentage"` + Enabled *bool `cty:"enabled" hcl:"enabled"` + MaxTxKbps *int64 `cty:"max_tx_kbps" hcl:"max_tx_kbps"` } diff --git a/internal/provider/test_utils.go b/internal/provider/test_utils.go index 78e77209..177fec6b 100644 --- a/internal/provider/test_utils.go +++ b/internal/provider/test_utils.go @@ -291,7 +291,7 @@ func GetSiteWlanBaseConfig(org_ID string) (config string, siteRef string, wlanRe return siteConfigStr + "\n\n" + wlanConfigStr, fmt.Sprintf("mist_site.%s.id", siteConfig.Name), "mist_site_wlan.wlanName.id" } -func GetSiteBaseConfig(org_ID string) (config string, wlanRef string) { +func GetSiteBaseConfig(org_ID string) (config string, siteRef string) { siteConfig := SiteModel{ Name: "TestSite", OrgId: org_ID, @@ -305,7 +305,20 @@ func GetSiteBaseConfig(org_ID string) (config string, wlanRef string) { return siteConfigStr, fmt.Sprintf("mist_site.%s.id", siteConfig.Name) } -// Helper functions +func GetSitegroupBaseConfig(org_ID string) (config string, sitegroupRef string) { + sitegroupConfig := OrgSitegroupModel{ + Name: "TestSitegroup", + OrgId: org_ID, + } + + f := hclwrite.NewEmptyFile() + gohcl.EncodeIntoBody(&sitegroupConfig, f.Body()) + sitegroupConfigStr := Render("org_sitegroup", sitegroupConfig.Name, string(f.Bytes())) + + return sitegroupConfigStr, fmt.Sprintf("mist_org_sitegroup.%s.id", sitegroupConfig.Name) +} + +// Helper function for creating string pointers func stringPtr(s string) *string { return &s } diff --git a/internal/resource_org_setting/sdk_to_terraform.go b/internal/resource_org_setting/sdk_to_terraform.go index 5a881b85..989262cb 100644 --- a/internal/resource_org_setting/sdk_to_terraform.go +++ b/internal/resource_org_setting/sdk_to_terraform.go @@ -6,6 +6,7 @@ import ( mistutils "github.com/Juniper/terraform-provider-mist/internal/commons/utils" "github.com/tmunzer/mistapi-go/mistapi/models" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -167,13 +168,31 @@ func SdkToTerraform(ctx context.Context, data *models.OrgSetting) (OrgSettingMod vpnOptions = vpnOptionsSdkToTerraform(ctx, &diags, data.VpnOptions) } if data.WanPma != nil && data.WanPma.Enabled != nil { - wanPma.Enabled = types.BoolValue(*data.WanPma.Enabled) + var tempDiags diag.Diagnostics + wanPma, tempDiags = NewWanPmaValue(WanPmaValue{}.AttributeTypes(ctx), map[string]attr.Value{ + "enabled": types.BoolValue(*data.WanPma.Enabled), + }) + if tempDiags.HasError() { + wanPma = NewWanPmaValueNull() + } } if data.WiredPma != nil && data.WiredPma.Enabled != nil { - wiredPma.Enabled = types.BoolValue(*data.WiredPma.Enabled) + var tempDiags diag.Diagnostics + wiredPma, tempDiags = NewWiredPmaValue(WiredPmaValue{}.AttributeTypes(ctx), map[string]attr.Value{ + "enabled": types.BoolValue(*data.WiredPma.Enabled), + }) + if tempDiags.HasError() { + wiredPma = NewWiredPmaValueNull() + } } if data.WirelessPma != nil && data.WirelessPma.Enabled != nil { - wirelessPma.Enabled = types.BoolValue(*data.WirelessPma.Enabled) + var tempDiags diag.Diagnostics + wirelessPma, tempDiags = NewWirelessPmaValue(WirelessPmaValue{}.AttributeTypes(ctx), map[string]attr.Value{ + "enabled": types.BoolValue(*data.WirelessPma.Enabled), + }) + if tempDiags.HasError() { + wirelessPma = NewWirelessPmaValueNull() + } } state.ApUpdownThreshold = apUpdownThreshold diff --git a/internal/resource_org_setting/terraform_to_sdk.go b/internal/resource_org_setting/terraform_to_sdk.go index 8a9ed6cb..ff7575ae 100644 --- a/internal/resource_org_setting/terraform_to_sdk.go +++ b/internal/resource_org_setting/terraform_to_sdk.go @@ -207,25 +207,19 @@ func TerraformToSdk(ctx context.Context, plan *OrgSettingModel) (*models.OrgSett } if !plan.WanPma.IsNull() && !plan.WanPma.IsUnknown() { - if plan.WanPma.Enabled.ValueBoolPointer() != nil { - data.WanPma.Enabled = plan.WanPma.Enabled.ValueBoolPointer() - } + data.WanPma = wanPmaTerraformToSdk(plan.WanPma) } else { unset["-wan_pma"] = "" } if !plan.WiredPma.IsNull() && !plan.WiredPma.IsUnknown() { - if plan.WiredPma.Enabled.ValueBoolPointer() != nil { - data.WiredPma.Enabled = plan.WiredPma.Enabled.ValueBoolPointer() - } + data.WiredPma = wiredPmaTerraformToSdk(plan.WiredPma) } else { unset["-wired_pma"] = "" } if !plan.WirelessPma.IsNull() && !plan.WirelessPma.IsUnknown() { - if plan.WirelessPma.Enabled.ValueBoolPointer() != nil { - data.WirelessPma.Enabled = plan.WirelessPma.Enabled.ValueBoolPointer() - } + data.WirelessPma = wirelessPmaTerraformToSdk(plan.WirelessPma) } else { unset["-wireless_pma"] = "" } diff --git a/internal/resource_org_setting/terraform_to_sdk_wan_pma.go b/internal/resource_org_setting/terraform_to_sdk_wan_pma.go new file mode 100644 index 00000000..efd735ba --- /dev/null +++ b/internal/resource_org_setting/terraform_to_sdk_wan_pma.go @@ -0,0 +1,15 @@ +package resource_org_setting + +import ( + "github.com/tmunzer/mistapi-go/mistapi/models" +) + +func wanPmaTerraformToSdk(d WanPmaValue) *models.OrgSettingWanPma { + data := models.OrgSettingWanPma{} + + if d.Enabled.ValueBoolPointer() != nil { + data.Enabled = d.Enabled.ValueBoolPointer() + } + + return &data +} diff --git a/internal/resource_org_setting/terraform_to_sdk_wired_pma.go b/internal/resource_org_setting/terraform_to_sdk_wired_pma.go new file mode 100644 index 00000000..3599277d --- /dev/null +++ b/internal/resource_org_setting/terraform_to_sdk_wired_pma.go @@ -0,0 +1,15 @@ +package resource_org_setting + +import ( + "github.com/tmunzer/mistapi-go/mistapi/models" +) + +func wiredPmaTerraformToSdk(d WiredPmaValue) *models.OrgSettingWiredPma { + data := models.OrgSettingWiredPma{} + + if d.Enabled.ValueBoolPointer() != nil { + data.Enabled = d.Enabled.ValueBoolPointer() + } + + return &data +} diff --git a/internal/resource_org_setting/terraform_to_sdk_wireless_pma.go b/internal/resource_org_setting/terraform_to_sdk_wireless_pma.go new file mode 100644 index 00000000..10ec7d81 --- /dev/null +++ b/internal/resource_org_setting/terraform_to_sdk_wireless_pma.go @@ -0,0 +1,15 @@ +package resource_org_setting + +import ( + "github.com/tmunzer/mistapi-go/mistapi/models" +) + +func wirelessPmaTerraformToSdk(d WirelessPmaValue) *models.OrgSettingWirelessPma { + data := models.OrgSettingWirelessPma{} + + if d.Enabled.ValueBoolPointer() != nil { + data.Enabled = d.Enabled.ValueBoolPointer() + } + + return &data +} diff --git a/internal/resource_org_vpn/sdk_to_terraform.go b/internal/resource_org_vpn/sdk_to_terraform.go index 60167775..98a01926 100644 --- a/internal/resource_org_vpn/sdk_to_terraform.go +++ b/internal/resource_org_vpn/sdk_to_terraform.go @@ -54,10 +54,23 @@ func SdkToTerraform(ctx context.Context, d *models.Vpn) (OrgVpnModel, diag.Diagn } func vpnPathSelectionSdkToTerraform(_ context.Context, _ *diag.Diagnostics, d *models.VpnPathSelection) (data PathSelectionValue) { + data = NewPathSelectionValueNull() - if d.Strategy != nil { - data.Strategy = types.StringValue(string(*d.Strategy)) + if d != nil && d.Strategy != nil { + strategy := types.StringValue(string(*d.Strategy)) + + dataMapValue := map[string]attr.Value{ + "strategy": strategy, + } + + var diags diag.Diagnostics + data, diags = NewPathSelectionValue(PathSelectionValue{}.AttributeTypes(context.Background()), dataMapValue) + if diags.HasError() { + // Fallback to null if creation fails + data = NewPathSelectionValueNull() + } } + return data } @@ -74,7 +87,7 @@ func vpnPathsPeerPathsSdkToTerraform(ctx context.Context, diags *diag.Diagnostic "preference": preference, } - data, e := NewPathsValue(PeerPathsValue{}.AttributeTypes(ctx), dataMapValue) + data, e := NewPeerPathsValue(PeerPathsValue{}.AttributeTypes(ctx), dataMapValue) diags.Append(e...) mapAttrValues[k] = data diff --git a/internal/resource_org_vpn/terraform_to_sdk.go b/internal/resource_org_vpn/terraform_to_sdk.go index 8caf35aa..44c88d49 100644 --- a/internal/resource_org_vpn/terraform_to_sdk.go +++ b/internal/resource_org_vpn/terraform_to_sdk.go @@ -23,6 +23,7 @@ func TerraformToSdk(ctx context.Context, plan *OrgVpnModel) (*models.Vpn, diag.D } func vpnPathSelectionTerraformToSdk(d PathSelectionValue) (data *models.VpnPathSelection) { + data = &models.VpnPathSelection{} if d.Strategy.ValueStringPointer() != nil { data.Strategy = (*models.VpnPathSelectionStrategyEnum)(d.Strategy.ValueStringPointer()) } @@ -32,8 +33,7 @@ func vpnPathSelectionTerraformToSdk(d PathSelectionValue) (data *models.VpnPathS func vpnPathsPeerPathsTerraformToSdk(d basetypes.MapValue) (dataMap map[string]models.VpnPathPeerPathsPeer) { dataMap = make(map[string]models.VpnPathPeerPathsPeer) for k, v := range d.Elements() { - var vInterface interface{} = v - plan := vInterface.(PeerPathsValue) + plan := v.(PeerPathsValue) data := models.VpnPathPeerPathsPeer{} if plan.Preference.ValueInt64Pointer() != nil { @@ -51,6 +51,7 @@ func vpnPathsTrafficShapingTerraformToSdk(ctx context.Context, diags *diag.Diagn if e != nil { diags.Append(e...) } else { + data = &models.VpnPathTrafficShaping{} if !d.ClassPercentage.IsNull() && !d.ClassPercentage.IsUnknown() { data.ClassPercentage = mistutils.ListOfIntTerraformToSdk(d.ClassPercentage) } @@ -67,8 +68,7 @@ func vpnPathsTrafficShapingTerraformToSdk(ctx context.Context, diags *diag.Diagn func vpnPathsTerraformToSdk(ctx context.Context, diags *diag.Diagnostics, d basetypes.MapValue) (dataMap map[string]models.VpnPath) { dataMap = make(map[string]models.VpnPath) for k, v := range d.Elements() { - var vInterface interface{} = v - plan := vInterface.(PathsValue) + plan := v.(PathsValue) data := models.VpnPath{} if plan.BfdProfile.ValueStringPointer() != nil {