<commit_before>/**
 * Copyright 2011-2012 eBusiness Information, Groupe Excilys (www.excilys.com)
 * Copyright 2012 Gilt Groupe, Inc. (www.gilt.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 		http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gatling.http.action

import java.io.IOException
import java.net.URI

import org.junit.runner.RunWith
import org.mockito.ArgumentMatcher
import org.mockito.Matchers.{ eq => isEq }
import org.mockito.Mockito.reset
import org.slf4j.LoggerFactory
import org.specs2.mock.Mockito
import org.specs2.mock.mockito.MocksCreation
import org.specs2.mutable.Specification
import org.specs2.runner.JUnitRunner
import org.specs2.specification.{ AllExpectations, Scope }

import com.ning.http.client.websocket.{ WebSocket, WebSocketListener }

import akka.actor.{ Actor, Props }
import akka.testkit.TestActorRef
import io.gatling.core.Predef.{ Session, stringToExpression }
import io.gatling.core.akka.GatlingActorSystem
import io.gatling.core.config.{ GatlingConfiguration, ProtocolRegistry }
import io.gatling.core.result.message.{ KO, OK }
import io.gatling.http.Predef.websocket
import io.gatling.http.util.{ RequestLogger, WebSocketClient }

@RunWith(classOf[JUnitRunner])
class WebSocketActorSpec extends Specification with AllExpectations with Mockito {

	step {
		// initialize logging to avoid substitute logger error messages
		LoggerFactory.getLogger(classOf[WebSocketActorSpec])
		// set up configuration to avoid NPEs constructing actors
		GatlingConfiguration.setUp()
		GatlingActorSystem.start
		success
	}

	"A WebSocketActor" should {

		"record a successful open and advance" in new scope {
			open(webSocketClient(_.onOpen(mock[WebSocket].smart)))

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(OK), anyLong, anyLong, isEq(null))
			next.underlyingActor.session.map(_.contains("testAttributeName")) mustEqual Some(true)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(false)
		}

		"record a failed open and advance" in new scope {
			open(mock[WebSocketClient].open(any[URI], any[WebSocketListener]) throws new IOException("testErrorMessage"))

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, anArgThat(contains("testErrorMessage")))
			next.underlyingActor.session.map(_.contains("testAttributeName")) mustEqual Some(false)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record an incomplete open as failed and advance" in new scope {
			open(webSocketClient(_.onError(new IOException("testException"))))

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, anArgThat(contains("testException")))
			next.underlyingActor.session.map(_.contains("testAttributeName")) mustEqual Some(false)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a close-before-open as failed and advance" in new scope {
			open(webSocketClient(_.onClose(mock[WebSocket].smart)))

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, any[Option[String]])
			next.underlyingActor.session.map(_.contains("testAttributeName")) mustEqual Some(false)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a successful send and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			openSuccessfully(webSocket)
			sendMessage("testMessage")

			there was one(webSocket).sendTextMessage(isEq("testMessage"))
			there was one(requestLogger).logRequest(any[Session], anyString, isEq(OK), anyLong, anyLong, isEq(null))

			next.underlyingActor.session.map(_.isFailed) mustEqual Some(false)
		}

		"record a send after an error as failed and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			reportError(webSocket, new IOException("testException"))
			sendMessage("testMessage")

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, anArgThat(contains("testException")))
			there was no(webSocket).sendTextMessage(anyString)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a send after an unexpected close as failed and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			closeUnexpectedly(webSocket)
			sendMessage("testMessage")

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, any[Option[String]])
			there was no(webSocket).sendTextMessage(anyString)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a successful close and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			openSuccessfully(webSocket)
			close()

			there was one(webSocket).close()
			there was one(requestLogger).logRequest(any[Session], anyString, isEq(OK), anyLong, anyLong, isEq(null))
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(false)
		}

		"record a close after an error as failed and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			reportError(webSocket, new IOException("testException"))
			close()

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, anArgThat(contains("testException")))
			there was no(webSocket).sendTextMessage(anyString)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a close after an unexpected close as failed and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			closeUnexpectedly(webSocket)
			close()

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, any[Option[String]])
			there was no(webSocket).sendTextMessage(anyString)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}
	}

	private trait scope extends Scope with MocksCreation {
		val requestLogger = mock[RequestLogger]
		var next: TestActorRef[DummyAction] = _

		def webSocketClient(open: (WebSocketListener) => Unit) = {
			mock[WebSocketClient].open(any[URI], any[WebSocketListener]) answers { (params, _) =>
				open(params.asInstanceOf[Array[_]](1).asInstanceOf[WebSocketListener])
			}
		}

		def open(webSocketClient: WebSocketClient) {
			next = TestActorRef[DummyAction](Props(new DummyAction))(GatlingActorSystem.instance)
			val action = websocket("testRequestName")
				.open("ws://dummy/", "testAttributeName")(webSocketClient, requestLogger)<commit_msg>Shutdown ActorSystem after tests<commit_after>/**
 * Copyright 2011-2012 eBusiness Information, Groupe Excilys (www.excilys.com)
 * Copyright 2012 Gilt Groupe, Inc. (www.gilt.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 		http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gatling.http.action

import java.io.IOException
import java.net.URI

import org.junit.runner.RunWith
import org.mockito.ArgumentMatcher
import org.mockito.Matchers.{ eq => isEq }
import org.mockito.Mockito.reset
import org.slf4j.LoggerFactory
import org.specs2.mock.Mockito
import org.specs2.mock.mockito.MocksCreation
import org.specs2.mutable.Specification
import org.specs2.runner.JUnitRunner
import org.specs2.specification.{ AllExpectations, Scope }

import com.ning.http.client.websocket.{ WebSocket, WebSocketListener }

import akka.actor.{ Actor, Props }
import akka.testkit.TestActorRef
import io.gatling.core.Predef.{ Session, stringToExpression }
import io.gatling.core.akka.GatlingActorSystem
import io.gatling.core.config.{ GatlingConfiguration, ProtocolRegistry }
import io.gatling.core.result.message.{ KO, OK }
import io.gatling.http.Predef.websocket
import io.gatling.http.util.{ RequestLogger, WebSocketClient }

@RunWith(classOf[JUnitRunner])
class WebSocketActorSpec extends Specification with AllExpectations with Mockito {

	step {
		// initialize logging to avoid substitute logger error messages
		LoggerFactory.getLogger(classOf[WebSocketActorSpec])
		// set up configuration to avoid NPEs constructing actors
		GatlingConfiguration.setUp()
		GatlingActorSystem.start
		success
	}

	"A WebSocketActor" should {

		"record a successful open and advance" in new scope {
			open(webSocketClient(_.onOpen(mock[WebSocket].smart)))

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(OK), anyLong, anyLong, isEq(null))
			next.underlyingActor.session.map(_.contains("testAttributeName")) mustEqual Some(true)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(false)
		}

		"record a failed open and advance" in new scope {
			open(mock[WebSocketClient].open(any[URI], any[WebSocketListener]) throws new IOException("testErrorMessage"))

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, anArgThat(contains("testErrorMessage")))
			next.underlyingActor.session.map(_.contains("testAttributeName")) mustEqual Some(false)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record an incomplete open as failed and advance" in new scope {
			open(webSocketClient(_.onError(new IOException("testException"))))

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, anArgThat(contains("testException")))
			next.underlyingActor.session.map(_.contains("testAttributeName")) mustEqual Some(false)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a close-before-open as failed and advance" in new scope {
			open(webSocketClient(_.onClose(mock[WebSocket].smart)))

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, any[Option[String]])
			next.underlyingActor.session.map(_.contains("testAttributeName")) mustEqual Some(false)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a successful send and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			openSuccessfully(webSocket)
			sendMessage("testMessage")

			there was one(webSocket).sendTextMessage(isEq("testMessage"))
			there was one(requestLogger).logRequest(any[Session], anyString, isEq(OK), anyLong, anyLong, isEq(null))

			next.underlyingActor.session.map(_.isFailed) mustEqual Some(false)
		}

		"record a send after an error as failed and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			reportError(webSocket, new IOException("testException"))
			sendMessage("testMessage")

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, anArgThat(contains("testException")))
			there was no(webSocket).sendTextMessage(anyString)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a send after an unexpected close as failed and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			closeUnexpectedly(webSocket)
			sendMessage("testMessage")

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, any[Option[String]])
			there was no(webSocket).sendTextMessage(anyString)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a successful close and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			openSuccessfully(webSocket)
			close()

			there was one(webSocket).close()
			there was one(requestLogger).logRequest(any[Session], anyString, isEq(OK), anyLong, anyLong, isEq(null))
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(false)
		}

		"record a close after an error as failed and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			reportError(webSocket, new IOException("testException"))
			close()

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, anArgThat(contains("testException")))
			there was no(webSocket).sendTextMessage(anyString)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}

		"record a close after an unexpected close as failed and advance" in new scope {
			val webSocket = mock[WebSocket].smart
			closeUnexpectedly(webSocket)
			close()

			there was one(requestLogger).logRequest(any[Session], anyString, isEq(KO), anyLong, anyLong, any[Option[String]])
			there was no(webSocket).sendTextMessage(anyString)
			next.underlyingActor.session.map(_.isFailed) mustEqual Some(true)
		}
	}

	step {
		GatlingActorSystem.shutdown
	}

	private trait scope extends Scope with MocksCreation {
		val requestLogger = mock[RequestLogger]
		var next: TestActorRef[DummyAction] = _

		def webSocketClient(open: (WebSocketListener) => Unit) = {
			mock[WebSocketClient].open(any[URI], any[WebSocketListener]) answers { (params, _) =>
				open(params.asInstanceOf[Array[_]](1).asInstanceOf[WebSocketListener])
			}
		}

		def open(webSocketClient: WebSocketClient) {
			next = TestActorRef[DummyAction](Props(new DummyAction))(GatlingActorSystem.instance)
			val action = websocket("testRequestName")
				.open("ws://dummy/", "testAttributeName")(webSocketClient, requestLogger)
{'fraction_non_alphanumeric': 0.10884402615130398, 'fraction_numerical': 0.003304835117465335, 'mean_word_length': 5.486486486486487, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1466097', 'n_tokens_mistral': 4528, 'n_tokens_neox': 4127, 'n_words': 1030}