using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading.Tasks;
public class CityFinderTests
private CityFinder _cityFinder;
private Database _database;
_database = new Database();
_cityFinder = new CityFinder(_database);
public async Task GetNearestCity_ReturnsCorrectCity()
var nearestCity = await _cityFinder.GetTheNearestCityByQueryAsync(40.7128, -74.0060);
Assert.That(nearestCity.City, Is.EqualTo("New York City"));
Assert.That(nearestCity.Country, Is.EqualTo("USA"));
public async Task GetNearestCity_ReturnsCorrectCity_DifferentCoordinates()
var nearestCity = await _cityFinder.GetTheNearestCityByQueryAsync(34.0522, -118.2437);
Assert.That(nearestCity.City, Is.EqualTo("Los Angeles"));
Assert.That(nearestCity.Country, Is.EqualTo("USA"));
public async Task GetNearestCity_ReturnsCorrectCity_EdgeCase_SameLocation()
var nearestCity = await _cityFinder.GetTheNearestCityByQueryAsync(51.5074, -0.1278);
Assert.That(nearestCity.City, Is.EqualTo("London"));
Assert.That(nearestCity.Country, Is.EqualTo("UK"));
public async Task GetNearestCity_ReturnsCorrectCity_CoordinatesOutsideRange()
var nearestCity = await _cityFinder.GetTheNearestCityByQueryAsync(45.0, -100.0);
Assert.That(nearestCity.City, Is.EqualTo("Chicago"));
Assert.That(nearestCity.Country, Is.EqualTo("USA"));
public async Task GetNearestCity_ReturnsCorrectCity_SouthAmerica()
var nearestCity = await _cityFinder.GetTheNearestCityByQueryAsync(-20, -48);
Assert.That(nearestCity.City, Is.EqualTo("Sao Paulo"));
Assert.That(nearestCity.Country, Is.EqualTo("Brazil"));
public class WorldCityTable
public double Lat { get; set; }
public double Lng { get; set; }
public string City { get; set; }
public string Country { get; set; }
public WorldCityTable(double lat, double lng, string city, string country)
public interface IDatabase
Task<List<T>> QueryAsync<T>(string sql, params object[] parameters);
public class Database : IDatabase
public Task<List<T>> QueryAsync<T>(string sql, params object[] parameters)
if (sql.Contains("ST_Distance"))
var cities = TestData.AllCities;
var latitude = (double)parameters[0];
var longitude = (double)parameters[1];
var result = cities.Select(city => new {
Distance = CalculateHaversineDistance(latitude, longitude, city.Lat, city.Lng)
}).OrderBy(x => x.Distance)
return Task.FromResult((List<T>)(object)result);
return Task.FromResult((List<T>)(object)TestData.AllCities);
private double CalculateHaversineDistance(double lat1, double lon1, double lat2, double lon2)
const double earthRadius = 6371;
var lat1Rad = lat1 * Math.PI / 180;
var lon1Rad = lon1 * Math.PI / 180;
var lat2Rad = lat2 * Math.PI / 180;
var lon2Rad = lon2 * Math.PI / 180;
var dLat = lat2Rad - lat1Rad;
var dLon = lon2Rad - lon1Rad;
var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
Math.Cos(lat1Rad) * Math.Cos(lat2Rad) *
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
var distance = earthRadius * c;
public static class TestData
public static List<WorldCityTable> AllCities = new List<WorldCityTable>
new WorldCityTable(40.7128, -74.0060, "New York City", "USA"),
new WorldCityTable(34.0522, -118.2437, "Los Angeles", "USA"),
new WorldCityTable(51.5074, -0.1278, "London", "UK"),
new WorldCityTable(48.8566, 2.3522, "Paris", "France"),
new WorldCityTable(35.6895, 139.6917, "Tokyo", "Japan"),
new WorldCityTable(41.8781, -87.6298, "Chicago", "USA"),
new WorldCityTable(25.2048, 55.2708, "Dubai", "UAE"),
new WorldCityTable(37.7749, -122.4194, "San Francisco", "USA"),
new WorldCityTable(1.3521, 103.8198, "Singapore", "Singapore"),
new WorldCityTable(-23.5505, -46.6333, "Sao Paulo", "Brazil"),
new WorldCityTable(-33.8688, 151.2093, "Sydney", "Australia")
private readonly IDatabase _database;
public CityFinder(IDatabase database)
public async Task<WorldCityTable> GetTheNearestCityByQueryAsync(double currentLat, double currentLng)
var cities = await _database.QueryAsync<WorldCityTable>(
ORDER BY ST_Distance(POINT(Lng, Lat), POINT(?, ?)) ASC
return cities.FirstOrDefault();
public static void Main(string[] args)
Console.WriteLine("-------------- STARTING UNIT TESTING ---------------");
new NUnitLite.AutoRun().Execute(["--noc" ]);