using System.Text; using Server.WatchBody.Web.Config; using Server.WatchBody.Web.Dto; using Xunit; namespace Server.WatchBody.Web.Tests; /// Юнит-тесты: валидация конфига и парсер пакета sync (без БД). 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(() => SyncItemParser.Parse("""{"client_guid":"..."}""")); } [Fact] public void Parser_UnknownEntity_Rejected() { Assert.Throws(() => SyncItemParser.Parse( """[{"client_guid":"0b8f6e2a-1111-4222-8333-444455556666","entity":"nonsense","op":"upsert","data":{}}]""")); } [Fact] public void Parser_BadOp_Rejected() { Assert.Throws(() => 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(() => SyncItemParser.Parse(sb.ToString())); } [Fact] public void Parser_BreakSourceAuto_Rejected() { Assert.Throws(() => 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(() => 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(() => 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")); } }