Files

246 lines
8.3 KiB
C#

using System.Text.Json.Nodes;
using Microsoft.Extensions.Options;
using TileServer.Application.Configuration;
using TileServer.Application.Extracts;
using TileServer.Application.Styles;
using TileServer.Application.Tiles;
using TileServer.Domain.Extracts;
using TileServer.Domain.Tiles;
namespace TileServer.UnitTests;
public sealed class StyleServiceTests
{
[Fact]
public void Get_BindsAbsoluteTilesAndSourceMaxZoom()
{
var extract = new Extract
{
Id = "central-fed-district",
Name = "ЦФО",
Url = new Uri("https://download.geofabrik.de/russia/central-fed-district-latest.osm.pbf"),
CenterLon = 37.6,
CenterLat = 55.7,
MinZoom = 0,
MaxZoom = 14
};
var metadata = new TilesetMetadata(
extract.Id,
extract.Name,
"pbf",
29.2,
48.7,
47.6,
59.6,
38.4,
54.2,
5,
0,
14,
[]);
var template = new JsonObject
{
["version"] = 8,
["name"] = "OSM Bright",
["sources"] = new JsonObject
{
["openmaptiles"] = new JsonObject
{
["type"] = "vector",
["url"] = "/api/v1/tiles/central-fed-district.json"
}
}
};
var service = new StyleService(
new MemoryStyleCatalog(template),
new MemoryExtractCatalog(extract),
new MemoryTileStore(metadata),
Options.Create(new TileServerOptions
{
GlyphsUrl = "https://fonts.example/{fontstack}/{range}.pbf"
}));
var style = service.Get("osm-bright", extract.Id, "https://tile-server.ru");
var source = style["sources"]!["openmaptiles"]!.AsObject();
Assert.Null(source["url"]);
Assert.Equal(0, source["minzoom"]!.GetValue<int>());
Assert.Equal(14, source["maxzoom"]!.GetValue<int>());
Assert.Equal("xyz", source["scheme"]!.GetValue<string>());
Assert.Equal(
"https://tile-server.ru/api/v1/tiles/central-fed-district/{z}/{x}/{y}.pbf?v=3",
source["tiles"]!.AsArray()[0]!.GetValue<string>());
Assert.Null(source["bounds"]);
}
[Fact]
public void Bind_UsesPersonalTileUrlAndToken()
{
var extract = new Extract
{
Id = "central-fed-district",
Name = "ЦФО",
Url = new Uri("https://download.geofabrik.de/russia/central-fed-district-latest.osm.pbf"),
CenterLon = 37.6,
CenterLat = 55.7,
MinZoom = 0,
MaxZoom = 14
};
var metadata = new TilesetMetadata(
extract.Id,
extract.Name,
"pbf",
29.2,
48.7,
47.6,
59.6,
38.4,
54.2,
5,
0,
14,
[]);
var template = new JsonObject
{
["version"] = 8,
["sources"] = new JsonObject
{
["openmaptiles"] = new JsonObject
{
["type"] = "vector"
}
}
};
var service = new StyleService(
new MemoryStyleCatalog(template),
new MemoryExtractCatalog(extract),
new MemoryTileStore(metadata),
Options.Create(new TileServerOptions()));
var tilesUrl = "https://tile-server.ru/u/alice/tiles/central-fed-district/{z}/{x}/{y}.pbf?v=3&token=ts_abc";
var style = service.Bind(template, extract.Id, tilesUrl);
var tile = style["sources"]!["openmaptiles"]!["tiles"]!.AsArray()[0]!.GetValue<string>();
Assert.Contains("/u/alice/", tile, StringComparison.Ordinal);
Assert.Contains("token=ts_abc", tile, StringComparison.Ordinal);
}
[Fact]
public void Bind_MultipleExtracts_AddsSourcesAndClonesLayers()
{
var central = SampleExtract("central-fed-district", 37.6, 55.7);
var volga = SampleExtract("volga-fed-district", 45, 57);
var template = new JsonObject
{
["version"] = 8,
["sources"] = new JsonObject
{
["openmaptiles"] = new JsonObject { ["type"] = "vector" }
},
["layers"] = new JsonArray
{
new JsonObject
{
["id"] = "background",
["type"] = "background",
["paint"] = new JsonObject { ["background-color"] = "#000" }
},
new JsonObject
{
["id"] = "water",
["type"] = "fill",
["source"] = "openmaptiles",
["source-layer"] = "water"
}
}
};
var service = new StyleService(
new MemoryStyleCatalog(template),
new MemoryExtractCatalog(central, volga),
new MemoryTileStore(SampleMetadata(central)),
Options.Create(new TileServerOptions()));
var style = service.Bind(template, [
new BoundExtract(central.Id, "https://tile-server.ru/u/alice/tiles/central-fed-district/{z}/{x}/{y}.pbf?token=ts"),
new BoundExtract(volga.Id, "https://tile-server.ru/u/alice/tiles/volga-fed-district/{z}/{x}/{y}.pbf?token=ts")
]);
var sources = style["sources"]!.AsObject();
Assert.True(sources.ContainsKey("openmaptiles"));
Assert.True(sources.ContainsKey("openmaptiles__volga-fed-district"));
Assert.Contains("volga-fed-district", sources["openmaptiles__volga-fed-district"]!["tiles"]![0]!.GetValue<string>(), StringComparison.Ordinal);
var layers = style["layers"]!.AsArray();
Assert.Equal(3, layers.Count);
Assert.Equal("water__volga-fed-district", layers[2]!["id"]!.GetValue<string>());
Assert.Equal("openmaptiles__volga-fed-district", layers[2]!["source"]!.GetValue<string>());
Assert.Null(layers[0]!["source"]);
}
private static Extract SampleExtract(string id, double lon, double lat)
=> new()
{
Id = id,
Name = id,
Url = new Uri($"https://download.geofabrik.de/russia/{id}-latest.osm.pbf"),
CenterLon = lon,
CenterLat = lat,
MinZoom = 0,
MaxZoom = 14
};
private static TilesetMetadata SampleMetadata(Extract extract)
=> new(extract.Id, extract.Name, "pbf", 0, 0, 1, 1, 0, 0, 5, 0, 14, []);
private sealed class MemoryStyleCatalog(JsonObject style) : IStyleCatalog
{
public IReadOnlyList<MapStyleInfo> List() => [new("osm-bright", DateTimeOffset.UnixEpoch)];
public JsonObject GetRequired(string name)
=> JsonNode.Parse(style.ToJsonString())!.AsObject();
public Task SaveAsync(string name, JsonObject incoming, CancellationToken ct) => Task.CompletedTask;
public Task SeedDefaultsAsync(CancellationToken ct) => Task.CompletedTask;
}
private sealed class MemoryExtractCatalog(params Extract[] extracts) : IExtractCatalog
{
public IReadOnlyList<Extract> GetAll() => extracts;
public Extract? Find(string id) => extracts.FirstOrDefault(item => item.Id == id);
public Extract GetRequired(string id)
=> Find(id) ?? throw new InvalidOperationException(id);
public Task AddOrUpdateAsync(Extract incoming, CancellationToken ct) => Task.CompletedTask;
public Task RemoveAsync(string id, CancellationToken ct) => Task.CompletedTask;
}
private sealed class MemoryTileStore(TilesetMetadata metadata) : ITileStore
{
public bool Exists(string extractId) => true;
public Task<TileData?> GetTileAsync(string extractId, TileCoordinate tile, CancellationToken ct)
=> Task.FromResult<TileData?>(null);
public TilesetMetadata? GetMetadata(string extractId) => metadata;
public string GetActivePath(string extractId) => extractId;
public string GetStagingPath(string extractId) => extractId + ".building.mbtiles";
public void Activate(string extractId, string stagingPath)
{
}
}
}