#region License

/*
 * Copyright © 2002-2007 the original author or authors.
 *
 * 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.
 */

#endregion

#region Imports

using System;
using System.Collections;
using System.Data;
using NHibernate;
using NHibernate.Cfg;
using NUnit.Framework;
using Rhino.Mocks;
using Spring.Dao;
using Spring.Data.Common;
using Spring.Data.Support;
using Spring.Support;
using Spring.Transaction;
using Spring.Transaction.Support;

#endregion

namespace Spring.Data.NHibernate
{
    /// <summary>
    /// This class contains tests for the HibernateTransactionManager
    /// </summary>
    /// <author>Mark Pollack</author>
    [TestFixture]
    public class HibernateTransactionManagerTests
    {
        private MockRepository mocks;

        [SetUp]
        public void Setup()
        {
            mocks = new MockRepository();
        }

        [Test]
        public void TransactionCommit()
        {
            IDbProvider provider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider));
            IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
            ISession session = (ISession)mocks.CreateMock(typeof(ISession));
            ITransaction transaction = (ITransaction) mocks.CreateMock(typeof (ITransaction));
            IQuery query = (IQuery) mocks.CreateMock(typeof (IQuery));

            IList list = new ArrayList();
            list.Add("test");
            using (mocks.Ordered())
            {
                Expect.Call(sessionFactory.OpenSession()).Return(session);
                Expect.Call(session.Connection).Return(connection);
                Expect.Call(session.BeginTransaction(IsolationLevel.Serializable)).Return(transaction);
                Expect.Call(session.IsOpen).Return(true);
                Expect.Call(session.CreateQuery("some query string")).Return(query);
                Expect.Call(query.List()).Return(list);
                transaction.Commit();
                LastCall.On(transaction).Repeat.Once();
                Expect.Call(session.Close()).Return(null);
            }

            mocks.ReplayAll();


            LocalSessionFactoryObjectStub lsfo = new LocalSessionFactoryObjectStub(sessionFactory);
            lsfo.AfterPropertiesSet();

            ISessionFactory sfProxy = lsfo.GetObject() as ISessionFactory;
            Assert.IsNotNull(sfProxy);

            HibernateTransactionManager tm = new HibernateTransactionManager();
            tm.AdoExceptionTranslator = new FallbackExceptionTranslator();
            tm.SessionFactory = sfProxy;
            tm.DbProvider = provider;
            TransactionTemplate tt = new TransactionTemplate(tm);

            tt.TransactionIsolationLevel = IsolationLevel.Serializable;

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sfProxy),"Hasn't thread session");
            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
            Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
            Assert.IsTrue(!TransactionSynchronizationManager.ActualTransactionActive, "Actual transaction not active");

            object result = tt.Execute(new TransactionCommitTxCallback(sfProxy, provider));

            Assert.IsTrue(result == list, "Incorrect result list");

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sfProxy), "Hasn't thread session");
            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
            Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");
            Assert.IsTrue(!TransactionSynchronizationManager.ActualTransactionActive, "Actual transaction not active");


            mocks.VerifyAll();

        }


        [Test]
        public void TransactionRollback()
        {

            IDbProvider provider = (IDbProvider)mocks.CreateMock(typeof(IDbProvider));
            IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
            ISession session = (ISession)mocks.CreateMock(typeof(ISession));
            ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));

            using (mocks.Ordered())
            {
                Expect.Call(sessionFactory.OpenSession()).Return(session);
                Expect.Call(session.Connection).Return(connection);
                Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
                Expect.Call(session.IsOpen).Return(true);

                transaction.Rollback();
                LastCall.On(transaction).Repeat.Once();

                Expect.Call(session.Close()).Return(null);
            }
            mocks.ReplayAll();

            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            TransactionTemplate tt = new TransactionTemplate(tm);

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
            Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");

            try
            {
                tt.Execute(new TransactionRollbackTxCallback(sessionFactory));
                Assert.Fail("Should have thrown exception");
            } catch (ArgumentException)
            {
                
            }

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
            
            mocks.VerifyAll();
        }

        [Test]
        public void TransactionRollbackOnly()
        {            
            IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
            ISession session = (ISession)mocks.CreateMock(typeof(ISession));
            ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));

            using (mocks.Ordered())
            {
                Expect.Call(sessionFactory.OpenSession()).Return(session);
                Expect.Call(session.Connection).Return(connection);
                Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
                Expect.Call(session.IsOpen).Return(true);
                Expect.Call(session.FlushMode).Return(FlushMode.Auto);
                session.Flush();
                LastCall.On(session).Repeat.Once();
                transaction.Rollback();
                LastCall.On(transaction).Repeat.Once();
                Expect.Call(session.Close()).Return(null);
            }
            mocks.ReplayAll();


            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            TransactionTemplate tt = new TransactionTemplate(tm);

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");

            tt.Execute(new TransactionRollbackOnlyTxCallback(sessionFactory));

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
            
            mocks.VerifyAll();
            
        }

        [Test]
        public void ParticipatingTransactionWithCommit()
        {
            IDbConnection connection = (IDbConnection) mocks.CreateMock(typeof (IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory) mocks.CreateMock(typeof (ISessionFactory));
            ISession session = (ISession) mocks.CreateMock(typeof (ISession));
            ITransaction transaction = (ITransaction) mocks.CreateMock(typeof (ITransaction));

            using (mocks.Ordered())
            {
                Expect.Call(sessionFactory.OpenSession()).Return(session);
                Expect.Call(session.Connection).Return(connection);
                Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
                Expect.Call(session.IsOpen).Return(true);
                Expect.Call(session.FlushMode).Return(FlushMode.Auto);
                session.Flush();
                LastCall.On(session).Repeat.Once();
                transaction.Commit();
                LastCall.On(transaction).Repeat.Once();
                Expect.Call(session.Close()).Return(null);
            }

            mocks.ReplayAll();

            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            TransactionTemplate tt = new TransactionTemplate(tm);
            IList list = new ArrayList();
            list.Add("test");

            object result = tt.Execute(new ParticipatingTransactionWithCommitTxCallback(sessionFactory, list));
            Assert.IsTrue(result == list);

            mocks.VerifyAll();

        }

        [Test]
        public void ParticipatingTransactionWithRollback()
        {
            IDbConnection connection = (IDbConnection) mocks.CreateMock(typeof (IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory) mocks.CreateMock(typeof (ISessionFactory));
            ISession session = (ISession) mocks.CreateMock(typeof (ISession));
            ITransaction transaction = (ITransaction) mocks.CreateMock(typeof (ITransaction));

            using (mocks.Ordered())
            {
                Expect.Call(sessionFactory.OpenSession()).Return(session);
                Expect.Call(session.Connection).Return(connection);
                Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
                Expect.Call(session.IsOpen).Return(true);
                Expect.Call(session.FlushMode).Return(FlushMode.Auto);

                transaction.Rollback();
                LastCall.On(transaction).Repeat.Once();
                Expect.Call(session.Close()).Return(null);
            }
            mocks.ReplayAll();


            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            TransactionTemplate tt = new TransactionTemplate(tm);
            try
            {
                tt.Execute(new ParticipatingTransactionWithRollbackTxCallback(sessionFactory));
                Assert.Fail("Should have thrown exception");
            }
            catch (ArgumentException)
            {

            }

            mocks.VerifyAll();
        }

        [Test]
        public void ParticipatingTransactionWithRollbackOnly()
        {
            IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
            ISession session = (ISession)mocks.CreateMock(typeof(ISession));
            ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));

            using (mocks.Ordered())
            {
                Expect.Call(sessionFactory.OpenSession()).Return(session);
                Expect.Call(session.Connection).Return(connection);
                Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
                Expect.Call(session.IsOpen).Return(true);

                transaction.Rollback();
                LastCall.On(transaction).Repeat.Once();
                Expect.Call(session.Close()).Return(null);
            }
            mocks.ReplayAll();


            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            TransactionTemplate tt = new TransactionTemplate(tm);
            IList list = new ArrayList();
            list.Add("test");
            try
            {
                tt.Execute(new ParticipatingTransactionWithRollbackOnlyTxCallback(tt,sessionFactory,list));
                Assert.Fail("Should have thrown UnexpectedRollbackException");
            }
            catch (UnexpectedRollbackException)
            {

            }

            mocks.VerifyAll();
        }

        [Test]
        public void ParticipatingTransactionWithWithRequiresNew()
        {
            IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
            ISession session1 = (ISession)mocks.CreateMock(typeof(ISession));
            ISession session2 = (ISession)mocks.CreateMock(typeof(ISession));
            ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));

            //using (mocks.Ordered())
            //{
                Expect.Call(sessionFactory.OpenSession()).Return(session1);
                Expect.Call(session1.Connection).Return(connection);
                Expect.Call(session1.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
                Expect.Call(session1.IsOpen).Return(true);

                Expect.Call(sessionFactory.OpenSession()).Return(session2);
                Expect.Call(session2.Connection).Return(connection);
                Expect.Call(session2.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
                Expect.Call(session2.IsOpen).Return(true);

                Expect.Call(session2.FlushMode).Return(FlushMode.Auto);
                session2.Flush();
                LastCall.On(session2).Repeat.Once();

                transaction.Commit();
                LastCall.On(transaction).Repeat.Twice();

                Expect.Call(session1.Close()).Return(null);
                Expect.Call(session2.Close()).Return(null);
            //}

            mocks.ReplayAll();

            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            TransactionTemplate tt = new TransactionTemplate(tm);
            tt.PropagationBehavior = TransactionPropagation.RequiresNew;

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");

            tt.Execute(new ParticipatingTransactionWithWithRequiresNewTxCallback(tt, sessionFactory));

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
            
            mocks.VerifyAll();

        }

        [Test]
        public void ParticipatingTransactionWithWithNotSupported()
        {
            IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
            ISession session = (ISession)mocks.CreateMock(typeof(ISession));
            ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));

            //using (mocks.Ordered())
            //{
                Expect.Call(sessionFactory.OpenSession()).Return(session).Repeat.Twice();
                Expect.Call(session.Connection).Return(connection);


                Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
                Expect.Call(session.IsOpen).Return(true);
                Expect.Call(session.FlushMode).Return(FlushMode.Auto).Repeat.Twice();
                session.Flush();
                LastCall.On(session).Repeat.Twice();
                transaction.Commit();
                LastCall.On(transaction).Repeat.Once();
#if NH_2_0
                Expect.Call(session.Close()).Return(null).Repeat.Once();
#else
                Expect.Call(session.Close()).Return(null).Repeat.Twice();
#endif
            //}
            mocks.ReplayAll();


            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            TransactionTemplate tt = new TransactionTemplate(tm);
            tt.PropagationBehavior = TransactionPropagation.RequiresNew;

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");

            tt.Execute(new ParticipatingTransactionWithWithNotSupportedTxCallback(tt, sessionFactory));

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");

            mocks.VerifyAll();


        }

        [Test]
        public void TransactionWithPropagationSupports()
        {
            ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
            ISession session = (ISession)mocks.CreateMock(typeof(ISession));

            Expect.Call(sessionFactory.OpenSession()).Return(session);
            Expect.Call(session.FlushMode).Return(FlushMode.Never);
            session.FlushMode = FlushMode.Auto;
            LastCall.IgnoreArguments();
            session.Flush();
            LastCall.IgnoreArguments();
            session.FlushMode = FlushMode.Never;
            Expect.Call(session.FlushMode).Return(FlushMode.Never);
#if NH_2_0
#else
            Expect.Call(session.Close()).Return(null);
#endif

            mocks.ReplayAll();


            LocalSessionFactoryObjectStub lsfo = new LocalSessionFactoryObjectStub(sessionFactory);
            lsfo.AfterPropertiesSet();
            ISessionFactory sfProxy = (ISessionFactory) lsfo.GetObject();
            Assert.IsNotNull(sfProxy);

            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            TransactionTemplate tt = new TransactionTemplate(tm);
            tt.PropagationBehavior = TransactionPropagation.Supports;

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");

            tt.Execute(new TransactionWithPropagationSupportsTxCallback(sessionFactory));

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");

            mocks.VerifyAll();

        }

        [Test]
        public void TransactionWithPropagationSupportsAndInnerTransaction()
        {
            IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
            ISession session1 = (ISession)mocks.CreateMock(typeof(ISession));
            ISession session2 = (ISession)mocks.CreateMock(typeof(ISession));
            ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));

            Expect.Call(sessionFactory.OpenSession()).Return(session1);
            Expect.Call(session1.Connection).Return(connection);
            Expect.Call(session1.SessionFactory).Return(sessionFactory);
            Expect.Call(session1.FlushMode).Return(FlushMode.Auto).Repeat.Twice();

            session1.Flush();
            LastCall.IgnoreArguments().Repeat.Twice();
            Expect.Call(session1.Close()).Return(null);

            Expect.Call(sessionFactory.OpenSession()).Return(session2);
            Expect.Call(session2.Connection).Return(connection).Repeat.Twice();
            Expect.Call(session2.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
            Expect.Call(session2.FlushMode).Return(FlushMode.Auto);
            session2.Flush();
            LastCall.IgnoreArguments();
            Expect.Call(session2.IsOpen).Return(true);


            transaction.Commit();
            LastCall.On(transaction).Repeat.Once();


            Expect.Call(session2.Close()).Return(null);

            mocks.ReplayAll();

            LocalSessionFactoryObjectStub lsfo = new LocalSessionFactoryObjectStub(sessionFactory);
            lsfo.AfterPropertiesSet();
            ISessionFactory sfProxy = (ISessionFactory)lsfo.GetObject();
            Assert.IsNotNull(sfProxy);

            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            
            TransactionTemplate tt = new TransactionTemplate(tm);
            tt.PropagationBehavior = TransactionPropagation.Supports;
            TransactionTemplate tt2 = new TransactionTemplate(tm);
            tt2.PropagationBehavior = TransactionPropagation.Required;

            HibernateTemplate ht = new HibernateTemplate(sessionFactory);
            ht.TemplateFlushMode = TemplateFlushMode.Eager;
            ht.ExposeNativeSession = true;

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");

            tt.Execute(new TransactionWithPropagationSupportsAndInnerTransactionTxCallback(tt2, sessionFactory, ht, session1, session2));

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
            
            mocks.ReplayAll();
            
        }


        [Test]
        public void TransactionCommitWithFlushFailure()
        {
            DoTransactionCommitWithFlushFailure(false);
        }

        [Test]
        public void TransactionCommitWithFlushFailureAndFallbackTranslation()
        {
            DoTransactionCommitWithFlushFailure(true);
        }

        /// <summary>
        /// Does the test transaction commit with flush failure.
        /// </summary>
        /// <param name="fallbackTranslation">if set to <c>true</c> if the exception throw
        /// is of the type NHibernate.ADOException, in which case HibernateTransactionManager
        /// will 'fallback' to using the error codes in the underlying exception thrown by
        /// the provider, ie. a SqlException, MySqlException.  Otherwise, if it is 
        /// another subclass of HibernateException, then perform a direct maping as 
        /// found in SessionFactoryUtils.ConvertHibernateAccessException.</param>
        private void DoTransactionCommitWithFlushFailure(bool fallbackTranslation)
        {
            #region Mock Setup

            IDbProvider provider = new TestDbProvider();
            IDbConnection connection = (IDbConnection)mocks.CreateMock(typeof(IDbConnection));
            ISessionFactory sessionFactory = (ISessionFactory)mocks.CreateMock(typeof(ISessionFactory));
            ISession session = (ISession)mocks.CreateMock(typeof(ISession));
            ITransaction transaction = (ITransaction)mocks.CreateMock(typeof(ITransaction));
            Exception rootCause = null;
            using (mocks.Ordered())
            {
                Expect.Call(sessionFactory.OpenSession()).Return(session);
                Expect.Call(session.Connection).Return(connection);
                Expect.Call(session.BeginTransaction(IsolationLevel.ReadCommitted)).Return(transaction);
                Expect.Call(session.IsOpen).Return(true);
                transaction.Commit();
                Exception sqlException = new TestSqlException("mymsg", "2627");
                if (fallbackTranslation)
                {
                    //error code 2627 will map to a DataAccessIntegrity exception in sqlserver, which is the metadata
                    //used by TestDbProvider.
                    rootCause = sqlException;
                    LastCall.On(transaction).Throw(new ADOException("mymsg", sqlException));
                }
                else
                {
#if NH_2_0
                    rootCause = new PropertyValueException("mymsg", typeof(string).Name, "Name");
#else
                    rootCause = new PropertyValueException("mymsg", typeof(string), "Name");
#endif
                    LastCall.On(transaction).Throw(rootCause);
                }

                transaction.Rollback();
                LastCall.On(transaction).Repeat.Once();
                Expect.Call(session.Close()).Return(null);
            }

            #endregion

            mocks.ReplayAll();


            HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
            tm.DbProvider = provider;

            TransactionTemplate tt = new TransactionTemplate(tm);
            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
            Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");

            IList list = new ArrayList();
            list.Add("test");
            try
            {
                tt.Execute(new TransactionCommitWithFlushFailureCallback(sessionFactory, list));
                Assert.Fail("Should have thrown DataIntegrityViolationException");
            }
            catch (DataIntegrityViolationException ex)
            {
                Assert.AreEqual(rootCause, ex.InnerException);
                Assert.IsTrue(ex.Message.IndexOf("mymsg") != -1);
            }

            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sessionFactory), "Hasn't thread session");
            Assert.IsTrue(!TransactionSynchronizationManager.SynchronizationActive, "Synchronizations not active");

            mocks.VerifyAll();


        }

    }



    #region Supporting classes for test TransactionCommit

    public class TransactionCommitTxCallback : ITransactionCallback
    {
        private ISessionFactory sfProxy;
        private IDbProvider provider;
        public TransactionCommitTxCallback(ISessionFactory sessionFactory, IDbProvider provider)
        {
            sfProxy = sessionFactory;
            this.provider = provider;
        }
        

        public object DoInTransaction(ITransactionStatus status)
        {
            Assert.IsTrue(TransactionSynchronizationManager.HasResource(sfProxy),"Has thread session");
            Assert.IsTrue(TransactionSynchronizationManager.HasResource(provider), "Hasn't thread db provider");
            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive);
            HibernateTemplate ht = new HibernateTemplate(sfProxy);
            return ht.Find("some query string");
        }
    }

    public class LocalSessionFactoryObjectStub  : LocalSessionFactoryObject
    {
        private ISessionFactory sf;
        public LocalSessionFactoryObjectStub(ISessionFactory sf)
        {
            this.sf = sf;
        }

        protected override ISessionFactory NewSessionFactory(Configuration config)
        {
            return sf;
        }
    }

    #endregion

    #region Supporting classes for test TransactionRollback

    public class TransactionRollbackTxCallback : ITransactionCallback
    {
        private ISessionFactory sf;
        public TransactionRollbackTxCallback(ISessionFactory sf)
        {
            this.sf = sf;
        }

        public object DoInTransaction(ITransactionStatus status)
        {
            Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf),"Has thread session");
            HibernateTemplate ht = new HibernateTemplate(sf);
            return ht.ExecuteFind(new ThrowExceptionHibernateCallback());
        }
    }

    public class ThrowExceptionHibernateCallback : IHibernateCallback
    {
        public object DoInHibernate(ISession session)
        {
            throw new ArgumentException("arg exception");
        }
    }

    #endregion

    #region Supporting classes for test TransactionRollbackOnly

    public class TransactionRollbackOnlyTxCallback : ITransactionCallback
    {
        private ISessionFactory sf;
        public TransactionRollbackOnlyTxCallback(ISessionFactory sf)
        {
            this.sf = sf;
        }


        public object DoInTransaction(ITransactionStatus status)
        {
            Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session");
            HibernateTemplate ht = new HibernateTemplate(sf);
            ht.TemplateFlushMode = TemplateFlushMode.Eager;
            ht.Execute(new HibernateDelegate(Del));
            status.SetRollbackOnly();
            return null;
        }

        private object Del(ISession session)
        {
            return null;
        }
    }
    #endregion

    #region Supporting classes for test ParticipatingTransactionWithCommit

    public class ParticipatingTransactionWithCommitTxCallback : ITransactionCallback
    {
        private ISessionFactory sf;
        private IList list;
        public ParticipatingTransactionWithCommitTxCallback(ISessionFactory sf, IList list)
        {
            this.sf = sf;
            this.list = list;
        }


        public object DoInTransaction(ITransactionStatus status)
        {
            Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session");
            HibernateTemplate ht = new HibernateTemplate(sf);
            ht.TemplateFlushMode = TemplateFlushMode.Eager;
            return ht.Execute(new HibernateDelegate(Del));
        }

        private object Del(ISession session)
        {
            return list;
        }
    }


    #endregion

    #region Supporting classes for test ParticipatingTransactionWithRollback
    public class ParticipatingTransactionWithRollbackTxCallback : ITransactionCallback
    {
        private ISessionFactory sf;
        public ParticipatingTransactionWithRollbackTxCallback(ISessionFactory sf)
        {
            this.sf = sf;
        }

        public object DoInTransaction(ITransactionStatus status)
        {
            Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session");
            HibernateTemplate ht = new HibernateTemplate(sf);
            ht.TemplateFlushMode = TemplateFlushMode.Eager;
            return ht.ExecuteFind(new ThrowExceptionHibernateCallback());
        }

    }

    #endregion

    #region Supporting classes for test ParticipatingTransactionWithRollbackOnly

    public class ParticipatingTransactionWithRollbackOnlyTxCallback : ITransactionCallback
    {
        private TransactionTemplate tt;
        private ISessionFactory sf;
        private IList list;
        public ParticipatingTransactionWithRollbackOnlyTxCallback(TransactionTemplate tt, ISessionFactory sf, IList list)
        {
            this.tt = tt;
            this.sf = sf;
            this.list = list;
        }

        public object DoInTransaction(ITransactionStatus status)
        {
            return tt.Execute(new TransactionDelegate(TransactionMethod));
        }

        private object TransactionMethod(ITransactionStatus status)
        {
            Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session");
            HibernateTemplate ht = new HibernateTemplate(sf);
            object returnValue = ht.Execute(new HibernateDelegate(Del));
            status.SetRollbackOnly();
            return null;
        }

        private object Del(ISession session)
        {
            return list;
        }

    }
    #endregion

    #region Supporting classes for test ParticipatingTransactionWithWithRequiresNew

    public class ParticipatingTransactionWithWithRequiresNewTxCallback : ITransactionCallback
    {
        private TransactionTemplate tt;
        private ISessionFactory sf;

        public ParticipatingTransactionWithWithRequiresNewTxCallback(TransactionTemplate tt, ISessionFactory sf)
        {
            this.tt = tt;
            this.sf = sf;
        }

        public object DoInTransaction(ITransactionStatus status)
        {
            SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.GetResource(sf);
            Assert.IsNotNull(holder,"Has thread session");
            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive);

            tt.Execute(new RequiresNewTxCallback(sf, holder));

            Assert.IsTrue(holder.Session == SessionFactoryUtils.GetSession(sf, false),"Same thread session as before");
            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive);
            return null;
        }


    }

    public class RequiresNewTxCallback : ITransactionCallback
    {
        private SessionHolder holder;
        private ISessionFactory sf;

        public RequiresNewTxCallback(ISessionFactory sf, SessionHolder holder)
        {
            this.holder = holder;
            this.sf = sf;
        }

        public object DoInTransaction(ITransactionStatus status)
        {
            HibernateTemplate ht = new HibernateTemplate(sf);
            ht.TemplateFlushMode = TemplateFlushMode.Eager;
            return ht.ExecuteFind(new RequiresNewTxCallbackInner(holder));
        }
    }

    public class RequiresNewTxCallbackInner : IHibernateCallback
    {
        private SessionHolder holder;

        public RequiresNewTxCallbackInner(SessionHolder holder)
        {
            this.holder = holder;
        }

        public object DoInHibernate(ISession session)
        {
            Assert.IsTrue(session != holder.Session,"Not enclosing session");
            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive);
            return null;
        }
    }

    #endregion


    #region Supporting classes for test ParticipatingTransactionWithWithNotSupported

    public class ParticipatingTransactionWithWithNotSupportedTxCallback : ITransactionCallback
    {
        private TransactionTemplate tt;
        private ISessionFactory sf;

        public ParticipatingTransactionWithWithNotSupportedTxCallback(TransactionTemplate tt, ISessionFactory sf)
        {
            this.tt = tt;
            this.sf = sf;
        }

        public object DoInTransaction(ITransactionStatus status)
        {
            SessionHolder holder = (SessionHolder)TransactionSynchronizationManager.GetResource(sf);
            Assert.IsNotNull(holder, "Has thread session");
            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive);
            tt.PropagationBehavior = TransactionPropagation.NotSupported;            
            tt.Execute(new NotSupportedTxCallback(sf));

            Assert.IsTrue(holder.Session == SessionFactoryUtils.GetSession(sf, false), "Same thread session as before");
            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive);
            return null;
        }


    }

    public class NotSupportedTxCallback : ITransactionCallback
    {
        private ISessionFactory sf;

        public NotSupportedTxCallback(ISessionFactory sf)
        {
            this.sf = sf;
        }

        public object DoInTransaction(ITransactionStatus status)
        {


            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sf), "Hasn't thread session");
            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive);

            HibernateTemplate ht = new HibernateTemplate(sf);
            ht.TemplateFlushMode = TemplateFlushMode.Eager;
            return ht.ExecuteFind(new NotSupportedTxCallbackInner());
        }
    }

    public class NotSupportedTxCallbackInner : IHibernateCallback
    {

        public object DoInHibernate(ISession session)
        {
            return null;
        }
    }

    #endregion

    #region Supporting classes for test TransactionWithPropagationSupports

    public class TransactionWithPropagationSupportsTxCallback : ITransactionCallback
    {
        private ISessionFactory sf;
        public TransactionWithPropagationSupportsTxCallback(ISessionFactory sf)
        {

            this.sf = sf;

        }

        public object DoInTransaction(ITransactionStatus status)
        {
            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sf), "Hasn't thread session");
            Assert.IsTrue(!status.IsNewTransaction, "Is not new transaction");

            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive);

            HibernateTemplate ht = new HibernateTemplate(sf);
            ht.TemplateFlushMode = TemplateFlushMode.Eager;
            object returnValue = ht.Execute(new HibernateDelegate(Del));
            Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session");
            return null;
        }

        private object Del(ISession session)
        {
            return null;
        }
    }

    #endregion


    #region Supporting classes for test ParticipatingTransactionWithWithNotSupported

    public class TransactionWithPropagationSupportsAndInnerTransactionTxCallback : ITransactionCallback
    {
        private TransactionTemplate tt;
        private ISessionFactory sf;
        private HibernateTemplate ht;
        private ISession session1;
        private ISession session2;

        public TransactionWithPropagationSupportsAndInnerTransactionTxCallback(TransactionTemplate tt, 
            ISessionFactory sf, HibernateTemplate ht, ISession session1, ISession session2)
        {
            this.tt = tt;
            this.sf = sf;
            this.ht = ht;
            this.session1 = session1;
            this.session2 = session2;
        }

        public object DoInTransaction(ITransactionStatus status)
        {
            Assert.IsTrue(!TransactionSynchronizationManager.HasResource(sf), "Hasn't thread session");
            Assert.IsTrue(!status.IsNewTransaction, "Is not new transaction");
            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive);

            ht.Execute(new HibernateDelegate(HibernateDelegate));

            Assert.IsTrue(TransactionSynchronizationManager.HasResource(sf), "Has thread session");

            tt.Execute(new PropagationSupportsTxCallback(ht, session2));

            Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
            Assert.IsFalse(TransactionSynchronizationManager.ActualTransactionActive);
  
            return null;
        }

        private object HibernateDelegate(ISession session)
        {
            Assert.AreSame(session1, session);
            return null;
        }

        public class PropagationSupportsTxCallback : ITransactionCallback
        {
            private HibernateTemplate ht;
            private ISession session2;

            public PropagationSupportsTxCallback(HibernateTemplate ht, ISession session2)
            {
                this.ht = ht;
                this.session2 = session2;
            }

            public object DoInTransaction(ITransactionStatus status)
            {
                return ht.Execute(new HibernateDelegate(HibernateDelegate));
            }

            private object HibernateDelegate(ISession session)
            {
                Assert.IsFalse(TransactionSynchronizationManager.CurrentTransactionReadOnly);
                Assert.IsTrue(TransactionSynchronizationManager.ActualTransactionActive);
                Assert.AreSame(session2, session);
                return null;
            }
        }


    }



    #endregion

    #region Supporting classes for DoTransactionCommitWithFlushFailure

    public class TransactionCommitWithFlushFailureCallback : ITransactionCallback
    {
        private ISessionFactory sessionFactory;
        private IList list;

        public TransactionCommitWithFlushFailureCallback(ISessionFactory sessionFactory, IList list)
        {
            this.sessionFactory = sessionFactory;
            this.list = list;
        }

        public object DoInTransaction(ITransactionStatus status)
        {
            Assert.IsTrue(TransactionSynchronizationManager.HasResource(sessionFactory), "Has thread session");
            HibernateTemplate ht = new HibernateTemplate(sessionFactory);
            return ht.ExecuteFind(new TransactionCommitWithFlushFailureHibernateCallback(list));
        }


    }

    public class TransactionCommitWithFlushFailureHibernateCallback : IHibernateCallback
    {
        private IList list;
        public TransactionCommitWithFlushFailureHibernateCallback(IList list)
        {
            this.list = list;
        }

        public object DoInHibernate(ISession session)
        {
            return list;
        }
    }

    #endregion

}