Module: Rooibos::TestHelper

Includes:
RatatuiRuby::TestHelper
Defined in:
lib/rooibos/test_helper.rb

Overview

Assertions and test utilities for Rooibos applications.

Custom commands run in background threads. Forgetting to include Command::Custom causes cryptic Ractor errors. Validating protocol compliance manually is tedious.

This module provides Rooibos-specific assertions. It also includes RatatuiRuby::TestHelper, giving you access to with_test_terminal, inject_key, etc.

Use it in Minitest classes to validate commands and control test terminals.

Example

class TestMyApp < Minitest::Test
  include Rooibos::TestHelper

  def test_app_exits_on_ctrl_c
    with_test_terminal do
      inject_key(:ctrl_c)
      Rooibos.run(MyApp)
    end
  end
end

Constant Summary collapse

ClearView =

Simple view that just clears the terminal

-> (_model, tui) { tui.clear }
ExitOnQUpdate =

Update that exits on ‘q’ key, passes through all other messages

-> (msg, model) do
  case msg
  when RatatuiRuby::Event::Key
    (msg.code == "q") ? [model, Rooibos::Command.exit] : [model, nil]
  else
    [model, nil]
  end
end
ExitOnAnyKeyUpdate =

Update that exits immediately on any key

-> (_msg, model) { [model, Rooibos::Command.exit] }

Instance Method Summary collapse

Instance Method Details

#assert_no_errors(messages, msg = nil) ⇒ Object

Fails if any Message::Error is present in the messages array.

Call after running the runtime and before asserting on expected messages. This ensures tests fail fast with helpful error messages instead of silently passing when errors occur.

messages

Array of messages collected from the update function.

msg

Optional custom failure message prefix.

Example

def test_dashboard_loads_data
  messages = []
  update = -> (msg, m) do
    # ... handle keys ...
    messages << msg
    [m, nil]
  end

  with_test_terminal do
    inject_key("s")
    inject_sync
    inject_key("q")
    Rooibos::Runtime.run(model:, view:, update:)
  end

  assert_no_errors(messages)
  # ... rest of assertions
end


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rooibos/test_helper.rb', line 105

def assert_no_errors(messages, msg = nil)
  error = messages.find { |m| m.is_a?(Rooibos::Message::Error) }
  return unless error

  error_detail = "#{error.exception.class}: #{error.exception.message}"
  failure_msg = msg ? "#{msg}\n#{error_detail}" : "Unexpected Message::Error: #{error_detail}"

  if respond_to?(:flunk)
    # rubocop:disable Style/SendWithLiteralMethodName
    public_send(:flunk, failure_msg)
    # rubocop:enable Style/SendWithLiteralMethodName
  else
    raise failure_msg
  end
end

#validate_rooibos_command!(command) ⇒ Object

Validates a command implements the Rooibos command protocol.

Custom commands run in background threads. They dispatch work and send messages. Forgetting to include <tt>Command::Custom</tt> breaks dispatch. The runtime treats <tt>[model, bad_command]</tt> as a model, not a tuple. Tests fail with confusing Ractor shareability errors.

This method checks the protocol. Call it in tests to catch mistakes early.

command

The command object to validate.

Example

def test_websocket_command_protocol
  cmd = WebSocketCommand.new("wss://example.com")
  validate_rooibos_command!(cmd)
end


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/rooibos/test_helper.rb', line 55

def validate_rooibos_command!(command)
  unless command.respond_to?(:rooibos_command?)
    raise Rooibos::Error::Invariant,
      "#{command.class} does not respond to #rooibos_command?. " \
        "Include Command::Custom or implement the rooibos_command? predicate."
  end

  unless command.respond_to?(:call)
    raise Rooibos::Error::Invariant,
      "#{command.class} does not respond to #call. " \
        "Implement call(out, token) to execute the command."
  end

  unless command.respond_to?(:rooibos_cancellation_grace_period)
    raise Rooibos::Error::Invariant,
      "#{command.class} does not respond to #rooibos_cancellation_grace_period. " \
        "Include Command::Custom or implement this method."
  end
end