package ioc.specs

import org.apache.tapestry5.ioc.internal.ToStringService
import org.apache.tapestry5.ioc.internal.UpcaseService
import org.apache.tapestry5.ioc.services.LoggingDecorator
import org.slf4j.Logger
import org.xml.sax.SAXParseException

interface AdderService {

  long add(long operand1, long operand2);
}

interface ExceptionService {

  void parse() throws SAXParseException;
}

class LoggingDecoratorImplSpec extends AbstractSharedRegistrySpecification {

  LoggingDecorator decorator = getService LoggingDecorator

  Logger logger = Mock()

  def "logging of void method"() {

    _ * logger.debugEnabled >> true

    Runnable delegate = Mock()

    Runnable interceptor = decorator.build(Runnable, delegate, "foo.Bar", logger)

    when:

    interceptor.run()

    then:

    1 * logger.debug("[ENTER] run()")

    then:

    1 * delegate.run()

    then:

    1 * logger.debug("[ EXIT] run")

    interceptor.toString() == "<Logging interceptor for foo.Bar(java.lang.Runnable)>"
  }

  def "runtime exception inside method is logged"() {
    _ * logger.debugEnabled >> true

    Runnable delegate = Mock()

    Runnable interceptor = decorator.build(Runnable, delegate, "foo.Bar", logger)

    def t = new RuntimeException("From delegate.")

    when:

    interceptor.run()

    then:

    1 * logger.debug("[ENTER] run()")

    then:

    1 * delegate.run() >> {
      throw t
    }

    then:

    1 * logger.debug("[ FAIL] run -- ${RuntimeException.name}", t)

    then:

    RuntimeException e = thrown()

    e.is t
  }

  def "method throws checked exception"() {
    Throwable t = new SAXParseException("From delegate.", null)
    _ * logger.debugEnabled >> true
    ExceptionService delegate = Mock()

    ExceptionService service = decorator.build(ExceptionService, delegate, "MyService", logger)

    when:

    service.parse()

    then:

    Throwable actual = thrown()

    actual.is(t)

    1 * logger.debug("[ENTER] parse()")

    1 * delegate.parse() >> { throw t }

    1 * logger.debug("[ FAIL] parse -- ${SAXParseException.name}", t)
  }

  def "handling of object parameter and return type"() {
    _ * logger.debugEnabled >> true

    UpcaseService delegate = Mock()

    UpcaseService service = decorator.build(UpcaseService, delegate, "MyService", logger)

    when:

    assert service.upcase("barney") == "BARNEY"

    then:

    1 * logger.debug('[ENTER] upcase("barney")')

    1 * delegate.upcase(_) >> { args -> args[0].toUpperCase() }

    1 * logger.debug('[ EXIT] upcase ["BARNEY"]')
  }

  def "handling of primitive parameter and return type"() {
    _ * logger.debugEnabled >> true

    AdderService delegate = Mock()

    AdderService service = decorator.build(AdderService, delegate, "Adder", logger)

    when:

    assert service.add(6, 13) == 19

    then:

    1 * logger.debug("[ENTER] add(6, 13)")

    1 * delegate.add(_, _) >> { args -> args[0] + args[1] }

    1 * logger.debug("[ EXIT] add [19]")
  }

  def "toString() method of service interface is delegated"() {
    _ * logger.debugEnabled >> true

    // Spock's Mocking doesn't seem to be as savvy as Tapestry's about letting toString()
    // delegate through, so we can't implement ToStringService as a Mock

    ToStringService delegate = new ToStringService() {

      String toString() { "FROM DELEGATE" }
    }

    ToStringService service = decorator.build(ToStringService, delegate, "ToString", logger)

    when:

    assert service.toString() == "FROM DELEGATE"

    then:

    1 * logger.debug("[ENTER] toString()")
    1 * logger.debug('[ EXIT] toString ["FROM DELEGATE"]')
  }

}
