@@ -1367,139 +1367,284 @@ func TestCountMetaArgumentInModule(t *testing.T) {
1367
1367
}
1368
1368
1369
1369
func TestDynamicBlocks (t * testing.T ) {
1370
- t .Run ("arg is list of int" , func (t * testing.T ) {
1371
- modules := parse (t , map [string ]string {
1372
- "main.tf" : `
1373
- resource "aws_security_group" "sg-webserver" {
1374
- vpc_id = "1111"
1375
- dynamic "ingress" {
1370
+ tests := []struct {
1371
+ name string
1372
+ src string
1373
+ expected []any
1374
+ }{
1375
+ {
1376
+ name : "for-each use tuple of int" ,
1377
+ src : `resource "test_resource" "test" {
1378
+ dynamic "foo" {
1376
1379
for_each = [80, 443]
1377
1380
content {
1378
- from_port = ingress.value
1379
- to_port = ingress.value
1380
- protocol = "tcp"
1381
- cidr_blocks = ["0.0.0.0/0"]
1381
+ bar = foo.value
1382
1382
}
1383
1383
}
1384
- }
1385
- ` ,
1386
- })
1387
- require .Len (t , modules , 1 )
1388
-
1389
- secGroups := modules .GetResourcesByType ("aws_security_group" )
1390
- assert .Len (t , secGroups , 1 )
1391
- ingressBlocks := secGroups [0 ].GetBlocks ("ingress" )
1392
- assert .Len (t , ingressBlocks , 2 )
1393
-
1394
- var inboundPorts []int
1395
- for _ , ingress := range ingressBlocks {
1396
- fromPort := ingress .GetAttribute ("from_port" ).AsIntValueOrDefault (- 1 , ingress ).Value ()
1397
- inboundPorts = append (inboundPorts , fromPort )
1398
- }
1399
-
1400
- assert .True (t , compareSets ([]int {80 , 443 }, inboundPorts ))
1401
- })
1402
-
1403
- t .Run ("empty for-each" , func (t * testing.T ) {
1404
- modules := parse (t , map [string ]string {
1405
- "main.tf" : `
1406
- resource "aws_lambda_function" "analyzer" {
1407
- dynamic "vpc_config" {
1384
+ }` ,
1385
+ expected : []any {float64 (80 ), float64 (443 )},
1386
+ },
1387
+ {
1388
+ name : "for-each use list of int" ,
1389
+ src : `resource "test_resource" "test" {
1390
+ dynamic "foo" {
1391
+ for_each = tolist([80, 443])
1392
+ content {
1393
+ bar = foo.value
1394
+ }
1395
+ }
1396
+ }` ,
1397
+ expected : []any {float64 (80 ), float64 (443 )},
1398
+ },
1399
+ {
1400
+ name : "for-each use set of int" ,
1401
+ src : `resource "test_resource" "test" {
1402
+ dynamic "foo" {
1403
+ for_each = toset([80, 443])
1404
+ content {
1405
+ bar = foo.value
1406
+ }
1407
+ }
1408
+ }` ,
1409
+ expected : []any {float64 (80 ), float64 (443 )},
1410
+ },
1411
+ {
1412
+ name : "for-each use list of bool" ,
1413
+ src : `resource "test_resource" "test" {
1414
+ dynamic "foo" {
1415
+ for_each = tolist([true])
1416
+ content {
1417
+ bar = foo.value
1418
+ }
1419
+ }
1420
+ }` ,
1421
+ expected : []any {true },
1422
+ },
1423
+ {
1424
+ name : "empty for-each" ,
1425
+ src : `resource "test_resource" "test" {
1426
+ dynamic "foo" {
1408
1427
for_each = []
1409
1428
content {}
1410
1429
}
1430
+ }` ,
1431
+ expected : []any {},
1432
+ },
1433
+ {
1434
+ name : "for-each use tuple of objects" ,
1435
+ src : `variable "test_var" {
1436
+ type = list(object({ enabled = bool }))
1437
+ default = [{ enabled = true }]
1411
1438
}
1412
- ` ,
1413
- })
1414
- require .Len (t , modules , 1 )
1415
1439
1416
- functions := modules .GetResourcesByType ("aws_lambda_function" )
1417
- assert .Len (t , functions , 1 )
1418
- vpcConfigs := functions [0 ].GetBlocks ("vpc_config" )
1419
- assert .Empty (t , vpcConfigs )
1420
- })
1440
+ resource "test_resource" "test" {
1441
+ dynamic "foo" {
1442
+ for_each = var.test_var
1421
1443
1422
- t .Run ("arg is list of bool" , func (t * testing.T ) {
1423
- modules := parse (t , map [string ]string {
1424
- "main.tf" : `
1425
- resource "aws_lambda_function" "analyzer" {
1426
- dynamic "vpc_config" {
1427
- for_each = [true]
1428
- content {}
1444
+ content {
1445
+ bar = foo.value.enabled
1446
+ }
1447
+ }
1448
+ }` ,
1449
+ expected : []any {true },
1450
+ },
1451
+ {
1452
+ name : "attribute ref to object key" ,
1453
+ src : `variable "some_var" {
1454
+ type = map(
1455
+ object({
1456
+ tag = string
1457
+ })
1458
+ )
1459
+ default = {
1460
+ ssh = { "tag" = "login" }
1461
+ http = { "tag" = "proxy" }
1462
+ https = { "tag" = "proxy" }
1429
1463
}
1430
1464
}
1431
- ` ,
1432
- })
1433
- require .Len (t , modules , 1 )
1434
-
1435
- functions := modules .GetResourcesByType ("aws_lambda_function" )
1436
- assert .Len (t , functions , 1 )
1437
- vpcConfigs := functions [0 ].GetBlocks ("vpc_config" )
1438
- assert .Len (t , vpcConfigs , 1 )
1439
- })
1440
1465
1441
- t .Run ("arg is list of objects" , func (t * testing.T ) {
1442
- modules := parse (t , map [string ]string {
1443
- "main.tf" : `locals {
1444
- cluster_network_policy = [{
1445
- enabled = true
1446
- }]
1466
+ resource "test_resource" "test" {
1467
+ dynamic "foo" {
1468
+ for_each = { for name, values in var.some_var : name => values }
1469
+ content {
1470
+ bar = foo.key
1471
+ }
1472
+ }
1473
+ }` ,
1474
+ expected : []any {"ssh" , "http" , "https" },
1475
+ },
1476
+ {
1477
+ name : "attribute ref to object value" ,
1478
+ src : `variable "some_var" {
1479
+ type = map(
1480
+ object({
1481
+ tag = string
1482
+ })
1483
+ )
1484
+ default = {
1485
+ ssh = { "tag" = "login" }
1486
+ http = { "tag" = "proxy" }
1487
+ https = { "tag" = "proxy" }
1488
+ }
1447
1489
}
1448
1490
1449
- resource "google_container_cluster" "primary" {
1450
- name = "test"
1491
+ resource "test_resource" "test" {
1492
+ dynamic "foo" {
1493
+ for_each = { for name, values in var.some_var : name => values }
1494
+ content {
1495
+ bar = foo.value.tag
1496
+ }
1497
+ }
1498
+ }` ,
1499
+ expected : []any {"login" , "proxy" , "proxy" },
1500
+ },
1501
+ {
1502
+ name : "attribute ref to map key" ,
1503
+ src : `variable "some_var" {
1504
+ type = map
1505
+ default = {
1506
+ ssh = { "tag" = "login" }
1507
+ http = { "tag" = "proxy" }
1508
+ https = { "tag" = "proxy" }
1509
+ }
1510
+ }
1451
1511
1452
- dynamic "network_policy" {
1453
- for_each = local.cluster_network_policy
1512
+ resource "test_resource" "test" {
1513
+ dynamic "foo" {
1514
+ for_each = var.some_var
1515
+ content {
1516
+ bar = foo.key
1517
+ }
1518
+ }
1519
+ }` ,
1520
+ expected : []any {"ssh" , "http" , "https" },
1521
+ },
1522
+ {
1523
+ name : "attribute ref to map value" ,
1524
+ src : `variable "some_var" {
1525
+ type = map
1526
+ default = {
1527
+ ssh = { "tag" = "login" }
1528
+ http = { "tag" = "proxy" }
1529
+ https = { "tag" = "proxy" }
1530
+ }
1531
+ }
1454
1532
1533
+ resource "test_resource" "test" {
1534
+ dynamic "foo" {
1535
+ for_each = var.some_var
1455
1536
content {
1456
- enabled = network_policy .value.enabled
1537
+ bar = foo .value.tag
1457
1538
}
1458
1539
}
1459
1540
}` ,
1460
- })
1461
- require .Len (t , modules , 1 )
1541
+ expected : []any {"login" , "proxy" , "proxy" },
1542
+ },
1543
+ {
1544
+ name : "dynamic block with iterator" ,
1545
+ src : `resource "test_resource" "test" {
1546
+ dynamic "foo" {
1547
+ for_each = ["foo", "bar"]
1548
+ iterator = some_iterator
1549
+ content {
1550
+ bar = some_iterator.value
1551
+ }
1552
+ }
1553
+ }` ,
1554
+ expected : []any {"foo" , "bar" },
1555
+ },
1556
+ {
1557
+ name : "iterator and parent block with same name" ,
1558
+ src : `resource "test_resource" "test" {
1559
+ dynamic "foo" {
1560
+ for_each = ["foo", "bar"]
1561
+ iterator = foo
1562
+ content {
1563
+ bar = foo.value
1564
+ }
1565
+ }
1566
+ }` ,
1567
+ expected : []any {"foo" , "bar" },
1568
+ },
1569
+ {
1570
+ name : "for-each use null value" ,
1571
+ src : `resource "test_resource" "test" {
1572
+ dynamic "foo" {
1573
+ for_each = null
1574
+ content {
1575
+ bar = foo.value
1576
+ }
1577
+ }
1578
+ }` ,
1579
+ expected : []any {},
1580
+ },
1581
+ {
1582
+ name : "no for-each attribute" ,
1583
+ src : `resource "test_resource" "test" {
1584
+ dynamic "foo" {
1585
+ content {
1586
+ bar = foo.value
1587
+ }
1588
+ }
1589
+ }` ,
1590
+ expected : []any {},
1591
+ },
1592
+ }
1462
1593
1463
- clusters := modules .GetResourcesByType ("google_container_cluster" )
1464
- assert .Len (t , clusters , 1 )
1594
+ for _ , tt := range tests {
1595
+ t .Run (tt .name , func (t * testing.T ) {
1596
+ modules := parse (t , map [string ]string {
1597
+ "main.tf" : tt .src ,
1598
+ })
1599
+ require .Len (t , modules , 1 )
1465
1600
1466
- networkPolicies := clusters [0 ].GetBlocks ("network_policy" )
1467
- assert .Len (t , networkPolicies , 1 )
1601
+ resource := modules .GetResourcesByType ("test_resource" )
1602
+ require .Len (t , resource , 1 )
1603
+ blocks := resource [0 ].GetBlocks ("foo" )
1468
1604
1469
- enabled := networkPolicies [0 ].GetAttribute ("enabled" )
1470
- assert .True (t , enabled .Value ().True ())
1471
- })
1605
+ var vals []any
1606
+ for _ , attr := range blocks {
1607
+ vals = append (vals , attr .GetAttribute ("bar" ).GetRawValue ())
1608
+ }
1472
1609
1473
- t .Run ("nested dynamic" , func (t * testing.T ) {
1474
- modules := parse (t , map [string ]string {
1475
- "main.tf" : `
1476
- resource "test_block" "this" {
1477
- name = "name"
1478
- location = "loc"
1479
- dynamic "env" {
1480
- for_each = ["1", "2"]
1481
- content {
1482
- dynamic "value_source" {
1483
- for_each = [true, true]
1484
- content {}
1485
- }
1610
+ assert .ElementsMatch (t , tt .expected , vals )
1611
+ })
1486
1612
}
1613
+ }
1614
+
1615
+ func TestNestedDynamicBlock (t * testing.T ) {
1616
+ modules := parse (t , map [string ]string {
1617
+ "main.tf" : `resource "test_resource" "test" {
1618
+ dynamic "foo" {
1619
+ for_each = ["1", "1"]
1620
+ content {
1621
+ dynamic "bar" {
1622
+ for_each = [true, true]
1623
+ content {
1624
+ baz = foo.value
1625
+ qux = bar.value
1626
+ }
1627
+ }
1628
+ }
1487
1629
}
1488
1630
}` ,
1489
- })
1490
- require .Len (t , modules , 1 )
1631
+ })
1632
+ require .Len (t , modules , 1 )
1491
1633
1492
- testResources := modules .GetResourcesByType ("test_block " )
1493
- assert .Len (t , testResources , 1 )
1494
- envs := testResources [0 ].GetBlocks ("env " )
1495
- assert .Len (t , envs , 2 )
1634
+ testResources := modules .GetResourcesByType ("test_resource " )
1635
+ assert .Len (t , testResources , 1 )
1636
+ blocks := testResources [0 ].GetBlocks ("foo " )
1637
+ assert .Len (t , blocks , 2 )
1496
1638
1497
- var sources []* terraform.Block
1498
- for _ , env := range envs {
1499
- sources = append (sources , env .GetBlocks ("value_source" )... )
1639
+ var nested []* terraform.Block
1640
+ for _ , block := range blocks {
1641
+ nested = append (nested , block .GetBlocks ("bar" )... )
1642
+ for _ , b := range nested {
1643
+ assert .Equal (t , "1" , b .GetAttribute ("baz" ).GetRawValue ())
1644
+ assert .Equal (t , true , b .GetAttribute ("qux" ).GetRawValue ())
1500
1645
}
1501
- assert . Len ( t , sources , 4 )
1502
- } )
1646
+ }
1647
+ assert . Len ( t , nested , 4 )
1503
1648
}
1504
1649
1505
1650
func parse (t * testing.T , files map [string ]string ) terraform.Modules {
@@ -1513,21 +1658,6 @@ func parse(t *testing.T, files map[string]string) terraform.Modules {
1513
1658
return modules
1514
1659
}
1515
1660
1516
- func compareSets (a , b []int ) bool {
1517
- m := make (map [int ]bool )
1518
- for _ , el := range a {
1519
- m [el ] = true
1520
- }
1521
-
1522
- for _ , el := range b {
1523
- if ! m [el ] {
1524
- return false
1525
- }
1526
- }
1527
-
1528
- return true
1529
- }
1530
-
1531
1661
func TestModuleRefersToOutputOfAnotherModule (t * testing.T ) {
1532
1662
files := map [string ]string {
1533
1663
"main.tf" : `
@@ -1775,42 +1905,6 @@ variable "foo" {}
1775
1905
assert .Equal (t , "bar" , blocks [0 ].GetAttribute ("foo" ).Value ().AsString ())
1776
1906
}
1777
1907
1778
- func TestDynamicWithIterator (t * testing.T ) {
1779
- fsys := fstest.MapFS {
1780
- "main.tf" : & fstest.MapFile {
1781
- Data : []byte (`resource "aws_s3_bucket" "this" {
1782
- dynamic versioning {
1783
- for_each = [true]
1784
- iterator = ver
1785
-
1786
- content {
1787
- enabled = ver.value
1788
- }
1789
- }
1790
- }` ),
1791
- },
1792
- }
1793
-
1794
- parser := New (
1795
- fsys , "" ,
1796
- OptionStopOnHCLError (true ),
1797
- OptionWithDownloads (false ),
1798
- )
1799
- require .NoError (t , parser .ParseFS (context .TODO (), "." ))
1800
-
1801
- modules , _ , err := parser .EvaluateAll (context .TODO ())
1802
- require .NoError (t , err )
1803
-
1804
- assert .Len (t , modules , 1 )
1805
-
1806
- buckets := modules .GetResourcesByType ("aws_s3_bucket" )
1807
- assert .Len (t , buckets , 1 )
1808
-
1809
- attr , _ := buckets [0 ].GetNestedAttribute ("versioning.enabled" )
1810
-
1811
- assert .True (t , attr .Value ().True ())
1812
- }
1813
-
1814
1908
func Test_AWSRegionNameDefined (t * testing.T ) {
1815
1909
1816
1910
fs := testutil .CreateFS (t , map [string ]string {
0 commit comments