Wednesday, July 15, 2026 4:01:55 AM
> fake_connection.rb
# frozen_string_literal: true

class WebsocketClient
  #
  # Subclass of `ESM::Websocket` (which serves double duty as the connections registry and the
  # connection-wrapper class) that bypasses the Faye/EM stack. Production
  # `ESM::Websocket#initialize` calls `authorize!`, `bind_events`, and `on_open` against a
  # Faye::WebSocket server connection. We don't have one in tests, and faking it is more complex
  # than just skipping it.
  #
  # Instead, we set the ivars the base class expects (`@server`, `@requests`, `@ready`, `@closed`)
  # directly and implement `deliver!` synchronously.
  #
  class FakeConnection < ESM::Websocket
    attr_reader :client, :last_request

    #
    # @param server [ESM::Server] the AR server this connection represents
    # @param client [WebsocketClient] the test client paired with this
    #   connection. The bot's outgoing requests are forwarded to this client
    #   for response handling.
    #
    def initialize(server:, client:)
      # Intentionally NOT calling super. Base initialize requires a Faye connection object that we
      # don't have.
      @server = server
      @client = client
      @requests = ESM::Websocket::Queue.new
      @ready = false
      @closed = false

      ESM::Websocket.add_connection(self)
    end

    #
    # Override of {ESM::Websocket::Connection#deliver!}. Instead of pushing
    # the request to a real Faye client over the wire, hand the JSON
    # directly to the test client which will synthesise a response.
    #
    # @param request [ESM::Websocket::Request]
    #
    # @return [ESM::Websocket::Request] the same request, for chaining
    #
    def deliver!(request)
      @requests << request
      @last_request = request
      @client.receive_from_bot(request.to_s)
      request
    end

    #
    # Test helper: simulate the "Arma server" sending a message TO the bot.
    # Drives the same `ServerRequest#process` pipeline production uses,
    # synchronously (no `Thread.new`) so specs see effects immediately.
    #
    # @param json [String]
    #
    def simulate_arma_message(json)
      message = json.to_ostruct
      server_request = ESM::Websocket::ServerRequest.new(connection: self, message: message)

      if server_request.invalid?
        server_request.remove_request if server_request.remove_on_ignore?
        return
      end

      @server.reload
      ESM::Database.with_connection { server_request.process }
    rescue => e
      ESM.logger.error("WebsocketClient::FakeConnection#simulate_arma_message") do
        "#{e.message}\n#{e.backtrace[0..5].join("\n")}"
      end
      raise e
    end

    #
    # No-op overrides. The base implementations would touch the Faye connection that we don't have.
    #
    def ping; end
  end
end
All opinions represented herein are my own
- © 2024 - 2026 itsthedevman
- build 43aa0d7