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

# Interpreter Operator Compatibility Test Suite
# Tests that VM operators behave exactly like interpreter operators
# Focuses on edge cases like undefined value handling, division by zero, etc.

cases:
  # Undefined Value Arithmetic Tests
  - note: undefined_add
    description: Test addition with undefined value
    example_rego: "input.nonexistent + 5"
    input: {}  # Empty input to create undefined access
    literals:
      - "nonexistent"
      - 5
    instructions:
      - "LoadInput { dest: 0 }"              # Load input into register 0
      - "Load { dest: 1, literal_idx: 0 }"   # Load string "nonexistent" for indexing
      - "Load { dest: 2, literal_idx: 1 }"   # Load 5 into register 2
      - "Index { dest: 3, container: 0, key: 1 }"  # Access input.nonexistent (undefined)
      - "Add { dest: 4, left: 3, right: 2 }" # undefined + 5
      - "Return { value: 4 }"
    want_result: "#undefined"

  - note: undefined_sub
    description: Test subtraction with undefined value
    example_rego: "input.nonexistent - 5"
    input: {}
    literals:
      - "nonexistent"
      - 5
    instructions:
      - "LoadInput { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 0, key: 1 }"
      - "Sub { dest: 4, left: 3, right: 2 }"
      - "Return { value: 4 }"
    want_result: "#undefined"

  - note: undefined_mul
    description: Test multiplication with undefined value
    example_rego: "input.nonexistent * 5"
    input: {}
    literals:
      - "nonexistent"
      - 5
    instructions:
      - "LoadInput { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 0, key: 1 }"
      - "Mul { dest: 4, left: 3, right: 2 }"
      - "Return { value: 4 }"
    want_result: "#undefined"

  # Division by Zero Tests
  - note: division_by_zero
    description: Test division by zero returns undefined (non-strict mode)
    example_rego: "5 / 0"
    literals:
      - 5
      - 0
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Div { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: "#undefined"

  - note: modulo_by_zero
    description: Test modulo by zero returns undefined (non-strict mode)
    example_rego: "5 % 0"
    literals:
      - 5
      - 0
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Mod { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: "#undefined"

  # Modulo Float Tests
  - note: modulo_float
    description: Test modulo with floating point numbers (should error)
    example_rego: "5.5 % 2"
    literals:
      - 5.5
      - 2
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Mod { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_error: "modulo on floating-point number"

  # Undefined Comparison Tests
  - note: undefined_eq
    description: Test equality comparison with undefined value
    example_rego: "input.nonexistent == 5"
    input: {}
    literals:
      - "nonexistent"
      - 5
    instructions:
      - "LoadInput { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 0, key: 1 }"
      - "Eq { dest: 4, left: 3, right: 2 }"
      - "Return { value: 4 }"
    want_result: "#undefined"

  - note: undefined_ne
    description: Test inequality comparison with undefined value
    example_rego: "input.nonexistent != 5"
    input: {}
    literals:
      - "nonexistent"
      - 5
    instructions:
      - "LoadInput { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 0, key: 1 }"
      - "Ne { dest: 4, left: 3, right: 2 }"
      - "Return { value: 4 }"
    want_result: "#undefined"

  - note: undefined_lt
    description: Test less than comparison with undefined value
    example_rego: "input.nonexistent < 5"
    input: {}
    literals:
      - "nonexistent"
      - 5
    instructions:
      - "LoadInput { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 0, key: 1 }"
      - "Lt { dest: 4, left: 3, right: 2 }"
      - "Return { value: 4 }"
    want_result: "#undefined"

  - note: undefined_le
    description: Test less than or equal comparison with undefined value
    example_rego: "input.nonexistent <= 5"
    input: {}
    literals:
      - "nonexistent"
      - 5
    instructions:
      - "LoadInput { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 0, key: 1 }"
      - "Le { dest: 4, left: 3, right: 2 }"
      - "Return { value: 4 }"
    want_result: "#undefined"

  - note: undefined_gt
    description: Test greater than comparison with undefined value
    example_rego: "input.nonexistent > 5"
    input: {}
    literals:
      - "nonexistent"
      - 5
    instructions:
      - "LoadInput { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 0, key: 1 }"
      - "Gt { dest: 4, left: 3, right: 2 }"
      - "Return { value: 4 }"
    want_result: "#undefined"

  - note: undefined_ge
    description: Test greater than or equal comparison with undefined value
    example_rego: "input.nonexistent >= 5"
    input: {}
    literals:
      - "nonexistent"
      - 5
    instructions:
      - "LoadInput { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 0, key: 1 }"
      - "Ge { dest: 4, left: 3, right: 2 }"
      - "Return { value: 4 }"
    want_result: "#undefined"

  # Number Type Precision Tests
  - note: number_add_precision
    description: Test Number type addition preserves precision
    example_rego: "1.1 + 2.2"
    literals:
      - 1.1
      - 2.2
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Add { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: 3.3000000000000003

  - note: number_sub_precision
    description: Test Number type subtraction preserves precision
    example_rego: "5.5 - 2.2"
    literals:
      - 5.5
      - 2.2
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Sub { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: 3.3

  - note: number_mul_precision
    description: Test Number type multiplication preserves precision
    example_rego: "2.5 * 4.0"
    literals:
      - 2.5
      - 4.0
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Mul { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: 10.0

  - note: number_div_precision
    description: Test Number type division preserves precision
    example_rego: "7.5 / 2.5"
    literals:
      - 7.5
      - 2.5
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Div { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: 3.0

  # Integer Modulo Tests
  - note: integer_modulo
    description: Test integer modulo works correctly
    example_rego: "7 % 3"
    literals:
      - 7
      - 3
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Mod { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: 1

  # Value Ordering Tests
  - note: value_ordering_null_bool
    description: Test null < bool ordering
    example_rego: "null < true"
    literals:
      - null
      - true
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Lt { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: true

  - note: value_ordering_bool_number
    description: Test bool < number ordering
    example_rego: "true < 1"
    literals:
      - true
      - 1
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Lt { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: true

  - note: value_ordering_number_string
    description: Test number < string ordering
    example_rego: "1 < \"a\""
    literals:
      - 1
      - "a"
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Lt { dest: 2, left: 0, right: 1 }"
      - "Return { value: 2 }"
    want_result: true

  # Edge Cases for Mixed Undefined Operations
  - note: undefined_both_operands
    description: Test operation with both operands undefined
    example_rego: "input.nonexistent1 + input.nonexistent2"
    input: {}
    literals:
      - "nonexistent1"
      - "nonexistent2"
    instructions:
      - "LoadInput { dest: 0 }"
      - "Load { dest: 1, literal_idx: 0 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 0, key: 1 }"
      - "Index { dest: 4, container: 0, key: 2 }"
      - "Add { dest: 5, left: 3, right: 4 }"
      - "Return { value: 5 }"
    want_result: "#undefined"

  - note: undefined_right_operand_arithmetic
    description: Test arithmetic with right operand undefined
    example_rego: "5 + input.nonexistent"
    input: {}
    literals:
      - 5
      - "nonexistent"
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "LoadInput { dest: 1 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 1, key: 2 }"
      - "Add { dest: 4, left: 0, right: 3 }"
      - "Return { value: 4 }"
    want_result: "#undefined"

  - note: undefined_right_operand_comparison
    description: Test comparison with right operand undefined  
    example_rego: "5 == input.nonexistent"
    input: {}
    literals:
      - 5
      - "nonexistent"
    instructions:
      - "Load { dest: 0, literal_idx: 0 }"
      - "LoadInput { dest: 1 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "Index { dest: 3, container: 1, key: 2 }"
      - "Eq { dest: 4, left: 0, right: 3 }"
      - "Return { value: 4 }"
    want_result: "#undefined"
