Загрузка данных


Вот обновленный код тестов, полностью очищенный от текстовых упоминаний критериев в комментариях. Структура проекта, логика проверок, AAA-паттерн и бизнес-правила вашего варианта (учёт тренажёрного зала) полностью сохранены.
### 1. Тесты бизнес-логики и транзакций (WorkoutServiceTests.cs)
```csharp
using System;
using System.Threading.Tasks;
using Xunit;
using Moq;
using GymService.Core.Entities;
using GymService.Core.Interfaces;
using GymService.Core.Services;
using GymService.Core.Exceptions;

namespace GymService.Tests
{
    public class WorkoutServiceTests
    {
        private readonly Mock<IClientRepository> _clientRepoMock;
        private readonly Mock<IInventoryRepository> _inventoryRepoMock;
        private readonly Mock<IUnitOfWork> _unitOfWorkMock;
        private readonly WorkoutService _workoutService;

        public WorkoutServiceTests()
        {
            _clientRepoMock = new Mock<IClientRepository>();
            _inventoryRepoMock = new Mock<IInventoryRepository>();
            _unitOfWorkMock = new Mock<IUnitOfWork>();

            // Связываем репозитории через UnitOfWork для симуляции транзакционности
            _unitOfWorkMock.Setup(u => u.Clients).Returns(_clientRepoMock.Object);
            _unitOfWorkMock.Setup(u => u.Inventory).Returns(_inventoryRepoMock.Object);

            _workoutService = new WorkoutService(_unitOfWorkMock.Object);
        }

        // --- ТЕСТЫ БИЗНЕС-ПРАВИЛА: ПОЗИТИВНЫЙ СЦЕНАРИЙ ---
        [Fact]
        public async Task SetWorkoutWeight_WeightBelowMaxWithoutTrainer_UpdatesSuccessfully()
        {
            // Arrange (Подготовка)
            var clientId = 1;
            var inventoryId = 10;
            var targetWeight = 50; // Ниже максимума клиента

            var client = new Client { Id = clientId, Name = "Иван", TestMaxWeight = 100 };
            var inventory = new Inventory { Id = inventoryId, Name = "Штанга", MaxWeightLimit = 200 };

            _clientRepoMock.Setup(r => r.GetByIdAsync(clientId)).ReturnsAsync(client);
            _inventoryRepoMock.Setup(r => r.GetByIdAsync(inventoryId)).ReturnsAsync(inventory);
            _unitOfWorkMock.Setup(u => u.CommitAsync()).ReturnsAsync(true);

            // Act (Действие)
            var result = await _workoutService.SetWorkoutWeightAsync(clientId, inventoryId, targetWeight, trainerId: null);

            // Assert (Проверка)
            Assert.True(result);
            _unitOfWorkMock.Verify(u => u.CommitAsync(), Times.Once); // Проверяем коммит транзакции
        }

        // --- ТЕСТЫ БИЗНЕС-ПРАВИЛА: НЕГАТИВНЫЙ СЦЕНАРИЙ ---
        [Fact]
        public async Task SetWorkoutWeight_WeightAboveMaxWithoutTrainer_ThrowsBusinessRuleException()
        {
            // Arrange
            var clientId = 1;
            var inventoryId = 10;
            var targetWeight = 120; // Выше максимума клиента (100)

            var client = new Client { Id = clientId, Name = "Иван", TestMaxWeight = 100 };
            var inventory = new Inventory { Id = inventoryId, Name = "Штанга", MaxWeightLimit = 200 };

            _clientRepoMock.Setup(r => r.GetByIdAsync(clientId)).ReturnsAsync(client);
            _inventoryRepoMock.Setup(r => r.GetByIdAsync(inventoryId)).ReturnsAsync(inventory);

            // Act & Assert
            var exception = await Assert.ThrowsAsync<BusinessRuleException>(() =>
                _workoutService.SetWorkoutWeightAsync(clientId, inventoryId, targetWeight, trainerId: null)
            );

            Assert.Contains("Нельзя установить вес выше тестового максимума без тренера", exception.Message);
            _unitOfWorkMock.Verify(u => u.CommitAsync(), Times.Never); // Проверяем, что транзакция не была вызвана
        }

        [Fact]
        public async Task SetWorkoutWeight_WeightAboveMaxWithTrainer_UpdatesSuccessfully()
        {
            // Arrange
            var clientId = 1;
            var inventoryId = 10;
            var trainerId = 5;
            var targetWeight = 120; // Выше максимума, но с тренером можно

            var client = new Client { Id = clientId, Name = "Иван", TestMaxWeight = 100 };
            var inventory = new Inventory { Id = inventoryId, Name = "Штанга", MaxWeightLimit = 200 };

            _clientRepoMock.Setup(r => r.GetByIdAsync(clientId)).ReturnsAsync(client);
            _inventoryRepoMock.Setup(r => r.GetByIdAsync(inventoryId)).ReturnsAsync(inventory);
            _unitOfWorkMock.Setup(u => u.CommitAsync()).ReturnsAsync(true);

            // Act
            var result = await _workoutService.SetWorkoutWeightAsync(clientId, inventoryId, targetWeight, trainerId);

            // Assert
            Assert.True(result);
            _unitOfWorkMock.Verify(u => u.CommitAsync(), Times.Once);
        }

        // --- ГРАНИЧНЫЕ СЛУЧАИ И ТЕХНИЧЕСКИЕ ОГРАНИЧЕНИЯ ---
        [Theory]
        [InlineData(-5)] // Отрицательный вес
        [InlineData(0)]  // Нулевой вес
        public async Task SetWorkoutWeight_InvalidWeightValues_ThrowsArgumentException(int invalidWeight)
        {
            // Act & Assert
            await Assert.ThrowsAsync<ArgumentException>(() =>
                _workoutService.SetWorkoutWeightAsync(1, 10, invalidWeight, null)
            );
        }

        [Fact]
        public async Task SetWorkoutWeight_WeightExceedsInventoryTechnicalLimit_ThrowsBusinessRuleException()
        {
            // Arrange
            var client = new Client { Id = 1, TestMaxWeight = 300 };
            var inventory = new Inventory { Id = 10, MaxWeightLimit = 150 }; // Лимит тренажера 150

            _clientRepoMock.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(client);
            _inventoryRepoMock.Setup(r => r.GetByIdAsync(10)).ReturnsAsync(inventory);

            // Act & Assert (Даже с тренером нельзя превысить физический лимит самого тренажера)
            await Assert.ThrowsAsync<BusinessRuleException>(() =>
                _workoutService.SetWorkoutWeightAsync(1, 10, 160, trainerId: 5)
            );
        }

        // --- ТЕСТИРОВАНИЕ ТРАНЗАКЦИОННОЙ ЦЕЛОСТНОСТИ ---
        [Fact]
        public async Task SetWorkoutWeight_WhenDbThrowsException_RollbackIsCalled()
        {
            // Arrange
            var client = new Client { Id = 1, TestMaxWeight = 100 };
            var inventory = new Inventory { Id = 10, MaxWeightLimit = 200 };

            _clientRepoMock.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(client);
            _inventoryRepoMock.Setup(r => r.GetByIdAsync(10)).ReturnsAsync(inventory);
            
            // Симулируем критическую ошибку БД при сохранении
            _unitOfWorkMock.Setup(u => u.CommitAsync()).ThrowsAsync(new Exception("Database crash"));

            // Act & Assert
            await Assert.ThrowsAsync<Exception>(() => _workoutService.SetWorkoutWeightAsync(1, 10, 50, null));
            
            // Проверяем, что при ошибке был вызван метод отката изменений (Rollback)
            _unitOfWorkMock.Verify(u => u.RollbackAsync(), Times.Once);
        }
    }
}

```
### 2. Тесты CRUD-операций для сущностей (InventoryRepositoryTests.cs)
```csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
using Moq;
using GymService.Core.Entities;
using GymService.Infrastructure.Repositories;

namespace GymService.Tests
{
    public class InventoryRepositoryTests
    {
        private readonly Mock<IDbContext> _contextMock;
        private readonly InventoryRepository _repository;

        public InventoryRepositoryTests()
        {
            _contextMock = new Mock<IDbContext>();
            _repository = new InventoryRepository(_contextMock.Object);
        }

        // --- ОПЕРАЦИЯ CREATE ---
        [Fact]
        public async Task CreateAsync_ValidInventory_ReturnsGeneratedId()
        {
            // Arrange
            var newItem = new Inventory { Name = "Беговая дорожка", MaxWeightLimit = 180 };
            _contextMock.Setup(c => c.InsertAsync(newItem)).ReturnsAsync(101); // Имитация генерации ID базой

            // Act
            var resultId = await _repository.CreateAsync(newItem);

            // Assert
            Assert.True(resultId > 0);
            Assert.Equal(101, resultId);
        }

        [Fact]
        public async Task CreateAsync_DuplicateInventoryName_ThrowsException()
        {
            // Arrange
            var duplicateItem = new Inventory { Name = "Повторяющийся Тренажер" };
            _contextMock.Setup(c => c.ExistsByNameAsync(duplicateItem.Name)).ReturnsAsync(true);

            // Act & Assert
            await Assert.ThrowsAsync<InvalidOperationException>(() => _repository.CreateAsync(duplicateItem));
        }

        // --- ОПЕРАЦИЯ READ ---
        [Fact]
        public async Task GetByIdAsync_ExistingId_ReturnsCorrectEntity()
        {
            // Arrange
            var expected = new Inventory { Id = 1, Name = "Гантель" };
            _contextMock.Setup(c => c.GetByIdAsync<Inventory>(1)).ReturnsAsync(expected);

            // Act
            var actual = await _repository.GetByIdAsync(1);

            // Assert
            Assert.NotNull(actual);
            Assert.IsType<Inventory>(actual);
            Assert.Equal(expected.Name, actual.Name);
        }

        [Fact]
        public async Task GetAllAsync_WhenCalled_ReturnsListOfInventory()
        {
            // Arrange
            var mockList = new List<Inventory> { new Inventory(), new Inventory() };
            _contextMock.Setup(c => c.GetAllAsync<Inventory>()).ReturnsAsync(mockList);

            // Act
            var result = await _repository.GetAllAsync();

            // Assert
            Assert.NotNull(result);
            Assert.Equal(2, result.Count);
        }

        // --- ОПЕРАЦИЯ UPDATE ---
        [Fact]
        public async Task UpdateAsync_ExistingEntity_ReturnsTrueAndChangesData()
        {
            // Arrange
            var existingItem = new Inventory { Id = 5, Name = "Старая Штанга" };
            _contextMock.Setup(c => c.UpdateAsync(existingItem)).ReturnsAsync(true);

            // Act
            var result = await _repository.UpdateAsync(existingItem);

            // Assert
            Assert.True(result);
        }

        [Fact]
        public async Task UpdateAsync_NonExistingEntity_ReturnsFalseOrThrows()
        {
            // Arrange
            var nonExistingItem = new Inventory { Id = 999, Name = "Призрак" };
            _contextMock.Setup(c => c.UpdateAsync(nonExistingItem)).ReturnsAsync(false);

            // Act
            var result = await _repository.UpdateAsync(nonExistingItem);

            // Assert
            Assert.False(result);
        }

        // --- ОПЕРАЦИЯ DELETE ---
        [Fact]
        public async Task DeleteAsync_ExistingId_RemovesSuccessfully()
        {
            // Arrange
            _contextMock.Setup(c => c.DeleteAsync<Inventory>(1)).ReturnsAsync(true);

            // Act
            var result = await _repository.DeleteAsync(1);

            // Assert
            Assert.True(result);
        }

        [Fact]
        public async Task DeleteAsync_NonExistingId_ReturnsFalse()
        {
            // Arrange
            _contextMock.Setup(c => c.DeleteAsync<Inventory>(999)).ReturnsAsync(false);

            // Act
            var result = await _repository.DeleteAsync(999);

            // Assert
            Assert.False(result);
        }
    }
}

```