require 'test_helper'

class ServerTest < Test::Unit::TestCase
  include JsChatHelpers

  def setup
    @jschat = JsChatMock.new
    @jschat.post_init
  end

  def teardown
    @jschat.reset
  end

  def test_identify
    response = JSON.parse @jschat.receive_line({ 'identify' => 'alex' }.to_json)
    assert_equal 'identified', response['display']
  end

  def test_invalid_identify
    response = JSON.parse @jschat.receive_line({ 'identify' => '@lex' }.to_json)
    assert_equal JsChat::Errors::Codes.invert[:invalid_name], response['error']['code']
  end

  def test_identify_twice_fails
    identify_as 'alex'
    result = JSON.parse identify_as('alex')
    assert_equal 106, result['error']['code']
  end

  def test_ensure_nicks_are_longer_than_0
    result = JSON.parse identify_as('')
    assert result['error']
  end

  def test_ensure_nicks_are_unique
    identify_as 'alex'

    # Obvious duplicate
    result = identify_as 'alex'
    assert result['error']

    # Case
    result = identify_as 'Alex'
    assert result['error']
  end

  def test_invalid_room_name
    identify_as 'bob'
    response = JSON.parse @jschat.receive_line({ 'join' => 'oublinet' }.to_json)
    assert_equal 'Invalid room name', response['error']['message']
  end

  def test_join
    identify_as 'bob'
    expected = { 'display' => 'join', 'join' => { 'user' => 'bob', 'room' => '#oublinet' } }.to_json + "\n"
    assert_equal expected, @jschat.receive_line({ 'join' => '#oublinet' }.to_json)
  end

  def test_join_without_identifying
    response = JSON.parse @jschat.receive_line({ 'join' => '#oublinet' }.to_json)
    assert response['error']
  end

  def test_join_more_than_once
    identify_as 'bob'

    expected = { 'display' => 'error', 'error' => { 'message' => 'Already in that room' } }.to_json + "\n"
    @jschat.receive_line({ 'join' => '#oublinet' }.to_json)
    response = JSON.parse @jschat.receive_line({ 'join' => '#oublinet' }.to_json)
    assert_equal JsChat::Errors::Codes.invert[:already_joined], response['error']['code']
  end

  def test_names
    identify_as 'nick', '#oublinet'

    # Add a user
    @jschat.add_user 'alex', '#oublinet'

    response = JSON.parse(@jschat.receive_line({ 'names' => '#oublinet' }.to_json))
    assert response['names']
  end

  def test_valid_names
    user = JsChat::User.new nil
    ['alex*', "alex\n"].each do |name|
      assert_raises JsChat::Errors::InvalidName do
        user.name = name
      end
    end
  end

  def test_message_not_in_room
    identify_as 'nick', '#oublinet'
    @jschat.add_user 'alex', '#oublinet'
    response = JSON.parse @jschat.receive_line({ 'send' => 'hello', 'to' => '#merk' }.to_json)
    assert_equal 'Please join this room first', response['error']['message']
  end

  def test_message
    identify_as 'nick', '#oublinet'
    @jschat.add_user 'alex', '#oublinet'
    assert @jschat.receive_line({ 'send' => 'hello', 'to' => '#oublinet' }.to_json)
  end

  def test_message_ignores_case
    identify_as 'nick', '#oublinet'
    @jschat.add_user 'alex', '#oublinet'
    response = @jschat.receive_line({ 'send' => 'hello', 'to' => '#Oublinet' }.to_json)
    assert response
  end

  def test_part
    identify_as 'nick', '#oublinet'
    @jschat.add_user 'alex', '#oublinet'
    response = JSON.parse @jschat.receive_line({ 'part' => '#oublinet'}.to_json)
    assert_equal '#oublinet', response['part']['room']
  end

  def test_private_message
    identify_as 'nick'
    @jschat.add_user 'alex', '#oublinet'
    response = JSON.parse @jschat.receive_line({ 'send' => 'hello', 'to' => 'alex' }.to_json)
    assert_equal 'hello', response['message']['message']
  end

  def test_private_message_ignores_case
    identify_as 'nick'
    @jschat.add_user 'alex', '#oublinet'
    response = JSON.parse @jschat.receive_line({ 'send' => 'hello', 'to' => 'Alex' }.to_json)
    assert_equal 'hello', response['message']['message']
  end

  def test_log_request
    identify_as 'nick', '#oublinet'
    @jschat.receive_line({ 'send' => 'hello', 'to' => '#oublinet' }.to_json)
    response = JSON.parse @jschat.receive_line({ 'lastlog' => '#oublinet' }.to_json)
    assert_equal 'hello', response['messages'].last['message']['message']
  end

  def test_name_change
    identify_as 'nick', '#oublinet'
    @jschat.add_user 'alex', '#oublinet'
    response = JSON.parse @jschat.receive_line({ 'change' => 'user', 'user' => { 'name' => 'bob' }}.to_json)
    assert_equal 'notice', response['display']
  end

  def test_name_change_duplicate
    identify_as 'nick', '#oublinet'
    @jschat.add_user 'alex', '#oublinet'
    response = JSON.parse @jschat.receive_line({ 'change' => 'user', 'user' => { 'name' => 'alex' }}.to_json)
    assert_equal 'error', response['display']
  end

  def test_max_message_length
    identify_as 'nick', '#oublinet'
    response = JSON.parse @jschat.receive_line({ 'send' => 'a' * 1000, 'to' => '#oublinet' }.to_json)
    assert response['error']
  end

  def test_flood_protection
    identify_as 'nick', '#oublinet'
    response = ''
    # simulate a flood and extract the error response
    (1..50).detect do
      response = @jschat.receive_line({ 'send' => 'a' * 10, 'to' => '#oublinet' }.to_json)
      response.match /error/
    end
    response = JSON.parse response
    assert response['error']
    assert_match /wait a few seconds/i, response['error']['message']
  end

end

