package patch

import (
	"encoding/json"
	"testing"

	"github.com/go-logr/logr"
	"github.com/kyverno/kyverno/pkg/engine/anchor"
	"gotest.tools/assert"
	yaml "sigs.k8s.io/kustomize/kyaml/yaml"
)

func Test_preProcessStrategicMergePatch_multipleAnchors(t *testing.T) {
	testCases := []struct {
		rawPolicy     []byte
		rawResource   []byte
		expectedPatch []byte
	}{
		{
			rawPolicy: []byte(`{
			 "spec": {
      "containers": [
        {
          "(name)": "*",
          "securityContext": {
              "+(allowPrivilegeEscalation)": false,
              "+(capabilities)": {
                  "drop": [
                      "NET_CAP"
                  ]
              },
              "+(privileged)": false
          }
        }
      ],
      "initContainers": [
        {
          "(name)": "*",
          "securityContext": {
              "+(allowPrivilegeEscalation)": false,
              "+(capabilities)": {
                  "drop": [
                      "NET_ADMIN"
                  ]
              },
              "+(privileged)": false
          }
        }
      ]
    }
			}`),
			rawResource: []byte(`{
    "apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
				  "name": "mutation-debug",
      "namespace": "amritapuri"
				},
				"spec": {
     "containers": [
       {
        "name": "sleepy-container-1",
        "image": "docker.io/library/ubuntu"
       },
       {
        "name": "sleepy-container-2",
        "image": "docker.io/library/ubuntu"
       }
     ],
     "initContainers": [
       {
        "name": "init-container-1",
        "image": "docker.io/library/ubuntu"
       },
       {
        "name": "init-container-2",
        "image": "docker.io/library/ubuntu"
       }
     ]
				}
			}`),
			expectedPatch: []byte(`{
				"spec": {
				  "containers": [
        {
          "name": "sleepy-container-1",
          "securityContext": {
            "allowPrivilegeEscalation": false,
            "capabilities": {
              "drop": [
                "NET_CAP"
              ]
            },
            "privileged": false
          }
        },
        {
         "name": "sleepy-container-2",
         "securityContext": {
           "allowPrivilegeEscalation": false,
           "capabilities": {
             "drop": [
               "NET_CAP"
             ]
           },
           "privileged": false
         }
       }
      ],
      "initContainers": [
        {
          "name": "init-container-1",
          "securityContext": {
            "allowPrivilegeEscalation": false,
            "capabilities": {
              "drop": [
                "NET_ADMIN"
              ]
            },
            "privileged": false
          }
        },
        {
         "name": "init-container-2",
         "securityContext": {
           "allowPrivilegeEscalation": false,
           "capabilities": {
             "drop": [
               "NET_ADMIN"
             ]
           },
           "privileged": false
         }
       }
      ]
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"metadata": {
				  "annotations": {
					"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": "true"
				  }
				},
				"spec": {
				  "volumes": [
					{
					  "<(emptyDir)": {}
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
				  "name": "static-web",
				  "labels": {
					"role": "myrole"
				  }
				},
				"spec": {
				  "containers": [
					{
					  "name": "web",
					  "image": "1nginx"
					}
				  ],
				  "volumes": [
					{
					  "emptyDir": {},
					  "name": "cache-volume"
					},
					{
					  "secret": {
						"secretName": "default-token-6gplg"
					  },
					  "name": "default-token-6gplg"
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"metadata": {
				  "annotations": {
					"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"
				  }
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"metadata": null
			  }`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
				  "name": "hello"
				},
				"spec": {
				  "containers": [
					{
					  "name": "hello",
					  "image": "busybox"
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"metadata": null
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"spec": {
				  "containers": [
					{
					  "(name)": "*",
					  "image": "gcr.io/google-containers/busybox:latest"
					}
				  ],
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
				  "name": "hello"
				},
				"spec": {
				  "containers": [
					{
					  "name": "hello",
					  "image": "busybox"
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"spec": {
				  "containers": [
					{
						"name": "hello",
						"image": "gcr.io/google-containers/busybox:latest"
					}
				  ],
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"spec": {
				  "containers": [
					{
					  "(name)": "*",
					  "(image)": "gcr.io/google-containers/busybox:*",
					  "new_filed": "must be inserted"
					}
				  ],
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
				  "name": "hello2"
				},
				"spec": {
				  "containers": [
					{
					  "name": "hello",
					  "image": "gcr.io/google-containers/busybox:latest"
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"spec": {
				  "containers": [{
					"name": "hello",
					"new_filed": "must be inserted"
				  }],
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"spec": {
				  "containers": [
					{
					  "(image)": "gcr.io/google-containers/busybox:latest"
					}
				  ],
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
				  "name": "hello2"
				},
				"spec": {
				  "containers": [
					{
					  "name": "hello",
					  "image": "gcr.io/google-containers/busybox:latest"
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"spec": {
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"spec": {
				  "containers": [
					{
					  "(image)": "gcr.io/google-containers/busybox:*"
					}
				  ],
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
				  "name": "hello2"
				},
				"spec": {
				  "containers": [
					{
					  "name": "hello",
					  "image": "gcr.io/google-containers/busybox:latest"
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"spec": {
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
		},
		{
			// only the third container does not match the given condition
			rawPolicy: []byte(`{
				"spec": {
				  "containers": [
					{
					  "(image)": "gcr.io/google-containers/busybox:*"
					}
				  ],
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
				  "name": "hello"
				},
				"spec": {
				  "containers": [
					{
					  "name": "hello",
					  "image": "gcr.io/google-containers/busybox:latest"
					},
					{
					  "name": "hello2",
					  "image": "gcr.io/google-containers/busybox:latest"
					},
					{
					  "name": "hello3",
					  "image": "gcr.io/google-containers/nginx:latest"
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"spec": {
				  "imagePullSecrets": [
					{
					  "name": "regcred"
					}
				  ]
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"metadata": {
				  "annotations": {
					"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": true
				  },
				  "labels": {
					"+(add-labels)": "add"
				  }
				},
				"spec": {
				  "volumes": [
					{
					  "<(hostPath)": {
						"path": "*"
					  }
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"kind": "Pod",
				"apiVersion": "v1",
				"metadata": {
				  "name": "nginx"
				},
				"spec": {
				  "containers": [
					{
					  "name": "nginx",
					  "image": "nginx:latest",
					  "imagePullPolicy": "Never",
					  "volumeMounts": [
						{
						  "mountPath": "/cache",
						  "name": "cache-volume"
						}
					  ]
					}
				  ],
				  "volumes": [
					{
					  "name": "cache-volume",
					  "hostPath": {
						"path": "/data",
						"type": "Directory"
					  }
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"metadata": {
				  "annotations": {
					"cluster-autoscaler.kubernetes.io/safe-to-evict": true
				  },
				  "labels": {
					"add-labels": "add"
				  }
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"metadata": {
				  "annotations": {
					"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": true
				  }
				},
				"spec": {
				  "volumes": [
					{
					  "<(hostPath)": {
						"path": "*"
					  }
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"kind": "Pod",
				"apiVersion": "v1",
				"metadata": {
				  "name": "nginx",
				  "annotations": {
					"cluster-autoscaler.kubernetes.io/safe-to-evict": "false"
				  }
				},
				"spec": {
				  "containers": [
					{
					  "name": "nginx",
					  "image": "nginx:latest",
					  "imagePullPolicy": "Never",
					  "volumeMounts": [
						{
						  "mountPath": "/cache",
						  "name": "cache-volume"
						}
					  ]
					}
				  ],
				  "volumes": [
					{
					  "name": "cache-volume",
					  "hostPath": {
						"path": "/data",
						"type": "Directory"
					  }
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{}`),
		},
		{
			rawPolicy: []byte(`{
				"metadata": {
					"annotations": {
						"+(alb.ingress.kubernetes.io/backend-protocol)": "HTTPS",
						"+(alb.ingress.kubernetes.io/healthcheck-protocol)": "HTTPS",
						"+(alb.ingress.kubernetes.io/scheme)": "internal",
						"+(alb.ingress.kubernetes.io/target-type)": "ip",
						"+(kubernetes.io/ingress.class)": "alb"
					}
				}
			}`),
			rawResource: []byte(`{
				"apiVersion": "extensions/v1beta1",
				"kind": "Ingress",
				"metadata": {
				  "annotations": {
					"alb.ingress.kubernetes.io/backend-protocol": "HTTPS",
					"alb.ingress.kubernetes.io/healthcheck-protocol": "HTTPS",
					"alb.ingress.kubernetes.io/scheme": "internal",
					"alb.ingress.kubernetes.io/target-type": "ip",
					"external-dns.alpha.kubernetes.io/hostname": "argo",
					"kubernetes.io/ingress.class": "test"
				  },
				  "labels": {
					"app": "argocd-server",
					"app.kubernetes.io/name": "argocd-server"
				  },
				  "name": "argocd",
				  "namespace": "default"
				}
			  }`),
			expectedPatch: []byte(`{}`),
		},
		{
			rawPolicy: []byte(`{
			"spec": {
				"template": {
				   "spec": {
					  "containers": [
						 {
							"(name)": "*",
							"resources": {
							   "limits": {
								  "+(memory)": "300Mi",
								  "+(cpu)": "100"
							   }
							}
						 }
					  ]
				   }
				}
			 }
			}`),
			rawResource: []byte(`{
				"apiVersion": "apps/v1",
				"kind": "Deployment",
				"metadata": {
				   "name": "qos-demo",
				   "labels": {
					  "test": "qos"
				   }
				},
				"spec": {
				   "replicas": 1,
				   "selector": {
					  "matchLabels": {
						 "app": "nginx"
					  }
				   },
				   "template": {
					  "metadata": {
						 "labels": {
							"app": "nginx"
						 }
					  },
					  "spec": {
						 "containers": [
							{
							   "name": "nginx",
							   "image": "nginx:latest",
							   "resources": {
								  "limits": {
									 "cpu": "50m"
								  }
							   }
							}
						 ]
					  }
				   }
				}
			 }`),
			expectedPatch: []byte(`{
				"spec": {
				  "template": {
					"spec": {
					  "containers": [
						{
						  "resources": {
							"limits": {
							  "memory": "300Mi"
							}
						  },
						  "name": "nginx"
						}
					  ]
					}
				  }
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"metadata": {
				  "annotations": {
					"+(annotation1)": "atest1",
					"+(annotation2)": "atest2"
				  },
				  "labels": {
					"+(label1)": "test1"
				  }
				},
				"spec": {
				  "(volumes)": [
					{
					  "(hostPath)": {
						"path": "/var/run/docker.sock"
					  }
					}
				  ],
				  "containers": [
					{
					  "(image)": "*:latest",
					  "command": [
						"ls"
					  ],
					  "imagePullPolicy": "Always"
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
				  "annotations": {
					"annotation1": "atest2"
				  },
				  "labels": {
					"label1": "test2",
					"label2": "test2"
				  },
				  "name": "check-root-user"
				},
				"spec": {
				  "containers": [
					{
					  "command": [
						"ll"
					  ],
					  "image": "nginx:latest",
					  "imagePullPolicy": "Never",
					  "name": "nginx"
					},
					{
					  "image": "busybox:latest",
					  "imagePullPolicy": "Never",
					  "name": "busybox"
					}
				  ],
				  "volumes": [
					{
					  "hostPath": {
						"path": "/var/run/docker.sock"
					  },
					  "name": "test-volume"
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"metadata": {
				  "annotations": {
					"annotation2": "atest2"
				  }
				},
				"spec": {
				  "containers": [
					{
					  "command": [
						"ls"
					  ],
					  "imagePullPolicy": "Always",
					  "name": "nginx"
					},
					{
					  "command": [
						"ls"
					  ],
					  "imagePullPolicy": "Always",
					  "name": "busybox"
					}
				  ]
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"metadata": {
					"annotations": {
						"+(annotation1)": "atest1",
				  	}
				}
			}`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"annotations": {
						"annotation1": "atest2"
				  	},
				  	"labels": {
						"label1": "test2",
						"label2": "test2"
				  	},
				  	"name": "check-root-user"
				}
			}`),
			expectedPatch: []byte(`{}`),
		},
		{
			rawPolicy: []byte(`{
				"metadata": {
					"annotations": {
						"annotation1": null
				  	}
				}
			}`),
			rawResource: []byte(`{
				"apiVersion": "v1",
				"kind": "Pod",
				"metadata": {
					"annotations": {
						"annotation1": "atest2"
				  	},
				  	"labels": {
						"label1": "test2",
						"label2": "test2"
				  	},
				  	"name": "check-root-user"
				}
			}`),
			expectedPatch: []byte(`{
				"metadata": {
					"annotations": {
						"annotation1": null
				  	}
				}
			}`),
		},
		{
			rawPolicy: []byte(`{
				"metadata": {
				  "annotations": {
					"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": true
				  }
				},
				"spec": {
				  "volumes": [
					{
					  "hostPath": {
						"<(path)": "*data"
					  }
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"kind": "Pod",
				"apiVersion": "v1",
				"metadata": {
				  "name": "nginx"
				},
				"spec": {
				  "containers": [
					{
					  "name": "nginx",
					  "image": "nginx:latest",
					  "imagePullPolicy": "Never",
					  "volumeMounts": [
						{
						  "mountPath": "/cache",
						  "name": "cache-volume"
						}
					  ]
					}
				  ],
				  "volumes": [
					{
					  "name": "cache-volume",
					  "hostPath": {
						"path": "/data",
						"type": "Directory"
					  }
					}
				  ]
				}
			  }`),
			expectedPatch: []byte(`{
				"metadata": {
				  "annotations": {
					"cluster-autoscaler.kubernetes.io/safe-to-evict": true
				  }
				}
			  }`),
		},
		{
			rawPolicy: []byte(`{
				"spec": {
				  "securityContext": {
					"runAsNonRoot": true
				  },
				  "initContainers": [
					{
					  "(name)": "*",
					  "securityContext": {
						"runAsNonRoot": true
					  }
					}
				  ],
				  "containers": [
					{
					  "(name)": "*",
					  "securityContext": {
						"runAsNonRoot": true
					  }
					}
				  ]
				}
			  }`),
			rawResource: []byte(`{
				"spec":{
				   "initContainers":[
					  {
						 "name":"initbusy",
						 "image":"busybox:1.28",
						 "command":[
							"sleep",
							"9999"
						 ]
					  }
				   ],
				   "containers":[
					  {
						 "image":"busybox:1.28",
						 "name":"busybox",
						 "command":[
							"sleep",
							"9999"
						 ]
					  }
				   ],
				   "affinity":{
					  "podAntiAffinity":{
						 "requiredDuringSchedulingIgnoredDuringExecution":[
							{
							   "labelSelector":{
								  "matchExpressions":[
									 {
										"key":"app",
										"operator":"In",
										"values":[
										   "foo",
										   "bar"
										]
									 }
								  ]
							   },
							   "topologyKey":"kubernetes.io/hostname"
							}
						 ]
					  }
				   }
				}
			 }`),
			expectedPatch: []byte(`{
				"spec": {
				  "securityContext": {
					"runAsNonRoot": true
				  },
				  "initContainers": [
					{
					  "name": "initbusy",
					  "securityContext": {
						"runAsNonRoot": true
					  }
					}
				  ],
				  "containers": [
					{
					  "name": "busybox",
					  "securityContext": {
						"runAsNonRoot": true
					  }
					}
				  ]
				}
			  }`),
		},
	}

	for i, test := range testCases {
		t.Logf("Running test %d...", i)
		preProcessedPolicy, err := preProcessStrategicMergePatch(logr.Discard(), string(test.rawPolicy), string(test.rawResource))
		assert.NilError(t, err)

		output, err := preProcessedPolicy.MarshalJSON()
		assert.NilError(t, err)

		assert.DeepEqual(t, toJSON(t, []byte(test.expectedPatch)), toJSON(t, output))
	}
}

func toJSON(t *testing.T, b []byte) interface{} {
	var i interface{}
	err := json.Unmarshal(b, &i)
	assert.NilError(t, err)
	return i
}

func Test_FilterKeys_NoConditions(t *testing.T) {
	patternRaw := []byte(`{
		"key1": "value1",
		"key2": "value2"
	}`)

	pattern := yaml.MustParse(string(patternRaw))
	conditions, err := filterKeys(pattern, anchor.IsCondition)

	assert.NilError(t, err)
	assert.Equal(t, len(conditions), 0)
}

func Test_FilterKeys_ConditionsArePresent(t *testing.T) {
	patternRaw := []byte(`{
		"key1": "value1",
		"(key2)": "value2",
		"(key3)": "value3"
	}`)

	pattern := yaml.MustParse(string(patternRaw))
	conditions, err := filterKeys(pattern, anchor.IsCondition)

	assert.NilError(t, err)
	assert.Equal(t, len(conditions), 2)
	assert.Equal(t, conditions[0].String(), "(key2)")
	assert.Equal(t, conditions[1].String(), "(key3)")
}

func Test_FilterKeys_EmptyList(t *testing.T) {
	patternRaw := []byte(`{}`)
	pattern := yaml.MustParse(string(patternRaw))
	conditions, err := filterKeys(pattern, anchor.IsCondition)

	assert.NilError(t, err)
	assert.Equal(t, len(conditions), 0)
}

func Test_CheckConditionAnchor_Matches(t *testing.T) {
	patternRaw := []byte(`{ "key1": "value*" }`)
	resourceRaw := []byte(`{ "key1": "value1" }`)

	pattern := yaml.MustParse(string(patternRaw))
	resource := yaml.MustParse(string(resourceRaw))

	err := checkCondition(logr.Discard(), pattern, resource)
	assert.Equal(t, err, nil)
}

func Test_CheckConditionAnchor_DoesNotMatch(t *testing.T) {
	patternRaw := []byte(`{ "key1": "value*" }`)
	resourceRaw := []byte(`{ "key1": "sample" }`)

	pattern := yaml.MustParse(string(patternRaw))
	resource := yaml.MustParse(string(resourceRaw))

	err := checkCondition(logr.Discard(), pattern, resource)
	assert.Error(t, err, "resource value 'sample' does not match 'value*' at path /key1/")
}

func Test_ValidateConditions_MapWithOneCondition_Matches(t *testing.T) {
	patternRaw := []byte(`{
		"(key1)": "value*",
		"key2": "value2"
	}`)

	resourceRaw := []byte(`{
		"key1": "value1",
		"key2": "sample"
	}`)

	pattern := yaml.MustParse(string(patternRaw))
	resource := yaml.MustParse(string(resourceRaw))

	err := validateConditions(logr.Discard(), pattern, resource)
	assert.NilError(t, err)
}

func Test_ValidateConditions_MapWithOneCondition_DoesNotMatch(t *testing.T) {
	patternRaw := []byte(`{
		"(key1)": "value*",
		"key2": "value2"
	}`)

	resourceRaw := []byte(`{
		"key1": "text",
		"key2": "sample"
	}`)

	pattern := yaml.MustParse(string(patternRaw))
	resource := yaml.MustParse(string(resourceRaw))

	err := validateConditions(logr.Discard(), pattern, resource)
	_, ok := err.(ConditionError)
	assert.Assert(t, ok)
}

func Test_RenameField(t *testing.T) {
	patternRaw := []byte(`{
		"+(key1)": "value",
	}`)

	pattern := yaml.MustParse(string(patternRaw))
	renameField("+(key1)", "key1", pattern)

	actual := pattern.Field("key1").Value.YNode().Value
	expected := "value"

	fields, err := pattern.Fields()
	assert.NilError(t, err)

	assert.Equal(t, len(fields), 1)
	assert.Equal(t, actual, expected)
}

func Test_RenameField_NonExisting(t *testing.T) {
	patternRaw := []byte(`{
		"+(key1)": "value",
	}`)

	pattern := yaml.MustParse(string(patternRaw))
	renameField("non_existing_field", "key1", pattern)

	actual := pattern.Field("+(key1)").Value.YNode().Value
	expected := "value"

	fields, err := pattern.Fields()
	assert.NilError(t, err)

	assert.Equal(t, len(fields), 1)
	assert.Equal(t, actual, expected)
}

func Test_deleteRNode(t *testing.T) {
	patternRaw := []byte(`{
		"list": [
			"first": {
				"a": "b"
			},
			"second": {
				"a": "b"
			},
			"third": {
				"a": "b"
			},
		],
	}`)

	pattern := yaml.MustParse(string(patternRaw))
	list := pattern.Field("list").Value
	elements, err := list.Elements()
	assert.NilError(t, err)

	assert.Equal(t, len(elements), 3)
	deleteListElement(list, 0)

	elements, err = list.Elements()
	assert.NilError(t, err)
	assert.Equal(t, len(elements), 2)
}

func Test_DeleteConditions(t *testing.T) {
	patternRaw := []byte(`{
		"spec": {
		  "containers": [
			{
			  "(name)": "*",
			  "image": "gcr.io/google-containers/busybox:latest"
			},
			{
			  "image": "gcr.io/google-containers/busybox:latest",
			  "name": "hello"
			}
		  ],
		  "imagePullSecrets": [
			{
			  "name": "regcred"
			}
		  ]
		}
	  }`)

	pattern := yaml.MustParse(string(patternRaw))

	containers, err := pattern.Field("spec").Value.Field("containers").Value.Elements()
	assert.NilError(t, err)
	assert.Equal(t, len(containers), 2)

	_, err = deleteAnchors(pattern, false, false)
	assert.NilError(t, err)

	containers, err = pattern.Field("spec").Value.Field("containers").Value.Elements()
	assert.NilError(t, err)
	assert.Equal(t, len(containers), 1)

	name := containers[0].Field("name").Value.YNode().Value
	assert.Equal(t, name, "hello")
}

func Test_ConditionCheck_SeveralElementsMatchExceptOne(t *testing.T) {
	patternRaw := []byte(`{
		"containers": [
			{
			  "(name)": "hello?",
			  "image": "gcr.io/google-containers/busybox:1"
			}
		]
	}`)

	containersRaw := []byte(`{
		"containers": [
			{
			  "name": "hello",
			  "image": "gcr.io/google-containers/busybox:latest"
			},
			{
			  "name": "hello2",
			  "image": "gcr.io/google-containers/busybox:latest"
			},
			{
			  "name": "hello3",
			  "image": "gcr.io/google-containers/nginx:latest"
			}
		  ]
	}`)

	pattern := yaml.MustParse(string(patternRaw))
	containers := yaml.MustParse(string(containersRaw))

	err := preProcessPattern(logr.Discard(), pattern, containers)
	assert.NilError(t, err)

	patternContainers := pattern.Field("containers")
	assert.Assert(t, patternContainers != nil)
	assert.Assert(t, patternContainers.Value != nil)

	elements, err := patternContainers.Value.Elements()
	assert.NilError(t, err)

	assert.Equal(t, len(elements), 2)
}

func Test_NonExistingKeyMustFailPreprocessing(t *testing.T) {
	rawPattern := []byte(`{
			"metadata": {
				"labels": {
					"(key1)": "value1",
				}
			},
			"spec": {
			  "containers": [
				{
				  "name": "busybox",
				  "image": "gcr.io/google-containers/busybox:latest"
				}
			  ],
			  "imagePullSecrets": [
				{
				  "name": "regcred"
				}
			  ]
			}
		  }`)

	rawResource := []byte(`{
			"apiVersion": "v1",
			"kind": "Pod",
			"metadata": {
			  "name": "hello"
			},
			"spec": {
			  "containers": [
				{
				  "name": "hello",
				  "image": "busybox"
				}
			  ]
			}
		  }`)

	pattern := yaml.MustParse(string(rawPattern))
	resource := yaml.MustParse(string(rawResource))
	err := preProcessPattern(logr.Discard(), pattern, resource)
	assert.Error(t, err, "condition failed: could not found \"key1\" key in the resource")
}

func Test_NestedConditionals(t *testing.T) {
	rawPattern := `{"spec":{"template":{"spec":{"volumes":[{"(emptyDir)":{"+(sizeLimit)":"20Mi"},"name":"*"}]}}}}`
	rawResource := `{"spec":{"template":{"spec":{"volumes":[{"emptyDir":{},"name":"vol02"}]}}}}`
	expectedPattern := `{"spec":{"template":{"spec":{"volumes":[{"emptyDir":{"sizeLimit":"20Mi"},"name":"vol02"}]}}}}`

	pattern := yaml.MustParse(rawPattern)
	resource := yaml.MustParse(rawResource)
	err := preProcessPattern(logr.Discard(), pattern, resource)
	assert.NilError(t, err)
	resultPattern, _ := pattern.String()

	assert.DeepEqual(t, toJSON(t, []byte(expectedPattern)), toJSON(t, []byte(resultPattern)))
}

func Test_GlobalCondition_Fail(t *testing.T) {
	rawPattern := []byte(`{
		"metadata": {
			"annotations": {
				"+(cluster-autoscaler.kubernetes.io/safe-to-evict)": "true"
			}
		},
		"spec": {
			"volumes": [
				{
					"<(emptyDir)": {}
				}
			]
		}
	}`)
	rawResource := []byte(`{
		"apiVersion": "v1",
		"kind": "Pod",
		"metadata": {
			"name": "pod-without-emptydir-hostpath"
		},
		"spec": {
			"containers": [
				{
					"name": "nginx",
					"image": "nginx"
				}
			]
		}
	}`)

	pattern := yaml.MustParse(string(rawPattern))
	resource := yaml.MustParse(string(rawResource))
	err := preProcessPattern(logr.Discard(), pattern, resource)
	assert.Error(t, err, "global condition failed: could not found \"emptyDir\" key in the resource")
}
