dimanche 7 janvier 2018

what is wrong with my test

I have 2 simple tests thus far for a sql query that is a little strange. There are two things I don't understand that is happening. My first test I don't insert any data and run my query and I expect that the section if (reader.HasRows) returns false (which it does) and it correctly than returns 0. The second test I insert 2 records one that is "paid" and the other that is not (it will make more sense in a second). I expect only to see the sum of the paid rows but I get nothing. The problem is that it is a time sensitive query that changes time zones. I'm not sure why it's that way but I can't change that fact. Since it is work related I'll have to change a few things around. Ebay simulator sounds like a good example.

make a simple table in mysql

CREATE TABLE `order` (
  `id` int(11) unsigned NOT NULL,
  `Total` decimal(8,2) DEFAULT NULL,
  `OrderDate` datetime(6) DEFAULT NULL,
  `Paid` tinyint(3) unsigned NOT NULL,
  `Deleted` tinyint(3) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`),
);

now to make 2 simple tests with NUnit

[TestFixture]
public class DbAccountingStatisticsTests
{
    private readonly DateTime TodayStartOrDay = DateTime.Today;
    private readonly DateTime TodayEndOfDay = DateTime.Today.AddDays(1).AddTicks(-1);
    private LoggingAssert _logging;
    private DeferredMultiTableDatabaseActions _dbActions;
    private static readonly TimeZoneInfo Pst = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
    private static readonly TimeZoneInfo Est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

    [SetUp]
    public void Setup()
    {
        _logging = LoggingAssert.Setup();
        _dbActions = new DeferredMultiTableDatabaseActions();
    }

    [TearDown]
    public void TearDown()
    {
        _dbActions.SubmitChanges();
        _logging.FailIfAnyLoggedErrors();
    }

    [Test]
    public void WithZeroShippedOrders()
    {
        var db = new DbAccountingStatistics();

        var stats = db.GetOrderStats(
            TimeZoneInfo.ConvertTime(TodayStartOrDay, Pst),
            TimeZoneInfo.ConvertTime(TodayEndOfDay, Pst)
        );

        Assert.That(stats.NumberofOrders, Is.Zero);
        Assert.That(stats.TotalOrders, Is.Zero);
    }

    [Test]
    public void NonPaidOrdersNotIncluded()
    {
        AddOrder(orderTotal: 100, paid: false);
        AddOrder(orderTotal: 100, paid: true);
        var db = new DbAccountingStatistics();

        var stats = db.GetOrderStats(
            TimeZoneInfo.ConvertTime(TodayStartOrDay, Est),
            TimeZoneInfo.ConvertTime(TodayEndOfDay, Est)
        );

        Assert.That(stats.NumberofOrders, Is.EqualTo(1));
        Assert.That(stats.TotalOrders, Is.EqualTo(100));
    }

    private void AddOrder(decimal orderTotal, bool paid)
    {
        var db = new DbOrder();
        var id = db.Insert(new PocoOrder
        {
            Paid = paid,
            Total = orderTotal,
            OrderDate = DateTime.Today//TimeZoneInfo.ConvertTime(DateTime.Today, Est)
        });
        _logging.FailIfAnyLoggedErrors();

        _dbActions.AddSimpleChange($"delete from order where id = {id}");
    }
}

and now the class under test and the sql that is giving me troubles

public class DbAccountingStatistics
{
    private readonly DeferredMultiTableDatabaseActions _dataAdapter = new DeferredMultiTableDatabaseActions();

    private const string SqlGetOrderStats =
        "SELECT count(id) as orderNum, sum(total) as Total " +
        "FROM order " +
        "WHERE Date(CONVERT_TZ(OrderDate,'US/Eastern','US/Pacific')) >= @STARTDATE " +
        "AND Date(CONVERT_TZ(OrderDate,'US/Eastern','US/Pacific')) <= @ENDDATE " +
        "AND Deleted = 0 and paid = 1";

    public PocoAccountingStatistics GetOrderStats(DateTime startDate, DateTime endDate)
    {
        var stats = new PocoAccountingStatistics();
        _dataAdapter.AddDeferredAction(new DeferredAction<PocoAccountingStatistics>
        {
            Sql = SqlGetOrderStats,
            SqlParameters = new Dictionary<string, object>
            {
                {"@STARTDATE", startDate},
                {"@ENDDATE", endDate},
            },
            ParseAction = FillOrderStats
        });
        _dataAdapter.Fill(stats);
        return stats;
    }

    private void FillOrderStats(PocoAccountingStatistics stats, IDataRecord record)
    {
        var orderCountOrdinal = record.GetOrdinal("orderNum");
        var totalOrdinal = record.GetOrdinal("Total");

        stats.NumberofOrders = (long)record[orderCountOrdinal];
        stats.TotalOrders = record.IsDBNull(totalOrdinal)? 0 : record.GetDecimal(totalOrdinal);
    }
}
public class PocoAccountingStatistics
{
    public long NumberofOrders { get; set; }
    public decimal TotalOrders { get; set; }
}
public class DeferredAction
{
    public string Sql { get; set; }
    public Dictionary<string, object> SqlParameters { get; set; }
}

public class DeferredAction<T> : DeferredAction
{
    public Action<T, IDataRecord> ParseAction { get; set; }
}

public class DeferredMultiTableDatabaseActions
{
    private static readonly Logger logger = LogManager.GetLogger(nameof(DeferredMultiTableDatabaseActions));
    private readonly List<DeferredAction> _deferredActions = new List<DeferredAction>();

    public void AddSimpleChange(string sql)
    {
        _deferredActions.Add(new DeferredAction
        {
            Sql = sql,
        });
    }

    public void AddDeferredAction(DeferredAction deferredAction)
    {
        _deferredActions.Add(deferredAction);
    }

    public void SubmitChanges()
    {
        using (var connection = new BfConnection().DbConnection)
        {
            var db = new Database(connection);
            connection.Open();
            foreach (var clown in _deferredActions)
            {
                using (var command = db.GetCommand(clown.Sql))
                {
                    var rowsAffected = db.ExecuteNonQuery(command);
                    if(rowsAffected < 1)
                        logger.Error($"Expected atleast 1 rows to be affected for statment '{clown.Sql}'");
                }
            }
        }
    }

    public void Fill<T>(T poco)
    {
        using (var connection = new BfConnection().DbConnection)
        {
            var db = new Database(connection);
            connection.Open();
            foreach (var deferredAction in _deferredActions)
            {
                using (var command = db.GetCommand(deferredAction.Sql))
                {
                    foreach (var parameter in deferredAction.SqlParameters)
                        AddParameter(command, parameter.Key, parameter.Value);

                    using (var reader = db.ExecuteReader(command))
                    {
                        if (!reader.HasRows)
                            continue;

                        if (reader.Read())
                            ((DeferredAction<T>) deferredAction).ParseAction(poco, reader);
                    }
                }
            }
        }
    }

    private static void AddParameter(DbCommand command, string name, object value)
    {
        var parameter = command.CreateParameter();
        parameter.ParameterName = name;
        parameter.Value = value;
        command.Parameters.Add(parameter);
    }
}

The first time I ran this test I was getting DbNull because of my timezones not being setup locally so I went to mysql's website and got that setup. Then I researched what the Convert_TZ is all about and figured out it is converting OrderDate from EST to PST and comparing with my two parameters. In the test I've played around with inserting the date as PST, EST and my current Time (CST). I've also changed the test to convert the time and not convert the time. Each time I get back 0 for total and count. I don't understand why the HasRows is true when I insert the two records if the where clause is wrong compared to when I insert nothing HasRows returns false. I've been fighting with this for a few hours and even after sleeping i still can't figure out what is wrong.

Aucun commentaire:

Enregistrer un commentaire