@@ -2,11 +2,50 @@ package github
22
33import (
44 "fmt"
5+ "regexp"
56 "testing"
67
78 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
89)
910
11+ func TestAccGithubOrganizationCustomPropertiesValidation (t * testing.T ) {
12+ t .Run ("rejects invalid values_editable_by value" , func (t * testing.T ) {
13+ config := `
14+ resource "github_organization_custom_properties" "test" {
15+ property_name = "TestInvalidValuesEditableBy"
16+ value_type = "string"
17+ required = false
18+ description = "Test invalid values_editable_by"
19+ values_editable_by = "invalid_value"
20+ }`
21+
22+ testCase := func (t * testing.T , mode string ) {
23+ resource .Test (t , resource.TestCase {
24+ PreCheck : func () { skipUnlessMode (t , mode ) },
25+ Providers : testAccProviders ,
26+ Steps : []resource.TestStep {
27+ {
28+ Config : config ,
29+ ExpectError : regexp .MustCompile ("invalid_value is an invalid value" ),
30+ },
31+ },
32+ })
33+ }
34+
35+ t .Run ("with an anonymous account" , func (t * testing.T ) {
36+ t .Skip ("anonymous account not supported for this operation" )
37+ })
38+
39+ t .Run ("with an individual account" , func (t * testing.T ) {
40+ t .Skip ("individual account not supported for this operation" )
41+ })
42+
43+ t .Run ("with an organization account" , func (t * testing.T ) {
44+ testCase (t , organization )
45+ })
46+ })
47+ }
48+
1049func TestAccGithubOrganizationCustomProperties (t * testing.T ) {
1150 t .Run ("creates custom property without error" , func (t * testing.T ) {
1251 config := `
@@ -151,4 +190,228 @@ func TestAccGithubOrganizationCustomProperties(t *testing.T) {
151190 testCase (t , organization )
152191 })
153192 })
193+
194+ t .Run ("creates custom property with values_editable_by without error" , func (t * testing.T ) {
195+ config := `
196+ resource "github_organization_custom_properties" "test" {
197+ property_name = "TestValuesEditableBy"
198+ value_type = "string"
199+ required = false
200+ description = "Test property for values_editable_by"
201+ values_editable_by = "org_and_repo_actors"
202+ }`
203+
204+ check := resource .ComposeTestCheckFunc (
205+ resource .TestCheckResourceAttr (
206+ "github_organization_custom_properties.test" ,
207+ "property_name" , "TestValuesEditableBy" ,
208+ ),
209+ resource .TestCheckResourceAttr (
210+ "github_organization_custom_properties.test" ,
211+ "values_editable_by" , "org_and_repo_actors" ,
212+ ),
213+ )
214+
215+ testCase := func (t * testing.T , mode string ) {
216+ resource .Test (t , resource.TestCase {
217+ PreCheck : func () { skipUnlessMode (t , mode ) },
218+ Providers : testAccProviders ,
219+ Steps : []resource.TestStep {
220+ {
221+ Config : config ,
222+ Check : check ,
223+ },
224+ },
225+ })
226+ }
227+
228+ t .Run ("with an anonymous account" , func (t * testing.T ) {
229+ t .Skip ("anonymous account not supported for this operation" )
230+ })
231+
232+ t .Run ("with an individual account" , func (t * testing.T ) {
233+ t .Skip ("individual account not supported for this operation" )
234+ })
235+
236+ t .Run ("with an organization account" , func (t * testing.T ) {
237+ testCase (t , organization )
238+ })
239+ })
240+
241+ t .Run ("backward compatibility - property without values_editable_by defaults correctly" , func (t * testing.T ) {
242+ config := `
243+ resource "github_organization_custom_properties" "test" {
244+ property_name = "TestBackwardCompat"
245+ value_type = "string"
246+ required = false
247+ description = "Test property without values_editable_by"
248+ }`
249+
250+ check := resource .ComposeTestCheckFunc (
251+ resource .TestCheckResourceAttr (
252+ "github_organization_custom_properties.test" ,
253+ "property_name" , "TestBackwardCompat" ,
254+ ),
255+ // When not specified, API returns "org_actors" as the default
256+ resource .TestCheckResourceAttr (
257+ "github_organization_custom_properties.test" ,
258+ "values_editable_by" , "org_actors" ,
259+ ),
260+ )
261+
262+ testCase := func (t * testing.T , mode string ) {
263+ resource .Test (t , resource.TestCase {
264+ PreCheck : func () { skipUnlessMode (t , mode ) },
265+ Providers : testAccProviders ,
266+ Steps : []resource.TestStep {
267+ {
268+ Config : config ,
269+ Check : check ,
270+ },
271+ },
272+ })
273+ }
274+
275+ t .Run ("with an anonymous account" , func (t * testing.T ) {
276+ t .Skip ("anonymous account not supported for this operation" )
277+ })
278+
279+ t .Run ("with an individual account" , func (t * testing.T ) {
280+ t .Skip ("individual account not supported for this operation" )
281+ })
282+
283+ t .Run ("with an organization account" , func (t * testing.T ) {
284+ testCase (t , organization )
285+ })
286+ })
287+
288+ t .Run ("update values_editable_by from org_actors to org_and_repo_actors" , func (t * testing.T ) {
289+ configBefore := `
290+ resource "github_organization_custom_properties" "test" {
291+ property_name = "TestUpdateValuesEditableBy"
292+ value_type = "string"
293+ required = false
294+ description = "Test updating values_editable_by"
295+ values_editable_by = "org_actors"
296+ }`
297+
298+ configAfter := `
299+ resource "github_organization_custom_properties" "test" {
300+ property_name = "TestUpdateValuesEditableBy"
301+ value_type = "string"
302+ required = false
303+ description = "Test updating values_editable_by"
304+ values_editable_by = "org_and_repo_actors"
305+ }`
306+
307+ const resourceName = "github_organization_custom_properties.test"
308+
309+ checkBefore := resource .ComposeTestCheckFunc (
310+ resource .TestCheckResourceAttr (resourceName , "values_editable_by" , "org_actors" ),
311+ )
312+ checkAfter := resource .ComposeTestCheckFunc (
313+ resource .TestCheckResourceAttr (resourceName , "values_editable_by" , "org_and_repo_actors" ),
314+ )
315+
316+ testCase := func (t * testing.T , mode string ) {
317+ resource .Test (t , resource.TestCase {
318+ PreCheck : func () { skipUnlessMode (t , mode ) },
319+ Providers : testAccProviders ,
320+ Steps : []resource.TestStep {
321+ {
322+ Config : configBefore ,
323+ Check : checkBefore ,
324+ },
325+ {
326+ Config : configAfter ,
327+ Check : checkAfter ,
328+ },
329+ },
330+ })
331+ }
332+
333+ t .Run ("with an anonymous account" , func (t * testing.T ) {
334+ t .Skip ("anonymous account not supported for this operation" )
335+ })
336+
337+ t .Run ("with an individual account" , func (t * testing.T ) {
338+ t .Skip ("individual account not supported for this operation" )
339+ })
340+
341+ t .Run ("with an organization account" , func (t * testing.T ) {
342+ testCase (t , organization )
343+ })
344+ })
345+
346+ t .Run ("imports existing property with values_editable_by set via UI" , func (t * testing.T ) {
347+ // This test simulates a scenario where values_editable_by was set to
348+ // org_and_repo_actors in the GitHub UI before Terraform support was added.
349+ // The resource config intentionally omits values_editable_by to verify
350+ // Terraform can read and maintain the existing value from the API.
351+
352+ configWithoutField := `
353+ resource "github_organization_custom_properties" "test" {
354+ property_name = "TestImportWithUISet"
355+ value_type = "string"
356+ required = false
357+ description = "Test property set via UI"
358+ }`
359+
360+ // After import, we explicitly set the value in config to match what's in the API
361+ configWithField := `
362+ resource "github_organization_custom_properties" "test" {
363+ property_name = "TestImportWithUISet"
364+ value_type = "string"
365+ required = false
366+ description = "Test property set via UI"
367+ values_editable_by = "org_and_repo_actors"
368+ }`
369+
370+ const resourceName = "github_organization_custom_properties.test"
371+
372+ testCase := func (t * testing.T , mode string ) {
373+ resource .Test (t , resource.TestCase {
374+ PreCheck : func () { skipUnlessMode (t , mode ) },
375+ Providers : testAccProviders ,
376+ Steps : []resource.TestStep {
377+ {
378+ // First, create a property with values_editable_by set
379+ Config : configWithField ,
380+ Check : resource .ComposeTestCheckFunc (
381+ resource .TestCheckResourceAttr (resourceName , "values_editable_by" , "org_and_repo_actors" ),
382+ ),
383+ },
384+ {
385+ // Simulate the scenario: config doesn't have values_editable_by
386+ // (as it would have been before Terraform support was added)
387+ // Terraform should read the existing value from the API
388+ Config : configWithoutField ,
389+ Check : resource .ComposeTestCheckFunc (
390+ // Terraform should still see the value from the API
391+ resource .TestCheckResourceAttr (resourceName , "values_editable_by" , "org_and_repo_actors" ),
392+ ),
393+ },
394+ {
395+ // Now add it back to the config - should be no changes needed
396+ Config : configWithField ,
397+ Check : resource .ComposeTestCheckFunc (
398+ resource .TestCheckResourceAttr (resourceName , "values_editable_by" , "org_and_repo_actors" ),
399+ ),
400+ },
401+ },
402+ })
403+ }
404+
405+ t .Run ("with an anonymous account" , func (t * testing.T ) {
406+ t .Skip ("anonymous account not supported for this operation" )
407+ })
408+
409+ t .Run ("with an individual account" , func (t * testing.T ) {
410+ t .Skip ("individual account not supported for this operation" )
411+ })
412+
413+ t .Run ("with an organization account" , func (t * testing.T ) {
414+ testCase (t , organization )
415+ })
416+ })
154417}
0 commit comments