# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# Count Expressions Test Suite
# Tests field count and value count with optional where clauses and name bindings.

cases:
  # =========================================================================
  # Field count — direct path (core subset, no alias resolution)
  # =========================================================================

  - note: field_count_direct_path_core
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "securityRules[*]"
          },
          "greater": 2
        },
        "then": { "effect": "deny" }
      }
    resource:
      securityRules:
        - { "name": "r1" }
        - { "name": "r2" }
        - { "name": "r3" }
    want_effect: "deny"

  - note: field_count_direct_path_where_core
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "securityRules[*]",
            "where": {
              "field": "securityRules[*].access",
              "equals": "Allow"
            }
          },
          "equals": 2
        },
        "then": { "effect": "audit" }
      }
    resource:
      securityRules:
        - { "access": "Allow" }
        - { "access": "Deny" }
        - { "access": "Allow" }
    want_effect: "audit"

  # =========================================================================
  # Field count — basic
  # =========================================================================

  - note: field_count_basic
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "securityRules[*]"
          },
          "greater": 10
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "Microsoft.Network/networkSecurityGroups"
      securityRules:
        - { "name": "r1" }
        - { "name": "r2" }
        - { "name": "r3" }
        - { "name": "r4" }
        - { "name": "r5" }
        - { "name": "r6" }
        - { "name": "r7" }
        - { "name": "r8" }
        - { "name": "r9" }
        - { "name": "r10" }
        - { "name": "r11" }
    want_effect: "deny"

  - note: field_count_equals_zero
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "storageProfile.dataDisks[*]"
          },
          "equals": 0
        },
        "then": { "effect": "audit" }
      }
    resource:
      type: "Microsoft.Compute/virtualMachines"
    want_effect: "audit"

  # =========================================================================
  # Field count — with where clause
  # =========================================================================

  - note: field_count_with_where
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "securityRules[*]",
            "where": {
              "field": "securityRules[*].access",
              "equals": "Allow"
            }
          },
          "greater": 5
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "Microsoft.Network/networkSecurityGroups"
      securityRules:
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
    want_effect: "deny"

  - note: field_count_where_allOf
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "securityRules[*]",
            "where": {
              "allOf": [
                {
                  "field": "securityRules[*].access",
                  "equals": "Allow"
                },
                {
                  "field": "securityRules[*].direction",
                  "equals": "Inbound"
                }
              ]
            }
          },
          "greaterOrEquals": 1
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "Microsoft.Network/networkSecurityGroups"
      securityRules:
        - { "access": "Allow", "direction": "Inbound" }
        - { "access": "Deny", "direction": "Outbound" }
    want_effect: "deny"

  - note: field_count_where_anyOf
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "securityRules[*]",
            "where": {
              "anyOf": [
                {
                  "field": "securityRules[*].destinationPortRange",
                  "equals": "22"
                },
                {
                  "field": "securityRules[*].destinationPortRange",
                  "equals": "3389"
                }
              ]
            }
          },
          "notEquals": 0
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "Microsoft.Network/networkSecurityGroups"
      securityRules:
        - { "destinationPortRange": "22" }
        - { "destinationPortRange": "443" }
    want_effect: "deny"

  - note: field_count_where_not
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "securityRules[*]",
            "where": {
              "not": {
                "field": "securityRules[*].access",
                "equals": "Deny"
              }
            }
          },
          "greater": 0
        },
        "then": { "effect": "audit" }
      }
    resource:
      type: "Microsoft.Network/networkSecurityGroups"
      securityRules:
        - { "access": "Allow" }
        - { "access": "Deny" }
    want_effect: "audit"

  # =========================================================================
  # Value count
  # =========================================================================

  - note: value_count_basic
    policy_rule: |
      {
        "if": {
          "count": {
            "value": ["eastus", "westus", "centralus"]
          },
          "equals": 3
        },
        "then": { "effect": "audit" }
      }
    resource:
      type: "any"
    want_effect: "audit"

  - note: value_count_with_name
    policy_rule: |
      {
        "if": {
          "count": {
            "value": ["eastus", "westus", "centralus"],
            "name": "location"
          },
          "greater": 0
        },
        "then": { "effect": "audit" }
      }
    resource:
      type: "any"
    want_effect: "audit"

  - note: value_count_with_name_and_where
    policy_rule: |
      {
        "if": {
          "count": {
            "value": ["eastus", "westus", "centralus", "northeurope"],
            "name": "loc",
            "where": {
              "value": "[current('loc')]",
              "like": "*us"
            }
          },
          "equals": 3
        },
        "then": { "effect": "audit" }
      }
    resource:
      type: "any"
    want_effect: "audit"

  - note: value_count_expression
    policy_rule: |
      {
        "if": {
          "count": {
            "value": "[parameters('allowedLocations')]",
            "name": "loc"
          },
          "greater": 0
        },
        "then": { "effect": "audit" }
      }
    parameters:
      allowedLocations:
        - "eastus"
        - "westus"
    resource:
      type: "any"
    want_effect: "audit"

  # =========================================================================
  # Count in allOf/anyOf
  # =========================================================================

  - note: count_in_allOf
    policy_rule: |
      {
        "if": {
          "allOf": [
            { "field": "type", "equals": "Microsoft.Network/networkSecurityGroups" },
            {
              "count": {
                "field": "securityRules[*]",
                "where": {
                  "field": "securityRules[*].access",
                  "equals": "Allow"
                }
              },
              "greater": 10
            }
          ]
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "Microsoft.Network/networkSecurityGroups"
      securityRules:
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
        - { "access": "Allow" }
    want_effect: "deny"

  - note: count_in_not
    policy_rule: |
      {
        "if": {
          "not": {
            "count": {
              "field": "storageProfile.dataDisks[*]"
            },
            "lessOrEquals": 4
          }
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "Microsoft.Compute/virtualMachines"
      storageProfile:
        dataDisks:
          - { "name": "d1" }
          - { "name": "d2" }
          - { "name": "d3" }
          - { "name": "d4" }
          - { "name": "d5" }
    want_effect: "deny"

  # =========================================================================
  # Count with nested where containing count
  # =========================================================================

  - note: value_count_nested_where
    policy_rule: |
      {
        "if": {
          "count": {
            "value": "[parameters('requiredTags')]",
            "name": "tag",
            "where": {
              "field": "[concat('tags[', current('tag'), ']')]",
              "exists": true
            }
          },
          "notEquals": "[length(parameters('requiredTags'))]"
        },
        "then": { "effect": "deny" }
      }
    parameters:
      requiredTags:
        - "environment"
        - "costCenter"
    resource:
      type: "Microsoft.Compute/virtualMachines"
      tags:
        environment: "prod"
    want_effect: "deny"

  # =========================================================================
  # Alias field refs inside count resolve to current loop element
  # =========================================================================

  - note: field_count_multiple_alias_refs_same_element
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "securityRules[*]",
            "where": {
              "allOf": [
                { "field": "securityRules[*].access", "equals": "Allow" },
                { "field": "securityRules[*].direction", "equals": "Inbound" },
                { "field": "securityRules[*].protocol", "equals": "Tcp" }
              ]
            }
          },
          "equals": 1
        },
        "then": { "effect": "audit" }
      }
    resource:
      securityRules:
        - { "access": "Allow", "direction": "Inbound", "protocol": "Tcp" }
        - { "access": "Allow", "direction": "Outbound", "protocol": "Tcp" }
        - { "access": "Deny", "direction": "Inbound", "protocol": "Tcp" }
    want_effect: "audit"

  - note: field_count_nested_field_access
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "storageProfile.dataDisks[*]",
            "where": {
              "field": "storageProfile.dataDisks[*].managedDisk.storageAccountType",
              "notEquals": "Premium_LRS"
            }
          },
          "greater": 0
        },
        "then": { "effect": "deny" }
      }
    resource:
      storageProfile:
        dataDisks:
          - { "name": "d1", "managedDisk": { "storageAccountType": "Premium_LRS" } }
          - { "name": "d2", "managedDisk": { "storageAccountType": "Standard_LRS" } }
    want_effect: "deny"

  - note: field_count_where_zero_matches
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "items[*]",
            "where": {
              "field": "items[*].status",
              "equals": "failed"
            }
          },
          "equals": 0
        },
        "then": { "effect": "audit" }
      }
    resource:
      items:
        - { "status": "ok" }
        - { "status": "ok" }
        - { "status": "ok" }
    want_effect: "audit"

  # =========================================================================
  # Nested count: count inside another count's where clause
  # =========================================================================

  - note: nested_field_and_value_count
    policy_rule: |
      {
        "if": {
          "count": {
            "value": "[parameters('requiredPorts')]",
            "name": "port",
            "where": {
              "count": {
                "field": "securityRules[*]",
                "where": {
                  "allOf": [
                    { "field": "securityRules[*].destinationPortRange", "equals": "[current('port')]" },
                    { "field": "securityRules[*].access", "equals": "Allow" }
                  ]
                }
              },
              "greater": 0
            }
          },
          "equals": "[length(parameters('requiredPorts'))]"
        },
        "then": { "effect": "audit" }
      }
    parameters:
      requiredPorts:
        - "443"
        - "80"
    resource:
      securityRules:
        - { "destinationPortRange": "443", "access": "Allow" }
        - { "destinationPortRange": "80", "access": "Allow" }
        - { "destinationPortRange": "22", "access": "Deny" }
    want_effect: "audit"

  - note: nested_field_and_value_count_fail
    policy_rule: |
      {
        "if": {
          "count": {
            "value": "[parameters('requiredPorts')]",
            "name": "port",
            "where": {
              "count": {
                "field": "securityRules[*]",
                "where": {
                  "allOf": [
                    { "field": "securityRules[*].destinationPortRange", "equals": "[current('port')]" },
                    { "field": "securityRules[*].access", "equals": "Allow" }
                  ]
                }
              },
              "greater": 0
            }
          },
          "equals": "[length(parameters('requiredPorts'))]"
        },
        "then": { "effect": "deny" }
      }
    parameters:
      requiredPorts:
        - "443"
        - "80"
        - "8080"
    resource:
      securityRules:
        - { "destinationPortRange": "443", "access": "Allow" }
        - { "destinationPortRange": "80", "access": "Allow" }
        - { "destinationPortRange": "22", "access": "Deny" }
    want_effect: ~

  # =========================================================================
  # current() — zero-arg form (innermost count element)
  # =========================================================================

  - note: current_zero_arg_value_count
    policy_rule: |
      {
        "if": {
          "count": {
            "value": ["Allow", "Allow", "Deny"],
            "name": "access",
            "where": {
              "value": "[current()]",
              "equals": "Allow"
            }
          },
          "equals": 2
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "any"
    want_effect: "deny"

  - note: current_zero_arg_field_count
    policy_rule: |
      {
        "if": {
          "count": {
            "field": "items[*]",
            "where": {
              "value": "[current()]",
              "equals": "yes"
            }
          },
          "equals": 2
        },
        "then": { "effect": "deny" }
      }
    resource:
      items: ["yes", "no", "yes"]
    want_effect: "deny"

  - note: current_zero_arg_with_function
    policy_rule: |
      {
        "if": {
          "count": {
            "value": ["HELLO", "WORLD"],
            "name": "word",
            "where": {
              "value": "[startsWith(current(), 'HE')]",
              "equals": true
            }
          },
          "equals": 1
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "any"
    want_effect: "deny"

  - note: current_zero_arg_nested_innermost
    policy_rule: |
      {
        "if": {
          "count": {
            "value": ["a", "b"],
            "name": "outer",
            "where": {
              "count": {
                "value": ["x", "y"],
                "name": "inner",
                "where": {
                  "value": "[current()]",
                  "equals": "x"
                }
              },
              "greater": 0
            }
          },
          "greater": 0
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "any"
    want_effect: "deny"
