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

# Null and Undefined Handling Test Suite
# Tests null and undefined behavior across all instruction families
# Covers arithmetic, comparisons, logical ops, indexing, loops, and comprehensions

cases:
  - note: load_null_basic
    description: LoadNull loads null value
    example_rego: "null"
    literals: []
    instructions:
      - "LoadNull { dest: 0 }"
      - "Return { value: 0 }"
    want_result: null

  - note: null_in_arithmetic_add
    description: Adding null is a type error
    example_rego: "null + 1"
    literals:
      - 1
    instructions:
      - "LoadNull { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Add { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_error: "Cannot add Null"

  - note: null_in_arithmetic_sub
    description: Subtracting null is a type error
    example_rego: "5 - null"
    literals:
      - 5
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "LoadNull { dest: 1 }"
      - "Sub { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_error: "Cannot subtract Number(5)"

  - note: null_in_arithmetic_mul
    description: Multiplying null is a type error
    example_rego: "null * 3"
    literals:
      - 3
    instructions:
      - "LoadNull { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Mul { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_error: "Cannot multiply Null"

  - note: null_in_comparison_eq
    description: null equals null
    example_rego: "null == null"
    literals: []
    instructions:
      - "LoadNull { dest: 0 }"
      - "LoadNull { dest: 1 }"
      - "Eq { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: true

  - note: null_not_equal_to_number
    description: null is not equal to numbers
    example_rego: "null == 0"
    literals:
      - 0
    instructions:
      - "LoadNull { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Eq { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: false

  - note: null_not_equal_to_false
    description: null is not equal to false
    example_rego: "null == false"
    literals: []
    instructions:
      - "LoadNull { dest: 0 }"
      - "LoadFalse { dest: 1 }"
      - "Eq { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: false

  - note: null_in_comparison_lt
    description: Ordering comparison treats null as less than numbers
    example_rego: "null < 5"
    literals:
      - 5
    instructions:
      - "LoadNull { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Lt { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: true

  - note: null_in_logical_and
    description: Logical AND treats null as truthy
    example_rego: "null && true"
    literals: []
    instructions:
      - "LoadNull { dest: 0 }"
      - "LoadTrue { dest: 1 }"
      - "And { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: true

  - note: null_in_logical_or
    description: Logical OR treats null as truthy
    example_rego: "null || false"
    literals: []
    instructions:
      - "LoadNull { dest: 0 }"
      - "LoadFalse { dest: 1 }"
      - "Or { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: true

  - note: null_in_logical_not
    description: Logical NOT treats null as truthy (returns false)
    example_rego: "not null"
    literals: []
    instructions:
      - "LoadNull { dest: 0 }"
      - "Not { dest: 1, operand: 0 }"
      - "Return { value: 1 }"
    want_result: false

  - note: null_as_array_index
    description: Indexing array with null key
    example_rego: "[1, 2, 3][null]"
    literals:
      - [1, 2, 3]
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "LoadNull { dest: 1 }"
      - "Index { dest: 2, container: 0, key: 1 }"
      - "Return { value: 2 }"
    want_result: "#undefined"  # null is not a valid array index

  - note: null_as_object_key
    description: Indexing object with null key
    example_rego: "{\"a\": 1, \"b\": 2}[null]"
    literals:
      - {"a": 1, "b": 2}
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "LoadNull { dest: 1 }"
      - "Index { dest: 2, container: 0, key: 1 }"
      - "Return { value: 2 }"
    want_result: "#undefined"  # null key doesn't exist

  - note: null_in_contains_check
    description: Contains check with null
    example_rego: "null in [1, 2, null, 3]"
    literals:
      - [1, 2, null, 3]
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "LoadNull { dest: 1 }"
      - "Contains { dest: 2, collection: 0, value: 1 }"
      - "Return { value: 2 }"
    want_result: true

  - note: null_in_set
    description: null can be a set member
    example_rego: "{1, null, 3}"
    literals:
      - 1
      - 3
    instructions:
      - "SetNew { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "SetAdd { set: 0, value: 1 }"
      - "LoadNull { dest: 2 }"
      - "SetAdd { set: 0, value: 2 }"
      - "Load { dest: 3, literal_idx: 1 }"
      - "SetAdd { set: 0, value: 3 }"
      - "Return { value: 0 }"
    want_result:
      set!:
        - 1
        - null
        - 3

  - note: null_in_array
    description: null can be an array element
    example_rego: "[1, null, 3]"
    literals:
      - 1
      - 3
    instruction_params:
      array_create_params:
        - dest: 0
          elements: [1, 2, 3]
    instructions:
      - "Load { dest: 1, literal_idx: 0 }"
      - "LoadNull { dest: 2 }"
      - "Load { dest: 3, literal_idx: 1 }"
      - "ArrayCreate { params_index: 0 }"
      - "Return { value: 0 }"
    want_result: [1, null, 3]

  - note: null_in_object_value
    description: null can be an object value
    example_rego: "{\"a\": 1, \"b\": null}"
    literals:
      - "a"
      - 1
      - "b"
      - {}
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 3
          literal_key_fields: []
          fields:
            - [1, 2]
            - [3, 4]
    instructions:
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Load { dest: 3, literal_idx: 2 }"
      - "LoadNull { dest: 4 }"
      - "ObjectCreate { params_index: 0 }"
      - "Return { value: 0 }"
    want_result: {"a": 1, "b": null}

  - note: undefined_in_arithmetic
    description: Arithmetic with undefined register produces undefined
    example_rego: "undefined_var + 1"
    literals:
      - 1
    instructions:
      # r0 is undefined (not loaded)
      - "Load { dest: 1, literal_idx: 0 }"
      - "Add { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: "#undefined"

  - note: undefined_in_comparison
    description: Comparison with undefined returns undefined
    example_rego: "undefined_var == 5"
    literals:
      - 5
    instructions:
      # r0 is undefined
      - "Load { dest: 1, literal_idx: 0 }"
      - "Eq { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: "#undefined"

  - note: undefined_in_logical_ops
    description: Logical operations with undefined return undefined
    example_rego: "undefined_var && true"
    literals: []
    instructions:
      # r0 is undefined
      - "LoadTrue { dest: 1 }"
      - "And { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: "#undefined"

  - note: undefined_indexing
    description: Indexing undefined returns undefined
    example_rego: "undefined_var[\"key\"]"
    literals:
      - "key"
    instructions:
      # r0 is undefined
      - "Load { dest: 1, literal_idx: 0 }"
      - "Index { dest: 2, container: 0, key: 1 }"
      - "Return { value: 2 }"
    want_result: "#undefined"

  - note: undefined_in_array_create
    description: ArrayCreate with undefined element returns undefined
    literals:
      - 1
      - 2
    instruction_params:
      array_create_params:
        - dest: 0
          elements: [1, 2, 3]  # r3 is undefined
    instructions:
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      # r3 is undefined
      - "ArrayCreate { params_index: 0 }"
      - "Return { value: 0 }"
    want_result: "#undefined"

  - note: undefined_in_object_create_key
    description: ObjectCreate with undefined key returns undefined
    literals:
      - 1
      - 2
      - {}
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 2
          literal_key_fields: []
          fields:
            - [1, 2]
            - [3, 4]
    instructions:
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      # r3, r4 are undefined
      - "ObjectCreate { params_index: 0 }"
      - "Return { value: 0 }"
    want_result: "#undefined"

  - note: undefined_in_object_create_value
    description: ObjectCreate with undefined value returns undefined
    literals:
      - "key1"
      - "key2"
      - 1
      - {}
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 3
          literal_key_fields: []
          fields:
            - [1, 3]
            - [2, 4]  # r4 is undefined
    instructions:
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Load { dest: 3, literal_idx: 2 }"
      # r4 is undefined
      - "ObjectCreate { params_index: 0 }"
      - "Return { value: 0 }"
    want_result: "#undefined"

  - note: undefined_in_loop_collection
    description: Loop over undefined collection fails gracefully
    literals: []
    instruction_params:
      loop_params:
        - mode: "ForEach"
          collection: 0  # r0 is undefined
          key_reg: 1
          value_reg: 2
          result_reg: 4
          body_start: 1
          loop_end: 3
    instructions:
      # r0 is undefined
      - "LoopStart { params_index: 0 }"
      # Loop body (never executed because collection is undefined)
      - "LoadTrue { dest: 3 }"
      - "LoopNext { body_start: 1, loop_end: 3 }"
      - "Return { value: 3 }"
    want_result: "#undefined"  # Loop over undefined collection yields undefined

  - note: undefined_propagation_through_chain
    description: Undefined propagates through operation chain
    example_rego: "(undefined_var + 1) * 2"
    literals:
      - 1
      - 2
    instructions:
      # r0 is undefined
      - "Load { dest: 1, literal_idx: 0 }"
      - "Add { dest: 2, left: 0, right: 1 }"  # r2 becomes undefined
      - "Load { dest: 3, literal_idx: 1 }"
      - "Mul { dest: 4, left: 2, right: 3 }"  # r4 becomes undefined
      - "Return { value: 4 }"
    want_result: "#undefined"