Beispiel #1
0
        public void TestSelectStar()
        {
            string code = @"
select *
from AirportInfo 
where airportCode = 'kaus'
";
            var    mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirportInfo(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirportInfo.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Columns.Length == 6);
            Assert.IsTrue(result.First().Columns[0].Name == "airportCode");
            Assert.IsTrue(result.First().Columns[1].Name == "latitude");
            Assert.IsTrue(result.First().Columns[2].Name == "longitude");
            Assert.IsTrue(result.First().Columns[3].Name == "location");
            Assert.IsTrue(result.First().Columns[4].Name == "name");
            Assert.IsTrue(result.First().Columns[5].Name == "timezone");

            Assert.AreEqual(result.First().Rows[0].Values[0], "kaus");
            Assert.AreEqual(result.First().Rows[0].Values[1], 30.1945272f);
            Assert.AreEqual(result.First().Rows[0].Values[2], -97.6698761f);
            Assert.AreEqual(result.First().Rows[0].Values[3], "Austin, TX");
            Assert.AreEqual(result.First().Rows[0].Values[5], ":America/Chicago");
        }
        public void TestNestedSelectJoinInvalidSelectArgInner()
        {
            string code = @"
select a.ident, f.faFlightID
from (
    select
        blah,
        case
            when actual_ident != ''
                then actual_ident
            else
                ident
            end as ident
    from airlineflightschedules
    where departuretime > '2020-1-21 9:15'
) a
join GetFlightId f on f.ident = a.ident and f.departureTime = a.departureTime 
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });
            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.GetFlightId.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 2);
        }
        public async Task <IActionResult> Query()
        {
            string query = string.Empty;

            using (var reader = new StreamReader(Request.Body))
            {
                query = await reader.ReadToEndAsync();
            }

            var authorization = Request.Headers["Authorization"];
            var context       = RunContext.CreateRunContext(query, authorization);
            var result        = context.Run();

            var isUnauthorized = context.Errors.Any(x =>
            {
                var apiError = x as ApiExecuteError;
                if (apiError != null)
                {
                    return(apiError.Type == ApiExecuteErrorType.AuthError);
                }
                return(false);
            });

            if (isUnauthorized)
            {
                return(Unauthorized());
            }

            return(Ok(new ResultViewModel()
            {
                Tables = result, Errors = context.Errors
            }));
        }
        public void TestExecute()
        {
            string code = @"
select *
from mapflight
where ident = 'DAL503'
";


            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetMapFlight(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.MapFlight.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);

            Assert.IsTrue(result.First().Rows.Length == 1);
            Assert.AreEqual(result.First().Columns[3].Type, "base64image");
            Assert.AreEqual(result.First().Rows[0].Values[1], 480);
            Assert.AreEqual(result.First().Rows[0].Values[2], 640);
            Assert.AreEqual(result.First().Rows[0].Values[3], "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");
        }
        public void TestQuerableParametersTwoDepartureTimeGreaterLessEqual()
        {
            string code = @"
select aircrafttype, actual_ident, ident, seats_cabin_business, arrivaltime
from AirlineFlightSchedules
where departuretime >= '2020-1-21 9:15' and departuretime <= '2020-11-21 9:15'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Callback <HttpExecuteArg>(args =>
            {
                Assert.IsTrue(args.Variables.Count() == 2);
                var start = args.Variables.Where(x => x.Variable == "startDate").SingleOrDefault();
                Assert.IsTrue(start.Value == "1579598100");

                var end = args.Variables.Where(x => x.Variable == "endDate").SingleOrDefault();
                Assert.IsTrue(end.Value == "1605950100");
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            mock.Verify(v => v.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>()), Times.Once());
        }
        public void TestSimpleExecute()
        {
            string code = @"
select aircrafttype, actual_ident, ident, seats_cabin_business, arrivaltime
from AirlineFlightSchedules
where departuretime > '2020-1-21 9:15'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(result.First().Columns.Length == 5);
            Assert.IsTrue(result.First().Columns[0].Name == "aircrafttype");

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 15);
            Assert.AreEqual(result.First().Rows[0].Values[0], "B762");
            Assert.AreEqual(result.First().Rows[0].Values[1], "OAE2412");
            Assert.AreEqual(result.First().Rows[0].Values[2], "ACA2412");
            Assert.AreEqual(result.First().Rows[0].Values[3], 18);
            Assert.AreEqual(Conversion.ConvertDateTimeToString((DateTime)result.First().Rows[0].Values[4]), "2020-03-07 14:55");
        }
        public void TestExecute()
        {
            string code = @"
select aircrafttype
from FlightInfoEx
where faFlightID = 'some-flight-number'
";

            var mock = new Mock <IHttpExecutor>();

            mock.Setup(x => x.GetFlightInfoEx(It.IsAny <HttpExecuteArg>())).Returns(() => new ApiExecuteResult <IEnumerable <FlightInfoEx> >(new FlightInfoEx[] { new FlightInfoEx()
                                                                                                                                                                  {
                                                                                                                                                                      aircrafttype = "B739", faFlightID = "some-flight-number"
                                                                                                                                                                  } }));

            var context = RunContext.CreateRunContext(code, mock.Object);
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Columns.Length == 1);
            Assert.IsTrue(result.First().Columns[0].Name == "aircrafttype");

            Assert.IsTrue(result.First().Rows.Length == 1);
            Assert.AreEqual(result.First().Rows[0].Values[0], "B739");
        }
Beispiel #8
0
        public void TestJoinFlightId()
        {
            string code = @"
select a.ident, faFlightID
from AirlineFlightSchedules a
join GetFlightId f on f.ident = a.ident and f.departureTime = a.departureTime 
where a.departuretime > '2020-1-21 9:15'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });
            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.GetFlightId.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 15);

            mock.Verify(v => v.GetFlightID(It.IsAny <HttpExecuteArg>()), Times.Exactly(15));
            mock.Verify(v => v.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>()), Times.Once());
        }
Beispiel #9
0
        public void TestJoinMultiple()
        {
            string code = @"
select a.ident, i.faFlightID, i.route
from AirlineFlightSchedules a
join GetFlightId f on f.ident = a.ident and f.departureTime = a.departureTime 
join FlightInfoEx i on i.faFlightID = f.faFlightID
where a.departuretime > '2020-1-21 9:15' and a.ident = 'ACI4600'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });
            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.GetFlightId.json"));
            });
            mock.Setup(x => x.GetFlightInfoEx(It.IsAny <HttpExecuteArg>()))
            .Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.FlightInfoEx.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 1);
            Assert.AreEqual(result.First().Rows[0].Values[2], "ELOEL2 FORSS GUTZZ SOCKK3");
        }
Beispiel #10
0
        public void TestExecute()
        {
            string code = @"
select location
from AirportInfo 
where airportCode = 'kaus'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirportInfo(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirportInfo.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Columns.Length == 1);
            Assert.IsTrue(result.First().Columns[0].Name == "location");

            Assert.IsTrue(result.First().Rows.Length == 1);
            Assert.AreEqual(result.First().Rows[0].Values[0], "Austin, TX");
        }
        public void TestNestedSelect()
        {
            string code = @"
select *
from (
    select
        case
            when actual_ident != ''
                then actual_ident
            else
                ident
            end
    from airlineflightschedules
    where departuretime > '2020-1-21 9:15'
) a
";
            var    mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);

            Assert.IsTrue(result.First().Rows.Length > 0);
            Assert.AreEqual(result.First().Columns[0].Name, "(No column name)");
            Assert.AreEqual(result.First().Rows[0].Values[0], "OAE2412");
        }
Beispiel #12
0
        public void TestJoinSameTable()
        {
            string code = @"
select f.ident, f.departuretime, f.arrivaltime, o.name, d.name
from airlineflightschedules f
join airportinfo d on d.airportCode = f.destination
join airportinfo o on o.airportCode = f.origin
where f.departuretime > '2020-1-16 1:46'and f.ident = 'DAL1381'";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });
            mock.Setup(x => x.GetAirportInfo(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirportInfo.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Columns.Length == 5);
            Assert.IsTrue(result.First().Rows.Length == 1);

            mock.Verify(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>()), Times.Once);
            mock.Verify(x => x.GetAirportInfo(It.IsAny <HttpExecuteArg>()), Times.Exactly(30));
        }
        public void TestImageMinMax()
        {
            string code = @"
select *
from mapflight
where ident = 'DAL503' and mapHeight = 100 and mapWidth = 120
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetMapFlight(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.MapFlight.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);

            Assert.AreEqual(result.First().Columns[1].Name, "mapHeight");
            Assert.AreEqual(result.First().Columns[2].Name, "mapWidth");

            Assert.AreEqual(result.First().Rows[0].Values[1], 100);
            Assert.AreEqual(result.First().Rows[0].Values[2], 120);
        }
Beispiel #14
0
        public void TestJoinWithApi200Error()
        {
            string code = @"
select *   
from airlineflightschedules a
join getflightid i on i.departureTime = a.departuretime and a.ident = i.ident
where a.departuretime > '2020-4-10 8:00' and a.origin = 'PHLI' and a.ident = 'DAL1381'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(
                new ExecuteResult()
            {
                Result = @"{""error"":""INVALID_ARGUMENT startDate is too far in the past(3 months)""}"
            }
                );
            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.GetFlightId.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 1);
            Assert.IsTrue(context.Errors[0].Message == "Error executing request: INVALID_ARGUMENT startDate is too far in the past(3 months)");
        }
Beispiel #15
0
        public void TestArrivalTimeLeft()
        {
            string code = @"
select departureTime,
    arrivalTime - UTC_TIMESTAMP() as timeLeft
from inflightinfo
where ident = ""SWA5302""
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetInFlightInfo(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.InFightInfo.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 1);
            Assert.IsTrue(result.First().Columns.Length == 2);

            Assert.AreNotEqual(Conversion.ConvertTimespanToString(result.First().Rows[0].Values[1]), null);
        }
Beispiel #16
0
        public void TestJoinNoFlightId()
        {
            string code = @"
select a.ident, faFlightID
from AirlineFlightSchedules a
join GetFlightId f on f.ident = a.ident and f.departureTime = a.departureTime 
where a.departuretime > '2020-1-21 9:15'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });
            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns(
                new ExecuteResult()
            {
                Result = @"{""error"":""NO_DATA flight not found""}"
            }
                );

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 0);
        }
Beispiel #17
0
        public void TestAirlineflightScheduleTestCase()
        {
            string code = @"
select
    case
        when actual_ident != ''
            then actual_ident
        else
            ident
        end
from airlineflightschedules
where departuretime > '2020-1-21 9:15'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
        }
Beispiel #18
0
        public void TestMultipleResultsRightTable()
        {
            string code = @"
select a.ident, f.faFlightID, altitude, h.timestamp
from AirlineFlightSchedules a
join GetFlightId f on f.ident = a.ident and f.departureTime = a.departureTime
join GetHistoricalTrack h on h.faFlightID = f.faFlightID
where a.departuretime > '2020-1-21 9:15'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });
            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns <HttpExecuteArg>((args) =>
            {
                var ident = args.Variables.Where(x => x.Variable == "ident").Single();
                if (ident.Value == "DAL1381")
                {
                    return new ExecuteResult()
                    {
                        Result = @"{""GetFlightIDResult"": ""flight-id-a""}"
                    }
                }
                ;

                return(new ExecuteResult()
                {
                    Result = @"{""error"":""NO_DATA flight not found""}"
                });
            });
            mock.Setup(x => x.GetHistoricalTrack(It.IsAny <HttpExecuteArg>())).Returns <HttpExecuteArg>((args) =>
            {
                var faflight = args.Variables.Where(x => x.Variable == "faFlightID").Single();
                if (faflight.Value == "flight-id-a")
                {
                    return(TestHelper.LoadJson("FlightQuery.Tests.GetHistoricTrack.json"));
                }

                return(new ExecuteResult()
                {
                    Result = @"{""error"":""NO_DATA no data available""}"
                });
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 392);
        }
Beispiel #19
0
        public void TestSelecThirdCase()
        {
            string code = @"
select a.ident,
    case
        when i.actualarrivaltime = -1 and i.actualdeparturetime = -1 and i.estimatedarrivaltime = -1
        then 'cancelled'
        when i.actualdeparturetime != 0 and i.actualarrivaltime = 0
        then 'enroute'
        when i.actualdeparturetime != 0 and i.actualarrivaltime != 0 and i.actualdeparturetime != i.actualarrivaltime
        then 'arrived'
        else a.seats_cabin_business
    end
from AirlineFlightSchedules a
join GetFlightId f on f.ident = a.ident and f.departureTime = a.departureTime 
join FlightInfoEx i on i.faFlightID = f.faFlightID
where a.departuretime > '2020-1-21 9:15' and a.ident = 'ACI4600'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });
            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.GetFlightId.json"));
            });
            mock.Setup(x => x.GetFlightInfoEx(It.IsAny <HttpExecuteArg>()))
            .Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.FlightInfoEx.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);

            Assert.IsTrue(result.First().Columns[0].Name == "ident");
            Assert.IsTrue(result.First().Columns[1].Name == "(No column name)");
            Assert.AreEqual(result.First().Rows[0].Values[0], "ACI4600");
            Assert.AreEqual(result.First().Rows[0].Values[1], "arrived");
        }
Beispiel #20
0
        public void TestQuerableParametersLowerCase()
        {
            string code = @"
select location
from AirportInfo 
where airportcode = 'kaus'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirportInfo(It.IsAny <HttpExecuteArg>())).Callback <HttpExecuteArg>((args) =>
            {
                Assert.IsTrue(args.Variables.Count() == 1);
                var start = args.Variables.Where(x => x.Variable == "airportCode").SingleOrDefault();
                Assert.IsTrue(start != null);
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();
        }
        public void TestNestedSelectJoin()
        {
            string code = @"
select a.ident, f.faFlightID
from (
    select
        departureTime,
        case
            when actual_ident != ''
                then actual_ident
            else
                ident
            end as ident
    from airlineflightschedules
    where departuretime > '2020-1-21 9:15'
) a
join GetFlightId f on f.ident = a.ident and f.departureTime = a.departureTime 
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.AirlineFlightSchedule.json"));
            });
            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.GetFlightId.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 15);

            Assert.AreEqual(result.First().Columns[0].Name, "ident");
            Assert.AreEqual(result.First().Columns[1].Name, "faFlightID");
            Assert.AreEqual(result.First().Rows[0].Values[0], "OAE2412");
            Assert.AreEqual(result.First().Rows[0].Values[1], "AAL2594-1586309220-schedule-0000");
        }
        public void TestExecuteCancelledEmpty()
        {
            string code = @"
select *
from FlightInfoEx
where faFlightID = 'AAL2594-1586309220-schedule-0000' and actualdeparturetime != -1 and estimatedarrivaltime != -1 and actualarrivaltime != -1
";
            var    mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetFlightInfoEx(It.IsAny <HttpExecuteArg>()))
            .Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.FlightInfoExCancelled.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 0);
        }
        public void TestNoDataError()
        {
            string code = @"
select destination
from FlightInfoEx
where ident = 'AAL2563'
";
            var    mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetFlightInfoEx(It.IsAny <HttpExecuteArg>())).Returns(
                new ExecuteResult()
            {
                Result = @"{""error"":""NO_DATA flight not found""}"
            }
                );

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 0);
        }
        public void TestNestedSelectInnerError()
        {
            string code = @"
select a.ident, f.faFlightID
from (
    select
        departureTime,
        case
            when actual_ident != ''
                then actual_ident
            else
                ident
            end as ident
    from airlineflightschedules
    where departuretime > '2020-1-21 9:15'
) a
join GetFlightId f on f.ident = a.ident and f.departureTime = a.departureTime 
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(
                new ExecuteResult()
            {
                Result = @"{""error"":""INVALID_ARGUMENT startDate is too far in the past(3 months)""}"
            }
                );
            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns <HttpExecuteArg>((args) =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.GetFlightId.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 1);
            Assert.IsTrue(context.Errors[0].Message == "Error executing request: INVALID_ARGUMENT startDate is too far in the past(3 months)");
            Assert.IsTrue(result.First().Rows.Length == 0);
        }
        public void TestExecuteNoFlightID()
        {
            string code = @"
select faFlightID
from GetFlightId
where ident = 'DAL503' and departuretime = '2020-3-7 9:15'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetFlightID(It.IsAny <HttpExecuteArg>())).Returns(
                new ExecuteResult()
            {
                Result = @"{""error"":""NO_DATA flight not found""}"
            }
                );

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 0);
        }
Beispiel #26
0
        public void TestExecute()
        {
            string code = @"
select *
from inflightinfo
where ident = ""SWA5302""
";
            var    mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetInFlightInfo(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.InFightInfo.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length > 0);

            Assert.AreEqual(result.First().Rows[0].Values[0], 400);
            Assert.AreEqual(result.First().Rows[0].Values[6], "SWA5302-1587617128-airline-0319");
        }
Beispiel #27
0
        public void TestExecute()
        {
            string code = @"
select faFlightID, ifaFlightID
from InboundFlightInfo
where faFlightID = 'unique-flight-id'
";
            var    mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetInboundFlightInfo(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.InboundFlightInfo.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length > 0);

            Assert.AreEqual(result.First().Rows[0].Values[0], "unique-flight-id");
            Assert.AreEqual(result.First().Rows[0].Values[1], "SWA2055-1587444311-airline-0873");
        }
        public void TestApiReturn200WithError()
        {
            string code = @"
select aircrafttype, actual_ident, ident, seats_cabin_business, arrivaltime
from AirlineFlightSchedules
where departuretime < '2020-1-21 9:15'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirlineFlightSchedule(It.IsAny <HttpExecuteArg>())).Returns(
                new ExecuteResult()
            {
                Result = @"{""error"":""INVALID_ARGUMENT startDate is too far in the past(3 months)""}"
            }
                );

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 1);
            Assert.IsTrue(context.Errors[0].Message == "Error executing request: INVALID_ARGUMENT startDate is too far in the past(3 months)");
        }
Beispiel #29
0
        public void TestNoAuth()
        {
            string code = @"
select location
from AirportInfo 
where airportCode = 'kaus'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetAirportInfo(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(new ExecuteResult()
                {
                    Result = "", Error = new ApiExecuteError(ApiExecuteErrorType.AuthError, "Authentication error")
                });
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 1);
        }
        public void TestExecuteIdent()
        {
            string code = @"
select destination
from FlightInfoEx
where ident = 'AAL2563'
";

            var mock = new Mock <IHttpExecutorRaw>();

            mock.Setup(x => x.GetFlightInfoEx(It.IsAny <HttpExecuteArg>())).Returns(() =>
            {
                return(TestHelper.LoadJson("FlightQuery.Tests.FlightInfoEx.json"));
            });

            var context = RunContext.CreateRunContext(code, new HttpExecutor(mock.Object));
            var result  = context.Run();

            Assert.IsTrue(context.Errors.Count == 0);
            Assert.IsTrue(result.First().Rows.Length == 1);
            Assert.IsTrue(result.First().Columns.Length == 1);
            Assert.AreEqual(result.First().Rows[0].Values[0], "KDFW");
        }