111 lines
3.9 KiB
C#
111 lines
3.9 KiB
C#
using System.Text;
|
|
using Server.WatchBody.Web.Config;
|
|
using Server.WatchBody.Web.Dto;
|
|
using Xunit;
|
|
|
|
namespace Server.WatchBody.Web.Tests;
|
|
|
|
/// <summary>Юнит-тесты: валидация конфига и парсер пакета sync (без БД).</summary>
|
|
public class UnitTests
|
|
{
|
|
private static ServerConfig Valid() => new()
|
|
{
|
|
ApiKey = "owner",
|
|
DbConnectionString = "Host=x",
|
|
Athletes = [new ConfigAthlete { Name = "A", ApiKey = "k1" }],
|
|
};
|
|
|
|
[Fact]
|
|
public void Config_EmptyApiKey_IsError()
|
|
{
|
|
var config = Valid();
|
|
config.ApiKey = "";
|
|
Assert.Contains(config.Validate(), e => e.Contains("Api.Key"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Config_DuplicateAthleteKeys_IsError()
|
|
{
|
|
var config = Valid();
|
|
config.Athletes.Add(new ConfigAthlete { Name = "B", ApiKey = "k1" });
|
|
Assert.Contains(config.Validate(), e => e.Contains("дубликат"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Config_AthleteKeyEqualsOwnerKey_IsError()
|
|
{
|
|
var config = Valid();
|
|
config.Athletes[0].ApiKey = "owner";
|
|
Assert.NotEmpty(config.Validate());
|
|
}
|
|
|
|
[Fact]
|
|
public void Config_Valid_NoErrors() => Assert.Empty(Valid().Validate());
|
|
|
|
[Fact]
|
|
public void Parser_NotArray_Rejected()
|
|
{
|
|
Assert.Throws<SyncException>(() => SyncItemParser.Parse("""{"client_guid":"..."}"""));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parser_UnknownEntity_Rejected()
|
|
{
|
|
Assert.Throws<SyncException>(() => SyncItemParser.Parse(
|
|
"""[{"client_guid":"0b8f6e2a-1111-4222-8333-444455556666","entity":"nonsense","op":"upsert","data":{}}]"""));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parser_BadOp_Rejected()
|
|
{
|
|
Assert.Throws<SyncException>(() => SyncItemParser.Parse(
|
|
"""[{"client_guid":"0b8f6e2a-1111-4222-8333-444455556666","entity":"daily_log","op":"delete","data":{}}]"""));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parser_PackageOver50_Rejected()
|
|
{
|
|
var sb = new StringBuilder("[");
|
|
for (var i = 0; i < 51; i++)
|
|
{
|
|
if (i > 0) sb.Append(',');
|
|
sb.Append($$"""{"client_guid":"0b8f6e2a-0000-4000-8000-{{i:D12}}","entity":"daily_log","op":"upsert","data":{"date":"2026-10-17" } }""");
|
|
}
|
|
sb.Append(']');
|
|
Assert.Throws<SyncException>(() => SyncItemParser.Parse(sb.ToString()));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parser_BreakSourceAuto_Rejected()
|
|
{
|
|
Assert.Throws<SyncException>(() => SyncItemParser.Parse(
|
|
"""[{"client_guid":"0b8f6e2a-1111-4222-8333-444455556666","entity":"break","op":"upsert","data":{"started_on":"2026-10-01","source":"auto"}}]"""));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parser_RpeOutOfRange_Rejected()
|
|
{
|
|
Assert.Throws<SyncException>(() => SyncItemParser.Parse(
|
|
"""[{"client_guid":"0b8f6e2a-1111-4222-8333-444455556666","entity":"workout_set","op":"upsert","data":{"workout_id":"0b8f6e2a-1111-4222-8333-444455557777","seq":1,"weight_kg":50,"reps":10,"done_at":"2026-10-17T10:05:00Z","source":"voice","in_plan":true,"rpe":11}}]"""));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parser_MissingRequiredField_Rejected()
|
|
{
|
|
Assert.Throws<SyncException>(() => SyncItemParser.Parse(
|
|
"""[{"client_guid":"0b8f6e2a-1111-4222-8333-444455556666","entity":"workout","op":"upsert","data":{"status":"active"}}]"""));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parser_ValidPackage_Parses()
|
|
{
|
|
var items = SyncItemParser.Parse(
|
|
"""[{"client_guid":"0b8f6e2a-1111-4222-8333-444455556666","entity":"profile","op":"upsert","data":{"sex":"m","birth_year":1980,"weight_kg":85.5,"hr_max":185}}]""");
|
|
var item = Assert.Single(items);
|
|
Assert.Equal("profile", item.Entity);
|
|
Assert.Equal("m", SyncItemParser.StringOrNull(item.Data, "sex"));
|
|
Assert.Equal(1980, SyncItemParser.IntOrNull(item.Data, "birth_year"));
|
|
Assert.Equal(85.5m, SyncItemParser.DecimalOrNull(item.Data, "weight_kg"));
|
|
}
|
|
}
|