mq-lang 0.5.26

Core language implementation for mq query language
Documentation
# Testing framework for mq
# A simple testing framework to execute test functions and output results

# Verifies that a condition is true and raises an error if it's false.
def assert(cond):
  if (cond):
    self
  else:
    {"error": true, "message": "Assertion failed: It is not true."}
end

# Renders inline diff parts for a delete line (shows equal + deleted chars, highlights deleted)
def _render_inline_delete(inline):
  join(foreach (part, inline):
    let pv = gsub(gsub(part["value"], " ", "·"), "\n", "↵")
    | if (part["tag"] == "delete"):
        "\x1b[1;31m" + pv + "\x1b[0;31m"
      elif (part["tag"] == "equal"):
        "\x1b[31m" + pv + "\x1b[0;31m"
      else:
        ""
  end, "")
end

# Renders inline diff parts for an insert line (shows equal + inserted chars, highlights inserted)
def _render_inline_insert(inline):
  join(foreach (part, inline):
    let pv = gsub(gsub(part["value"], " ", "·"), "\n", "↵")
    | if (part["tag"] == "insert"):
        "\x1b[1;32m" + pv + "\x1b[0;32m"
      elif (part["tag"] == "equal"):
        "\x1b[32m" + pv + "\x1b[0;32m"
      else:
        ""
  end, "")
end

# Verifies that two values are equal
def assert_eq(actual, expected):
  if (actual == expected):
    self
  else:
    do
      let is_array_diff = is_array(expected) && is_array(actual)
      | let diff_res = _diff(expected, actual)
      | let diff_lines = foreach (change, diff_res):
              let tag = change["tag"]
              | let value = change["value"]
              | let text = if (is_string(value)):
                              do
                                let s = gsub(gsub(value, "\n", ""), " ", "·")
                                | if (is_array_diff):
                                    "\"" + s + "\""
                                  else:
                                    s
                              end
                            else:
                              to_string(value)
              | let rendered = if (contains(change, "inline") && not(is_array_diff)):
                  if (tag == "delete"):
                    _render_inline_delete(change["inline"])
                  else:
                    _render_inline_insert(change["inline"])
                else:
                  text
              | if (tag == "insert"):
                  "\x1b[32m    + \x1b[0m" + rendered + "\x1b[0m"
                elif (tag == "delete"):
                  "\x1b[31m    - \x1b[0m" + rendered + "\x1b[0m"
                else:
                  "      " + text
            end
      | let diff_str = join(diff_lines, "\n")
      | {"error": true, "message": "\x1b[1mAssertion failed\x1b[0m\n\n" + diff_str}
    end
end

# Verifies that two values are not equal
def assert_ne(actual, expected):
  if (actual != expected):
    self
  else:
    {"error": true, "message": "Assertion failed: Expected values to be different, but both were " + to_string(actual)}
end

# Verifies that a value is true
def assert_true(value):
  if (value == true):
    self
  else:
    {"error": true, "message": "Assertion failed: Expected true\n    but got " + to_string(value)}
end

# Verifies that a value is false
def assert_false(value):
  if (value == false):
    self
  else:
    {"error": true, "message": "Assertion failed: Expected false\n    but got " + to_string(value)}
end

# Verifies that a value is None
def assert_none(value):
  if (is_none(value)):
    self
  else:
    {"error": true, "message": "Assertion failed: Expected None\n    but got " + to_string(value)}
end

# Verifies that a value is not None
def assert_not_none(value):
  if (not(is_none(value))):
    self
  else:
    {"error": true, "message": "Assertion failed: Expected non-None value but got None"}
end

# Verifies that an array contains a specific value
def assert_contains(array, value):
  if (in(array, value)):
    self
  else:
    {"error": true, "message": "Assertion failed: Array does not contain " + to_string(value)}
end

# Verifies that an array has a specific length
def assert_len(array, expected_length):
  let actual_length = len(array)
  | if (actual_length == expected_length):
      self
    else:
      {"error": true, "message": "Assertion failed: Expected length " + to_string(expected_length) + " but got " + to_string(actual_length)}
end

# Verifies that an array is empty
def assert_empty(array):
  if (is_empty(array)):
    self
  else:
    {"error": true, "message": "Assertion failed: Expected empty array but got " + to_string(array)}
end

# Verifies that an array is not empty
def assert_not_empty(array):
  if (not(is_empty(array))):
    self
  else:
    {"error": true, "message": "Assertion failed: Expected non-empty array but got empty array"}
end

# Test execution helper functions

# Runs a single test function and captures the result
def _run_test(test_name, test_func):
  let test_count = 1
  | let start_time = now()
  | let result = test_func()
  | let end_time = now()
  | let duration = end_time - start_time
  | if (is_dict(result) && contains(result, "error")):
      {
        "name": test_name,
        "status": "failed",
        "duration": duration,
        "error": result["message"]
      }
    else:
      {
        "name": test_name,
        "status": "passed",
        "duration": duration,
        "error": None
      }
end

def _succeed(message):
  "\x1b[32m" + message + "\x1b[0m"
end

def _failed(message):
  "\x1b[31m" + message + "\x1b[0m"
end

# Formats duration in seconds with fixed width padding
def _format_duration(duration_ms):
  let duration_s = duration_ms / 1000
  | let duration_str = to_string(duration_s)
  | let duration_str = if (contains(duration_str, ".")):
      duration_str
    else:
      duration_str + ".0"
  | let duration_split = split(duration_str, ".")
  | let duration_str = if (len(duration_split[1]) == 1):
      duration_str + "00"
    elif (len(duration_split[1]) == 2):
      duration_str + "0"
    else:
      duration_str
  # Pad to fixed width: [   X.XXXs]
  | let padding_needed = 8 - len(duration_str)
  | let padding = if (padding_needed > 0):
      repeat(" ", padding_needed)
    else:
      ""
  | "[" + padding + duration_str + "s]"
end

# Executes multiple test functions
def run_tests(tests):
  let final_test_count = len(tests)

  # Print starting message
  | let _ = print("    Starting " + to_string(final_test_count) + " tests")

  # Run each test and print result immediately
  | let test_results = foreach (test, tests):
      if (is_dict(test) && contains(test, "name") && contains(test, "func")):
        do
          let result = _run_test(get(test, "name"), get(test, "func"))
          | let duration_str = _format_duration(result["duration"])
          | let line = if (result["status"] == "passed"):
              _succeed("        PASS") + " " + duration_str + " " + result["name"]
            else:
              _failed("        FAIL") + " " + duration_str + " " + result["name"]
          | let _ = print(line)
          | result
        end
      else:
        error("Invalid test format: each test must be a dict with 'name' and 'func' keys")
    end

  | let final_failed_count = len(filter(test_results, fn(r): r["status"] == "failed";))
  | let passed_count = final_test_count - final_failed_count
  | let total_duration = fold(map(test_results, fn(r): r["duration"];), 0, fn(acc, d): acc + d;)

  # Print failures section if any
  | let _ = if (final_failed_count > 0):
      do
        let failures = filter(test_results, fn(r): r["status"] == "failed";)
        | let failure_msgs = ["\n\x1b[1mFailures:\x1b[0m\n"]
        | let failure_msgs = failure_msgs + foreach (failure, failures):
                  ["---- " + failure["name"] + " ----", "    " + failure["error"], ""]
                end
        | print(join(flatten(failure_msgs), "\n"))
      end
    else:
      self

  # Print summary
  | let summary_duration = _format_duration(total_duration)
  | let summary = "\x1b[1m------------\x1b[0m"
  | let summary = summary + "\n\x1b[1m     Summary " + summary_duration + " " + to_string(final_test_count) + " tests run: " + to_string(passed_count) + " passed"
  | let summary = if (final_failed_count > 0):
      summary + ", " + to_string(final_failed_count) + " failed"
    else:
      summary
  | let summary = summary + "\x1b[0m"
  | let _ = print(summary)

  # Exit with error code if tests failed
  | if (final_failed_count > 0): halt(1)
end

# Helper function to create a test case
def test_case(name, func):
  {
    "name": name,
    "func": func
  }
end