@@ -1255,6 +1255,312 @@ func TestIterateHierarchyV2_CrossNamespaceOwnerReference(t *testing.T) {
1255
1255
assert .True (t , foundNamespacedChild , "Should visit Deployment child (this tests the fix)" )
1256
1256
}
1257
1257
1258
+ // TestBuildGraph_NilAllResources tests buildGraph with nil allResources parameter
1259
+ func TestBuildGraph_NilAllResources (t * testing.T ) {
1260
+ // Create test resources
1261
+ parentUID := types .UID ("parent-123" )
1262
+ childUID := types .UID ("child-456" )
1263
+
1264
+ parent := & Resource {
1265
+ Ref : corev1.ObjectReference {
1266
+ APIVersion : "v1" ,
1267
+ Kind : "ConfigMap" ,
1268
+ Name : "parent" ,
1269
+ Namespace : "test-ns" ,
1270
+ UID : parentUID ,
1271
+ },
1272
+ }
1273
+
1274
+ child := & Resource {
1275
+ Ref : corev1.ObjectReference {
1276
+ APIVersion : "v1" ,
1277
+ Kind : "Pod" ,
1278
+ Name : "child" ,
1279
+ Namespace : "test-ns" ,
1280
+ UID : childUID ,
1281
+ },
1282
+ OwnerRefs : []metav1.OwnerReference {{
1283
+ APIVersion : "v1" ,
1284
+ Kind : "ConfigMap" ,
1285
+ Name : "parent" ,
1286
+ UID : parentUID ,
1287
+ }},
1288
+ }
1289
+
1290
+ nsNodes := map [kube.ResourceKey ]* Resource {
1291
+ parent .ResourceKey (): parent ,
1292
+ child .ResourceKey (): child ,
1293
+ }
1294
+
1295
+ // Call buildGraph with nil allResources
1296
+ graph := buildGraph (nsNodes , nil )
1297
+
1298
+ // Should still work for same-namespace relationships
1299
+ assert .Contains (t , graph , parent .ResourceKey ())
1300
+ assert .Contains (t , graph [parent .ResourceKey ()], childUID )
1301
+ }
1302
+
1303
+ // TestBuildGraph_InvalidAPIVersion tests handling of invalid API versions in owner references
1304
+ func TestBuildGraph_InvalidAPIVersion (t * testing.T ) {
1305
+ childUID := types .UID ("child-123" )
1306
+
1307
+ child := & Resource {
1308
+ Ref : corev1.ObjectReference {
1309
+ APIVersion : "v1" ,
1310
+ Kind : "Pod" ,
1311
+ Name : "child" ,
1312
+ Namespace : "test-ns" ,
1313
+ UID : childUID ,
1314
+ },
1315
+ OwnerRefs : []metav1.OwnerReference {{
1316
+ APIVersion : "invalid/api/version" , // Invalid API version
1317
+ Kind : "ConfigMap" ,
1318
+ Name : "parent" ,
1319
+ // No UID - should trigger API version parsing
1320
+ }},
1321
+ }
1322
+
1323
+ nsNodes := map [kube.ResourceKey ]* Resource {
1324
+ child .ResourceKey (): child ,
1325
+ }
1326
+
1327
+ allResources := map [kube.ResourceKey ]* Resource {
1328
+ child .ResourceKey (): child ,
1329
+ }
1330
+
1331
+ // Should not panic and should handle invalid API version gracefully
1332
+ graph := buildGraph (nsNodes , allResources )
1333
+
1334
+ // No parent should be found due to invalid API version
1335
+ assert .Empty (t , graph )
1336
+ }
1337
+
1338
+ // TestBuildGraph_CrossNamespaceMissingUID tests cross-namespace parent resolution with missing UID
1339
+ func TestBuildGraph_CrossNamespaceMissingUID (t * testing.T ) {
1340
+ parentUID := types .UID ("parent-123" )
1341
+ childUID := types .UID ("child-456" )
1342
+
1343
+ // Cluster-scoped parent
1344
+ parent := & Resource {
1345
+ Ref : corev1.ObjectReference {
1346
+ APIVersion : "v1" ,
1347
+ Kind : "ConfigMap" ,
1348
+ Name : "parent" ,
1349
+ Namespace : "" , // Cluster-scoped
1350
+ UID : parentUID ,
1351
+ },
1352
+ }
1353
+
1354
+ // Namespaced child with owner reference missing UID
1355
+ child := & Resource {
1356
+ Ref : corev1.ObjectReference {
1357
+ APIVersion : "v1" ,
1358
+ Kind : "Pod" ,
1359
+ Name : "child" ,
1360
+ Namespace : "test-ns" ,
1361
+ UID : childUID ,
1362
+ },
1363
+ OwnerRefs : []metav1.OwnerReference {{
1364
+ APIVersion : "v1" ,
1365
+ Kind : "ConfigMap" ,
1366
+ Name : "parent" ,
1367
+ // UID is missing - should be resolved via cross-namespace lookup
1368
+ }},
1369
+ }
1370
+
1371
+ nsNodes := map [kube.ResourceKey ]* Resource {
1372
+ child .ResourceKey (): child ,
1373
+ }
1374
+
1375
+ allResources := map [kube.ResourceKey ]* Resource {
1376
+ parent .ResourceKey (): parent ,
1377
+ child .ResourceKey (): child ,
1378
+ }
1379
+
1380
+ graph := buildGraph (nsNodes , allResources )
1381
+
1382
+ // Should find the parent via cross-namespace lookup and establish relationship
1383
+ assert .Contains (t , graph , parent .ResourceKey ())
1384
+ assert .Contains (t , graph [parent .ResourceKey ()], childUID )
1385
+
1386
+ // Verify UID was backfilled
1387
+ assert .Equal (t , parentUID , child .OwnerRefs [0 ].UID )
1388
+ }
1389
+
1390
+ // TestBuildGraph_NonExistentParent tests cross-namespace child with non-existent parent
1391
+ func TestBuildGraph_NonExistentParent (t * testing.T ) {
1392
+ childUID := types .UID ("child-456" )
1393
+
1394
+ child := & Resource {
1395
+ Ref : corev1.ObjectReference {
1396
+ APIVersion : "v1" ,
1397
+ Kind : "Pod" ,
1398
+ Name : "child" ,
1399
+ Namespace : "test-ns" ,
1400
+ UID : childUID ,
1401
+ },
1402
+ OwnerRefs : []metav1.OwnerReference {{
1403
+ APIVersion : "v1" ,
1404
+ Kind : "ConfigMap" ,
1405
+ Name : "non-existent-parent" ,
1406
+ // No UID - should trigger lookup that fails
1407
+ }},
1408
+ }
1409
+
1410
+ nsNodes := map [kube.ResourceKey ]* Resource {
1411
+ child .ResourceKey (): child ,
1412
+ }
1413
+
1414
+ allResources := map [kube.ResourceKey ]* Resource {
1415
+ child .ResourceKey (): child ,
1416
+ // Parent is not in allResources
1417
+ }
1418
+
1419
+ graph := buildGraph (nsNodes , allResources )
1420
+
1421
+ // No relationships should be established
1422
+ assert .Empty (t , graph )
1423
+ }
1424
+
1425
+ // TestBuildGraph_CrossNamespaceUIDLookup tests cross-namespace parent lookup by UID
1426
+ func TestBuildGraph_CrossNamespaceUIDLookup (t * testing.T ) {
1427
+ parentUID := types .UID ("parent-123" )
1428
+ childUID := types .UID ("child-456" )
1429
+
1430
+ // Cluster-scoped parent
1431
+ parent := & Resource {
1432
+ Ref : corev1.ObjectReference {
1433
+ APIVersion : "v1" ,
1434
+ Kind : "ConfigMap" ,
1435
+ Name : "parent" ,
1436
+ Namespace : "" , // Cluster-scoped
1437
+ UID : parentUID ,
1438
+ },
1439
+ }
1440
+
1441
+ // Namespaced child with owner reference that has UID but parent not in same namespace
1442
+ child := & Resource {
1443
+ Ref : corev1.ObjectReference {
1444
+ APIVersion : "v1" ,
1445
+ Kind : "Pod" ,
1446
+ Name : "child" ,
1447
+ Namespace : "test-ns" ,
1448
+ UID : childUID ,
1449
+ },
1450
+ OwnerRefs : []metav1.OwnerReference {{
1451
+ APIVersion : "v1" ,
1452
+ Kind : "ConfigMap" ,
1453
+ Name : "parent" ,
1454
+ UID : parentUID , // UID is present
1455
+ }},
1456
+ }
1457
+
1458
+ nsNodes := map [kube.ResourceKey ]* Resource {
1459
+ child .ResourceKey (): child ,
1460
+ // Parent is not in same namespace
1461
+ }
1462
+
1463
+ allResources := map [kube.ResourceKey ]* Resource {
1464
+ parent .ResourceKey (): parent ,
1465
+ child .ResourceKey (): child ,
1466
+ }
1467
+
1468
+ graph := buildGraph (nsNodes , allResources )
1469
+
1470
+ // Should establish cross-namespace relationship via UID lookup
1471
+ assert .Contains (t , graph , parent .ResourceKey ())
1472
+ assert .Contains (t , graph [parent .ResourceKey ()], childUID )
1473
+ }
1474
+
1475
+ // TestBuildGraph_DuplicateUIDs tests handling of resources with duplicate UIDs
1476
+ func TestBuildGraph_DuplicateUIDs (t * testing.T ) {
1477
+ parentUID := types .UID ("parent-123" )
1478
+ duplicateUID := types .UID ("duplicate-456" )
1479
+
1480
+ parent := & Resource {
1481
+ Ref : corev1.ObjectReference {
1482
+ APIVersion : "v1" ,
1483
+ Kind : "ConfigMap" ,
1484
+ Name : "parent" ,
1485
+ Namespace : "test-ns" ,
1486
+ UID : parentUID ,
1487
+ },
1488
+ }
1489
+
1490
+ // Two children with the same UID (simulating replicasets from different API groups)
1491
+ child1 := & Resource {
1492
+ Ref : corev1.ObjectReference {
1493
+ APIVersion : "apps/v1" ,
1494
+ Kind : "ReplicaSet" ,
1495
+ Name : "child-apps" ,
1496
+ Namespace : "test-ns" ,
1497
+ UID : duplicateUID ,
1498
+ },
1499
+ OwnerRefs : []metav1.OwnerReference {{
1500
+ APIVersion : "v1" ,
1501
+ Kind : "ConfigMap" ,
1502
+ Name : "parent" ,
1503
+ UID : parentUID ,
1504
+ }},
1505
+ }
1506
+
1507
+ child2 := & Resource {
1508
+ Ref : corev1.ObjectReference {
1509
+ APIVersion : "extensions/v1beta1" ,
1510
+ Kind : "ReplicaSet" ,
1511
+ Name : "child-extensions" ,
1512
+ Namespace : "test-ns" ,
1513
+ UID : duplicateUID ,
1514
+ },
1515
+ OwnerRefs : []metav1.OwnerReference {{
1516
+ APIVersion : "v1" ,
1517
+ Kind : "ConfigMap" ,
1518
+ Name : "parent" ,
1519
+ UID : parentUID ,
1520
+ }},
1521
+ }
1522
+
1523
+ nsNodes := map [kube.ResourceKey ]* Resource {
1524
+ parent .ResourceKey (): parent ,
1525
+ child1 .ResourceKey (): child1 ,
1526
+ child2 .ResourceKey (): child2 ,
1527
+ }
1528
+
1529
+ graph := buildGraph (nsNodes , nil )
1530
+
1531
+ // Should handle duplicate UIDs gracefully by picking consistently
1532
+ assert .Contains (t , graph , parent .ResourceKey ())
1533
+ assert .Contains (t , graph [parent .ResourceKey ()], duplicateUID )
1534
+
1535
+ // Should pick the same child consistently (based on string comparison)
1536
+ selectedChild := graph [parent .ResourceKey ()][duplicateUID ]
1537
+ assert .NotNil (t , selectedChild )
1538
+ }
1539
+
1540
+ // TestIterateHierarchyV2_EdgeCases tests additional edge cases for hierarchy iteration
1541
+ func TestIterateHierarchyV2_EdgeCases (t * testing.T ) {
1542
+ cluster := newCluster (t )
1543
+
1544
+ t .Run ("EmptyKeysList" , func (t * testing.T ) {
1545
+ var visited []kube.ResourceKey
1546
+ cluster .IterateHierarchyV2 ([]kube.ResourceKey {}, func (resource * Resource , _ map [kube.ResourceKey ]* Resource ) bool {
1547
+ visited = append (visited , resource .ResourceKey ())
1548
+ return true
1549
+ })
1550
+ assert .Empty (t , visited )
1551
+ })
1552
+
1553
+ t .Run ("NonExistentKeys" , func (t * testing.T ) {
1554
+ var visited []kube.ResourceKey
1555
+ nonExistentKey := kube.ResourceKey {Group : "fake" , Kind : "Fake" , Namespace : "fake" , Name : "fake" }
1556
+ cluster .IterateHierarchyV2 ([]kube.ResourceKey {nonExistentKey }, func (resource * Resource , _ map [kube.ResourceKey ]* Resource ) bool {
1557
+ visited = append (visited , resource .ResourceKey ())
1558
+ return true
1559
+ })
1560
+ assert .Empty (t , visited )
1561
+ })
1562
+ }
1563
+
1258
1564
// Test_watchEvents_Deadlock validates that starting watches will not create a deadlock
1259
1565
// caused by using improper locking in various callback methods when there is a high load on the
1260
1566
// system.
0 commit comments