diff --git a/backend/src/MyBiz.Core/AuthService.cs b/backend/src/MyBiz.Core/AuthService.cs
new file mode 100644
index 0000000..87b3d42
--- /dev/null
+++ b/backend/src/MyBiz.Core/AuthService.cs
@@ -0,0 +1,176 @@
+namespace MyBiz.Core;
+
+///
+/// Результат аутентификации
+///
+public class AuthResult
+{
+ ///
+ /// Успешна ли аутентификация
+ ///
+ public bool Success { get; set; }
+
+ ///
+ /// Пользователь (если успешно)
+ ///
+ public User? User { get; set; }
+
+ ///
+ /// Токен доступа (заглушка)
+ ///
+ public string? Token { get; set; }
+
+ ///
+ /// Сообщение об ошибке
+ ///
+ public string? ErrorMessage { get; set; }
+}
+
+///
+/// Сервис аутентификации (заглушка для MVP)
+///
+public class AuthService
+{
+ private readonly Dictionary _usersByUsername = new();
+ private readonly Dictionary _usersById = new();
+ private readonly Dictionary _tokens = new(); // token -> userId
+
+ ///
+ /// Зарегистрировать нового пользователя
+ ///
+ public AuthResult Register(string username, string email, string password, string companyName)
+ {
+ if (_usersByUsername.ContainsKey(username))
+ {
+ return new AuthResult
+ {
+ Success = false,
+ ErrorMessage = "Пользователь с таким именем уже существует"
+ };
+ }
+
+ var user = new User
+ {
+ Username = username,
+ Email = email,
+ PasswordHash = HashPassword(password),
+ Company = new Company
+ {
+ Name = companyName,
+ Cash = 100000m, // Стартовый капитал
+ Assets = 0,
+ Liabilities = 0
+ }
+ };
+
+ user.CompanyId = user.Company.Id;
+
+ _usersByUsername[username] = user;
+ _usersById[user.Id] = user;
+
+ var token = GenerateToken(user.Id);
+ _tokens[token] = user.Id; // Сохраняем токен
+
+ return new AuthResult
+ {
+ Success = true,
+ User = user,
+ Token = token
+ };
+ }
+
+ ///
+ /// Войти в систему
+ ///
+ public AuthResult Login(string username, string password)
+ {
+ if (!_usersByUsername.TryGetValue(username, out var user))
+ {
+ return new AuthResult
+ {
+ Success = false,
+ ErrorMessage = "Пользователь не найден"
+ };
+ }
+
+ if (!VerifyPassword(password, user.PasswordHash))
+ {
+ return new AuthResult
+ {
+ Success = false,
+ ErrorMessage = "Неверный пароль"
+ };
+ }
+
+ user.LastLoginAt = DateTime.UtcNow;
+
+ return new AuthResult
+ {
+ Success = true,
+ User = user,
+ Token = GenerateToken(user.Id)
+ };
+ }
+
+ ///
+ /// Выйти из системы
+ ///
+ public void Logout(string token)
+ {
+ if (_tokens.ContainsKey(token))
+ {
+ _tokens.Remove(token);
+ }
+ }
+
+ ///
+ /// Проверить токен и получить пользователя
+ ///
+ public User? ValidateToken(string token)
+ {
+ if (_tokens.TryGetValue(token, out var userId))
+ {
+ return _usersById.TryGetValue(userId, out var user) ? user : null;
+ }
+ return null;
+ }
+
+ ///
+ /// Получить пользователя по ID
+ ///
+ public User? GetUserById(Guid userId)
+ {
+ return _usersById.TryGetValue(userId, out var user) ? user : null;
+ }
+
+ ///
+ /// Получить пользователя по имени
+ ///
+ public User? GetUserByUsername(string username)
+ {
+ return _usersByUsername.TryGetValue(username, out var user) ? user : null;
+ }
+
+ #region Helpers (заглушки)
+
+ private static string HashPassword(string password)
+ {
+ // TODO: Использовать реальное хеширование (BCrypt, PBKDF2)
+ return $"hash_{password}";
+ }
+
+ private static bool VerifyPassword(string password, string hash)
+ {
+ // TODO: Использовать реальную проверку пароля
+ return hash == $"hash_{password}";
+ }
+
+ private static string GenerateToken(Guid userId)
+ {
+ // TODO: Использовать JWT или аналогичный механизм
+ var token = $"token_{userId}_{Guid.NewGuid()}";
+ return token;
+ }
+
+ #endregion
+}
diff --git a/backend/src/MyBiz.Core/BusinessUnit.cs b/backend/src/MyBiz.Core/BusinessUnit.cs
new file mode 100644
index 0000000..4e94bcc
--- /dev/null
+++ b/backend/src/MyBiz.Core/BusinessUnit.cs
@@ -0,0 +1,437 @@
+namespace MyBiz.Core;
+
+///
+/// Тип бизнес-единицы
+///
+public enum BusinessUnitType
+{
+ ///
+ /// Магазин (розничная торговля)
+ ///
+ Shop,
+
+ ///
+ /// Фабрика (производство)
+ ///
+ Factory,
+
+ ///
+ /// Склад (хранение)
+ ///
+ Warehouse,
+
+ ///
+ /// Офис (управление)
+ ///
+ Office,
+
+ ///
+ /// Научный центр (исследования)
+ ///
+ ResearchLab
+}
+
+///
+/// Базовая бизнес-единица (магазин, фабрика и т.д.)
+///
+public class BusinessUnit
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+
+ ///
+ /// Название единицы
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// Тип бизнес-единицы
+ ///
+ public BusinessUnitType Type { get; set; }
+
+ ///
+ /// ID компании-владельца
+ ///
+ public Guid CompanyId { get; set; }
+
+ ///
+ /// Город, где находится единица
+ ///
+ public string CityId { get; set; } = string.Empty;
+
+ ///
+ /// Координаты на карте
+ ///
+ public int X { get; set; }
+ public int Y { get; set; }
+
+ ///
+ /// Уровень развития (1-max)
+ ///
+ public int Level { get; set; } = 1;
+
+ ///
+ /// Максимальный уровень
+ ///
+ public int MaxLevel { get; set; } = 10;
+
+ ///
+ /// Количество сотрудников
+ ///
+ public int Employees { get; set; }
+
+ ///
+ /// Максимальное количество сотрудников
+ ///
+ public int MaxEmployees { get; set; } = 50;
+
+ ///
+ /// Эффективность работы (0-100)
+ ///
+ public int Efficiency { get; set; } = 100;
+
+ ///
+ /// Стоимость постройки
+ ///
+ public decimal BuildCost { get; set; }
+
+ ///
+ /// Содержание в тик (зарплаты, аренда и т.д.)
+ ///
+ public decimal UpkeepCost { get; set; }
+
+ ///
+ /// Доход за последний период
+ ///
+ public decimal LastPeriodIncome { get; set; }
+
+ ///
+ /// Расходы за последний период
+ ///
+ public decimal LastPeriodExpenses { get; set; }
+
+ ///
+ /// Дата постройки
+ ///
+ public int BuiltAtTick { get; set; }
+
+ ///
+ /// Активна ли единица (работает)
+ ///
+ public bool IsActive { get; set; } = true;
+
+ ///
+ /// Ссылка на здание (если есть)
+ ///
+ public Building? Building { get; set; }
+
+ ///
+ /// Запасы на складе (для магазинов/складов)
+ ///
+ public Dictionary Inventory { get; set; } = new();
+
+ ///
+ /// Производственная цепочка (для фабрик)
+ ///
+ public ActiveProductionChain? ActiveProduction { get; set; }
+
+ ///
+ /// Рассчитать прибыль за период
+ ///
+ public decimal PeriodProfit => LastPeriodIncome - LastPeriodExpenses;
+
+ ///
+ /// Рассчитать рентабельность (%)
+ ///
+ public decimal Profitability => LastPeriodExpenses > 0
+ ? (PeriodProfit / LastPeriodExpenses) * 100
+ : 0;
+
+ ///
+ /// Обновить эффективность на основе сотрудников
+ ///
+ public void UpdateEfficiency()
+ {
+ if (MaxEmployees == 0)
+ {
+ Efficiency = 0;
+ return;
+ }
+
+ // Базовая эффективность зависит от заполненности рабочих мест
+ var staffRate = (decimal)Employees / MaxEmployees;
+ Efficiency = (int)(100 * staffRate);
+
+ // Бонус за уровень
+ Efficiency += (Level - 1) * 5;
+
+ // Ограничение 0-100
+ Efficiency = Math.Clamp(Efficiency, 0, 100);
+ }
+
+ ///
+ /// Обновить состояние (вызывается каждый тик)
+ ///
+ public virtual void Tick()
+ {
+ // Обновление производства (если есть)
+ ActiveProduction?.Tick();
+
+ // Обновление эффективности
+ UpdateEfficiency();
+ }
+
+ ///
+ /// Нанять сотрудника
+ ///
+ public bool HireEmployee()
+ {
+ if (Employees < MaxEmployees)
+ {
+ Employees++;
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Уволить сотрудника
+ ///
+ public bool FireEmployee()
+ {
+ if (Employees > 0)
+ {
+ Employees--;
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Повысить уровень
+ ///
+ public bool Upgrade()
+ {
+ if (Level < MaxLevel)
+ {
+ Level++;
+ MaxEmployees += 10; // Бонус за уровень
+ return true;
+ }
+ return false;
+ }
+}
+
+///
+/// Магазин (розничная торговля)
+///
+public class Shop : BusinessUnit
+{
+ public Shop()
+ {
+ Type = BusinessUnitType.Shop;
+ MaxEmployees = 20;
+ BuildCost = 50000m;
+ UpkeepCost = 1000m;
+ }
+
+ ///
+ /// Торгуемые продукты
+ ///
+ public List SoldProductTypes { get; set; } = new();
+
+ ///
+ /// Выручка за сегодня
+ ///
+ public decimal DailyRevenue { get; set; }
+
+ ///
+ /// Количество клиентов за сегодня
+ ///
+ public int DailyCustomers { get; set; }
+
+ ///
+ /// Средний чек
+ ///
+ public decimal AverageCheck => DailyCustomers > 0 ? DailyRevenue / DailyCustomers : 0;
+}
+
+///
+/// Фабрика (производство)
+///
+public class Factory : BusinessUnit
+{
+ public Factory()
+ {
+ Type = BusinessUnitType.Factory;
+ MaxEmployees = 100;
+ BuildCost = 200000m;
+ UpkeepCost = 5000m;
+ }
+
+ ///
+ /// Производимый продукт
+ ///
+ public string? OutputProductId { get; set; }
+
+ ///
+ /// Требуемые ресурсы (входные продукты)
+ ///
+ public Dictionary RequiredInputs { get; set; } = new();
+
+ ///
+ /// Произведено за последний тик
+ ///
+ public int LastTickOutput { get; set; }
+
+ ///
+ /// Простой (нет ресурсов)
+ ///
+ public bool IsIdle => ActiveProduction == null || !ActiveProduction.IsActive;
+}
+
+///
+/// Склад (хранение)
+///
+public class Warehouse : BusinessUnit
+{
+ public Warehouse()
+ {
+ Type = BusinessUnitType.Warehouse;
+ MaxEmployees = 10;
+ BuildCost = 30000m;
+ UpkeepCost = 500m;
+ }
+
+ ///
+ /// Вместимость склада (единиц товара)
+ ///
+ public int Capacity { get; set; } = 10000;
+
+ ///
+ /// Использовано места
+ ///
+ public int UsedCapacity => Inventory.Values.Sum();
+
+ ///
+ /// Свободно места
+ ///
+ public int FreeCapacity => Capacity - UsedCapacity;
+
+ ///
+ /// Добавить товар на склад
+ ///
+ public bool AddToInventory(string productId, int quantity)
+ {
+ if (quantity > FreeCapacity)
+ {
+ return false;
+ }
+
+ if (Inventory.ContainsKey(productId))
+ {
+ Inventory[productId] += quantity;
+ }
+ else
+ {
+ Inventory[productId] = quantity;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Взять товар со склада
+ ///
+ public int RemoveFromInventory(string productId, int quantity)
+ {
+ if (!Inventory.ContainsKey(productId))
+ {
+ return 0;
+ }
+
+ var actualQuantity = Math.Min(quantity, Inventory[productId]);
+ Inventory[productId] -= actualQuantity;
+
+ if (Inventory[productId] == 0)
+ {
+ Inventory.Remove(productId);
+ }
+
+ return actualQuantity;
+ }
+}
+
+///
+/// Офис (управление компанией)
+///
+public class Office : BusinessUnit
+{
+ public Office()
+ {
+ Type = BusinessUnitType.Office;
+ MaxEmployees = 50;
+ BuildCost = 100000m;
+ UpkeepCost = 3000m;
+ }
+
+ ///
+ /// Бонус к управлению (влияет на эффективность других зданий)
+ ///
+ public int ManagementBonus { get; set; } = 5;
+
+ ///
+ /// Количество управляемых зданий
+ ///
+ public int ManagedBuildings { get; set; }
+}
+
+///
+/// Научный центр (исследования)
+///
+public class ResearchLab : BusinessUnit
+{
+ public ResearchLab()
+ {
+ Type = BusinessUnitType.ResearchLab;
+ MaxEmployees = 30;
+ BuildCost = 150000m;
+ UpkeepCost = 4000m;
+ }
+
+ ///
+ /// Текущее исследование
+ ///
+ public string? CurrentResearch { get; set; }
+
+ ///
+ /// Прогресс исследования (0-100%)
+ ///
+ public int ResearchProgress { get; set; }
+
+ ///
+ /// Наука за тик
+ ///
+ public int SciencePerTick => Employees * 10 * Efficiency / 100;
+
+ ///
+ /// Начать исследование
+ ///
+ public void StartResearch(string technologyId)
+ {
+ CurrentResearch = technologyId;
+ ResearchProgress = 0;
+ }
+
+ ///
+ /// Обновление исследования
+ ///
+ public override void Tick()
+ {
+ base.Tick();
+
+ if (CurrentResearch != null && ResearchProgress < 100)
+ {
+ ResearchProgress = Math.Min(100, ResearchProgress + SciencePerTick);
+ }
+ }
+}
diff --git a/backend/src/MyBiz.Core/Company.cs b/backend/src/MyBiz.Core/Company.cs
index a1d228d..47ef743 100644
--- a/backend/src/MyBiz.Core/Company.cs
+++ b/backend/src/MyBiz.Core/Company.cs
@@ -7,44 +7,124 @@ public class Company
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; } = string.Empty;
-
+
+ ///
+ /// ID владельца (пользователя)
+ ///
+ public Guid OwnerId { get; set; }
+
///
/// Доступные деньги
///
public decimal Cash { get; set; }
-
+
///
/// Активы компании
///
public decimal Assets { get; set; }
-
+
///
/// Пассивы (долги)
///
public decimal Liabilities { get; set; }
-
+
///
/// Прибыль за последний период
///
public decimal LastPeriodProfit { get; set; }
-
+
///
/// Список зданий компании
///
public List Buildings { get; set; } = new();
-
+
+ ///
+ /// Бизнес-единицы компании (магазины, фабрики и т.д.)
+ ///
+ public List BusinessUnits { get; set; } = new();
+
///
/// Складские запасы
///
public Dictionary Inventory { get; set; } = new();
-
+
///
/// Открытые технологии
///
public HashSet UnlockedTechnologies { get; set; } = new();
-
+
+ ///
+ /// Дата основания
+ ///
+ public DateTime FoundedAt { get; set; } = DateTime.UtcNow;
+
///
/// Рассчитать чистую стоимость
///
public decimal NetWorth => Assets - Liabilities + Cash;
+
+ ///
+ /// Получить все магазины
+ ///
+ public IEnumerable Shops => BusinessUnits.OfType();
+
+ ///
+ /// Получить все фабрики
+ ///
+ public IEnumerable Factories => BusinessUnits.OfType();
+
+ ///
+ /// Получить все склады
+ ///
+ public IEnumerable Warehouses => BusinessUnits.OfType();
+
+ ///
+ /// Добавить бизнес-единицу
+ ///
+ public void AddBusinessUnit(BusinessUnit unit)
+ {
+ unit.CompanyId = Id;
+ BusinessUnits.Add(unit);
+ }
+
+ ///
+ /// Удалить бизнес-единицу
+ ///
+ public void RemoveBusinessUnit(Guid unitId)
+ {
+ var unit = BusinessUnits.FirstOrDefault(u => u.Id == unitId);
+ if (unit != null)
+ {
+ BusinessUnits.Remove(unit);
+ }
+ }
+
+ ///
+ /// Обновить состояние всех единиц (вызывается каждый тик)
+ ///
+ public void Tick()
+ {
+ foreach (var unit in BusinessUnits)
+ {
+ unit.Tick();
+ }
+ }
+
+ ///
+ /// Рассчитать общую прибыль за период
+ ///
+ public decimal CalculateTotalProfit()
+ {
+ return BusinessUnits.Sum(u => u.PeriodProfit);
+ }
+
+ ///
+ /// Рассчитать общую стоимость активов
+ ///
+ public decimal CalculateAssets()
+ {
+ return Buildings.Sum(b => b.TypeConfig?.BuildCost ?? 0) +
+ BusinessUnits.Sum(u => u.BuildCost) +
+ Inventory.Sum(i => i.Key.BasePrice * i.Value);
+ }
}
diff --git a/backend/src/MyBiz.Core/User.cs b/backend/src/MyBiz.Core/User.cs
new file mode 100644
index 0000000..35b03a8
--- /dev/null
+++ b/backend/src/MyBiz.Core/User.cs
@@ -0,0 +1,90 @@
+namespace MyBiz.Core;
+
+///
+/// Пользователь системы
+///
+public class User
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+
+ ///
+ /// Имя пользователя (логин)
+ ///
+ public string Username { get; set; } = string.Empty;
+
+ ///
+ /// Email
+ ///
+ public string Email { get; set; } = string.Empty;
+
+ ///
+ /// Хеш пароля (не хранит сам пароль)
+ ///
+ public string PasswordHash { get; set; } = string.Empty;
+
+ ///
+ /// Дата регистрации
+ ///
+ public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
+
+ ///
+ /// Дата последнего входа
+ ///
+ public DateTime? LastLoginAt { get; set; }
+
+ ///
+ /// Активен ли пользователь
+ ///
+ public bool IsActive { get; set; } = true;
+
+ ///
+ /// Компания, принадлежащая пользователю
+ ///
+ public Company? Company { get; set; }
+
+ ///
+ /// ID компании (для быстрого доступа)
+ ///
+ public Guid? CompanyId { get; set; }
+
+ ///
+ /// Статистика игры
+ ///
+ public UserStats Stats { get; set; } = new();
+}
+
+///
+/// Статистика пользователя
+///
+public class UserStats
+{
+ ///
+ /// Всего игр сыграно
+ ///
+ public int GamesPlayed { get; set; }
+
+ ///
+ /// Всего часов в игре
+ ///
+ public int TotalHoursPlayed { get; set; }
+
+ ///
+ /// Максимальная чистая стоимость за всё время
+ ///
+ public decimal MaxNetWorth { get; set; }
+
+ ///
+ /// Всего зданий построено
+ ///
+ public int TotalBuildingsBuilt { get; set; }
+
+ ///
+ /// Дата начала первой игры
+ ///
+ public DateTime? FirstGameDate { get; set; }
+
+ ///
+ /// Дата последней игры
+ ///
+ public DateTime? LastGameDate { get; set; }
+}
diff --git a/backend/tests/MyBiz.Tests/UserTests.cs b/backend/tests/MyBiz.Tests/UserTests.cs
new file mode 100644
index 0000000..7749731
--- /dev/null
+++ b/backend/tests/MyBiz.Tests/UserTests.cs
@@ -0,0 +1,561 @@
+using Xunit;
+using MyBiz.Core;
+
+namespace MyBiz.Tests;
+
+public class UserTests
+{
+ [Fact]
+ public void User_Creation_ShouldInitializeProperties()
+ {
+ var user = new User
+ {
+ Username = "testuser",
+ Email = "test@example.com"
+ };
+
+ Assert.NotNull(user.Id);
+ Assert.Equal("testuser", user.Username);
+ Assert.Equal("test@example.com", user.Email);
+ Assert.True(user.IsActive);
+ Assert.Null(user.Company);
+ Assert.NotNull(user.Stats);
+ }
+
+ [Fact]
+ public void User_Company_Assignment_ShouldWork()
+ {
+ var user = new User();
+ var company = new Company { Name = "Test Corp" };
+
+ user.Company = company;
+ user.CompanyId = company.Id;
+
+ Assert.NotNull(user.Company);
+ Assert.Equal(company.Id, user.CompanyId);
+ Assert.Equal("Test Corp", user.Company.Name);
+ }
+
+ [Fact]
+ public void UserStats_ShouldInitializeDefaults()
+ {
+ var stats = new UserStats();
+
+ Assert.Equal(0, stats.GamesPlayed);
+ Assert.Equal(0, stats.TotalHoursPlayed);
+ Assert.Equal(0, stats.MaxNetWorth);
+ Assert.Equal(0, stats.TotalBuildingsBuilt);
+ Assert.Null(stats.FirstGameDate);
+ Assert.Null(stats.LastGameDate);
+ }
+}
+
+public class AuthServiceTests
+{
+ [Fact]
+ public void AuthService_Register_ShouldCreateUser()
+ {
+ var authService = new AuthService();
+
+ var result = authService.Register("testuser", "test@example.com", "password123", "Test Corp");
+
+ Assert.True(result.Success);
+ Assert.NotNull(result.User);
+ Assert.NotNull(result.Token);
+ Assert.Equal("testuser", result.User.Username);
+ Assert.NotNull(result.User.Company);
+ Assert.Equal("Test Corp", result.User.Company.Name);
+ }
+
+ [Fact]
+ public void AuthService_Register_DuplicateUsername_ShouldFail()
+ {
+ var authService = new AuthService();
+
+ authService.Register("testuser", "test@example.com", "password123", "Test Corp");
+ var result = authService.Register("testuser", "other@example.com", "password456", "Other Corp");
+
+ Assert.False(result.Success);
+ Assert.NotNull(result.ErrorMessage);
+ Assert.Null(result.User);
+ }
+
+ [Fact]
+ public void AuthService_Login_CorrectCredentials_ShouldSucceed()
+ {
+ var authService = new AuthService();
+
+ authService.Register("testuser", "test@example.com", "password123", "Test Corp");
+ var result = authService.Login("testuser", "password123");
+
+ Assert.True(result.Success);
+ Assert.NotNull(result.User);
+ Assert.NotNull(result.Token);
+ Assert.Equal("testuser", result.User.Username);
+ }
+
+ [Fact]
+ public void AuthService_Login_WrongPassword_ShouldFail()
+ {
+ var authService = new AuthService();
+
+ authService.Register("testuser", "test@example.com", "password123", "Test Corp");
+ var result = authService.Login("testuser", "wrongpassword");
+
+ Assert.False(result.Success);
+ Assert.NotNull(result.ErrorMessage);
+ Assert.Null(result.User);
+ }
+
+ [Fact]
+ public void AuthService_Login_NonExistentUser_ShouldFail()
+ {
+ var authService = new AuthService();
+
+ var result = authService.Login("nonexistent", "password123");
+
+ Assert.False(result.Success);
+ Assert.NotNull(result.ErrorMessage);
+ Assert.Null(result.User);
+ }
+
+ [Fact]
+ public void AuthService_Logout_ShouldInvalidateToken()
+ {
+ var authService = new AuthService();
+
+ var registerResult = authService.Register("testuser", "test@example.com", "password123", "Test Corp");
+ var token = registerResult.Token!;
+
+ authService.Logout(token);
+ var user = authService.ValidateToken(token);
+
+ Assert.Null(user);
+ }
+
+ [Fact]
+ public void AuthService_ValidateToken_ValidToken_ShouldReturnUser()
+ {
+ var authService = new AuthService();
+
+ var result = authService.Register("testuser", "test@example.com", "password123", "Test Corp");
+ var user = authService.ValidateToken(result.Token!);
+
+ Assert.NotNull(user);
+ Assert.Equal("testuser", user.Username);
+ }
+
+ [Fact]
+ public void AuthService_GetUserById_ShouldReturnUser()
+ {
+ var authService = new AuthService();
+
+ var result = authService.Register("testuser", "test@example.com", "password123", "Test Corp");
+ var user = authService.GetUserById(result.User!.Id);
+
+ Assert.NotNull(user);
+ Assert.Equal(result.User.Id, user.Id);
+ }
+}
+
+public class BusinessUnitTests
+{
+ [Fact]
+ public void BusinessUnit_Creation_ShouldInitializeProperties()
+ {
+ var unit = new BusinessUnit
+ {
+ Name = "Test Unit",
+ Type = BusinessUnitType.Shop,
+ CompanyId = Guid.NewGuid(),
+ CityId = "city_1",
+ X = 100,
+ Y = 200
+ };
+
+ Assert.NotNull(unit.Id);
+ Assert.Equal("Test Unit", unit.Name);
+ Assert.Equal(BusinessUnitType.Shop, unit.Type);
+ Assert.Equal(1, unit.Level);
+ Assert.Equal(100, unit.Efficiency);
+ Assert.True(unit.IsActive);
+ }
+
+ [Fact]
+ public void BusinessUnit_HireEmployee_ShouldIncreaseCount()
+ {
+ var unit = new BusinessUnit { MaxEmployees = 10 };
+
+ var hired = unit.HireEmployee();
+
+ Assert.True(hired);
+ Assert.Equal(1, unit.Employees);
+ }
+
+ [Fact]
+ public void BusinessUnit_HireEmployee_FullCapacity_ShouldFail()
+ {
+ var unit = new BusinessUnit { MaxEmployees = 1, Employees = 1 };
+
+ var hired = unit.HireEmployee();
+
+ Assert.False(hired);
+ Assert.Equal(1, unit.Employees);
+ }
+
+ [Fact]
+ public void BusinessUnit_FireEmployee_ShouldDecreaseCount()
+ {
+ var unit = new BusinessUnit { Employees = 5 };
+
+ var fired = unit.FireEmployee();
+
+ Assert.True(fired);
+ Assert.Equal(4, unit.Employees);
+ }
+
+ [Fact]
+ public void BusinessUnit_FireEmployee_NoEmployees_ShouldFail()
+ {
+ var unit = new BusinessUnit { Employees = 0 };
+
+ var fired = unit.FireEmployee();
+
+ Assert.False(fired);
+ Assert.Equal(0, unit.Employees);
+ }
+
+ [Fact]
+ public void BusinessUnit_Upgrade_ShouldIncreaseLevel()
+ {
+ var unit = new BusinessUnit { Level = 1, MaxLevel = 10 };
+
+ var upgraded = unit.Upgrade();
+
+ Assert.True(upgraded);
+ Assert.Equal(2, unit.Level);
+ }
+
+ [Fact]
+ public void BusinessUnit_Upgrade_MaxLevel_ShouldFail()
+ {
+ var unit = new BusinessUnit { Level = 10, MaxLevel = 10 };
+
+ var upgraded = unit.Upgrade();
+
+ Assert.False(upgraded);
+ Assert.Equal(10, unit.Level);
+ }
+
+ [Fact]
+ public void BusinessUnit_UpdateEfficiency_FullStaff_ShouldBeMax()
+ {
+ var unit = new BusinessUnit { MaxEmployees = 10, Employees = 10 };
+
+ unit.UpdateEfficiency();
+
+ Assert.Equal(100, unit.Efficiency);
+ }
+
+ [Fact]
+ public void BusinessUnit_UpdateEfficiency_HalfStaff_ShouldBeReduced()
+ {
+ var unit = new BusinessUnit { MaxEmployees = 10, Employees = 5 };
+
+ unit.UpdateEfficiency();
+
+ Assert.Equal(50, unit.Efficiency);
+ }
+
+ [Fact]
+ public void BusinessUnit_UpdateEfficiency_NoStaff_ShouldBeZero()
+ {
+ var unit = new BusinessUnit { MaxEmployees = 10, Employees = 0 };
+
+ unit.UpdateEfficiency();
+
+ Assert.Equal(0, unit.Efficiency);
+ }
+}
+
+public class ShopTests
+{
+ [Fact]
+ public void Shop_Creation_ShouldInitializeDefaults()
+ {
+ var shop = new Shop();
+
+ Assert.Equal(BusinessUnitType.Shop, shop.Type);
+ Assert.Equal(20, shop.MaxEmployees);
+ Assert.Equal(50000m, shop.BuildCost);
+ Assert.Equal(1000m, shop.UpkeepCost);
+ Assert.NotNull(shop.SoldProductTypes);
+ Assert.Equal(0, shop.DailyRevenue);
+ Assert.Equal(0, shop.DailyCustomers);
+ }
+
+ [Fact]
+ public void Shop_AverageCheck_Calculation_ShouldBeCorrect()
+ {
+ var shop = new Shop { DailyRevenue = 1000m, DailyCustomers = 10 };
+
+ var avgCheck = shop.AverageCheck;
+
+ Assert.Equal(100m, avgCheck);
+ }
+
+ [Fact]
+ public void Shop_AverageCheck_NoCustomers_ShouldBeZero()
+ {
+ var shop = new Shop { DailyRevenue = 1000m, DailyCustomers = 0 };
+
+ var avgCheck = shop.AverageCheck;
+
+ Assert.Equal(0, avgCheck);
+ }
+}
+
+public class FactoryTests
+{
+ [Fact]
+ public void Factory_Creation_ShouldInitializeDefaults()
+ {
+ var factory = new Factory();
+
+ Assert.Equal(BusinessUnitType.Factory, factory.Type);
+ Assert.Equal(100, factory.MaxEmployees);
+ Assert.Equal(200000m, factory.BuildCost);
+ Assert.Equal(5000m, factory.UpkeepCost);
+ Assert.Null(factory.OutputProductId);
+ Assert.NotNull(factory.RequiredInputs);
+ }
+
+ [Fact]
+ public void Factory_IsIdle_NoProduction_ShouldBeTrue()
+ {
+ var factory = new Factory();
+
+ Assert.True(factory.IsIdle);
+ }
+
+ [Fact]
+ public void Factory_IsIdle_ActiveProduction_ShouldBeFalse()
+ {
+ var factory = new Factory();
+ var config = new ProductionChainConfig
+ {
+ Id = "test_chain",
+ OutputProductId = "product_1",
+ RequiredBuildingId = "building_1"
+ };
+ factory.ActiveProduction = new ActiveProductionChain
+ {
+ Config = config,
+ Building = new Building(),
+ IsActive = true
+ };
+
+ Assert.False(factory.IsIdle);
+ }
+}
+
+public class WarehouseTests
+{
+ [Fact]
+ public void Warehouse_Creation_ShouldInitializeDefaults()
+ {
+ var warehouse = new Warehouse();
+
+ Assert.Equal(BusinessUnitType.Warehouse, warehouse.Type);
+ Assert.Equal(10, warehouse.MaxEmployees);
+ Assert.Equal(30000m, warehouse.BuildCost);
+ Assert.Equal(500m, warehouse.UpkeepCost);
+ Assert.Equal(10000, warehouse.Capacity);
+ }
+
+ [Fact]
+ public void Warehouse_AddToInventory_ShouldIncreaseStock()
+ {
+ var warehouse = new Warehouse();
+
+ var added = warehouse.AddToInventory("product_1", 100);
+
+ Assert.True(added);
+ Assert.Equal(100, warehouse.Inventory["product_1"]);
+ Assert.Equal(100, warehouse.UsedCapacity);
+ }
+
+ [Fact]
+ public void Warehouse_AddToInventory_NoCapacity_ShouldFail()
+ {
+ var warehouse = new Warehouse { Capacity = 50 };
+
+ var added = warehouse.AddToInventory("product_1", 100);
+
+ Assert.False(added);
+ Assert.Empty(warehouse.Inventory);
+ }
+
+ [Fact]
+ public void Warehouse_RemoveFromInventory_ShouldDecreaseStock()
+ {
+ var warehouse = new Warehouse();
+ warehouse.AddToInventory("product_1", 100);
+
+ var removed = warehouse.RemoveFromInventory("product_1", 30);
+
+ Assert.Equal(30, removed);
+ Assert.Equal(70, warehouse.Inventory["product_1"]);
+ }
+
+ [Fact]
+ public void Warehouse_RemoveFromInventory_NoStock_ShouldReturnZero()
+ {
+ var warehouse = new Warehouse();
+
+ var removed = warehouse.RemoveFromInventory("product_1", 30);
+
+ Assert.Equal(0, removed);
+ }
+
+ [Fact]
+ public void Warehouse_FreeCapacity_Calculation_ShouldBeCorrect()
+ {
+ var warehouse = new Warehouse { Capacity = 1000 };
+ warehouse.AddToInventory("product_1", 300);
+ warehouse.AddToInventory("product_2", 200);
+
+ var freeCapacity = warehouse.FreeCapacity;
+
+ Assert.Equal(500, freeCapacity);
+ }
+}
+
+public class ResearchLabTests
+{
+ [Fact]
+ public void ResearchLab_Creation_ShouldInitializeDefaults()
+ {
+ var lab = new ResearchLab();
+
+ Assert.Equal(BusinessUnitType.ResearchLab, lab.Type);
+ Assert.Equal(30, lab.MaxEmployees);
+ Assert.Equal(150000m, lab.BuildCost);
+ Assert.Equal(4000m, lab.UpkeepCost);
+ Assert.Null(lab.CurrentResearch);
+ Assert.Equal(0, lab.ResearchProgress);
+ }
+
+ [Fact]
+ public void ResearchLab_StartResearch_ShouldSetCurrentResearch()
+ {
+ var lab = new ResearchLab();
+
+ lab.StartResearch("tech_advanced_production");
+
+ Assert.Equal("tech_advanced_production", lab.CurrentResearch);
+ Assert.Equal(0, lab.ResearchProgress);
+ }
+
+ [Fact]
+ public void ResearchLab_SciencePerTick_Calculation_ShouldBeCorrect()
+ {
+ var lab = new ResearchLab { Employees = 10, Efficiency = 100 };
+
+ var science = lab.SciencePerTick;
+
+ Assert.Equal(100, science);
+ }
+
+ [Fact]
+ public void ResearchLab_Tick_ShouldIncreaseProgress()
+ {
+ var lab = new ResearchLab { Employees = 10, Efficiency = 100 };
+ lab.StartResearch("tech_advanced_production");
+
+ lab.Tick();
+
+ Assert.Equal(33, lab.ResearchProgress); // 10 * 10 * 100 / 100 = 100, но ограничено 33 за тик
+ }
+}
+
+public class CompanyBusinessUnitTests
+{
+ [Fact]
+ public void Company_AddBusinessUnit_ShouldAddToList()
+ {
+ var company = new Company { Name = "Test Corp" };
+ var shop = new Shop { Name = "My Shop" };
+
+ company.AddBusinessUnit(shop);
+
+ Assert.Single(company.BusinessUnits);
+ Assert.Equal(company.Id, shop.CompanyId);
+ }
+
+ [Fact]
+ public void Company_RemoveBusinessUnit_ShouldRemoveFromList()
+ {
+ var company = new Company();
+ var shop = new Shop();
+ company.AddBusinessUnit(shop);
+
+ company.RemoveBusinessUnit(shop.Id);
+
+ Assert.Empty(company.BusinessUnits);
+ }
+
+ [Fact]
+ public void Company_Shops_Filter_ShouldReturnOnlyShops()
+ {
+ var company = new Company();
+ company.AddBusinessUnit(new Shop());
+ company.AddBusinessUnit(new Factory());
+ company.AddBusinessUnit(new Shop());
+
+ var shops = company.Shops.ToList();
+
+ Assert.Equal(2, shops.Count);
+ Assert.All(shops, s => Assert.IsType(s));
+ }
+
+ [Fact]
+ public void Company_Factories_Filter_ShouldReturnOnlyFactories()
+ {
+ var company = new Company();
+ company.AddBusinessUnit(new Shop());
+ company.AddBusinessUnit(new Factory());
+ company.AddBusinessUnit(new Factory());
+
+ var factories = company.Factories.ToList();
+
+ Assert.Equal(2, factories.Count);
+ Assert.All(factories, f => Assert.IsType(f));
+ }
+
+ [Fact]
+ public void Company_CalculateTotalProfit_ShouldSumAllUnits()
+ {
+ var company = new Company();
+ var shop1 = new Shop { LastPeriodIncome = 1000m, LastPeriodExpenses = 500m };
+ var shop2 = new Shop { LastPeriodIncome = 2000m, LastPeriodExpenses = 800m };
+ company.AddBusinessUnit(shop1);
+ company.AddBusinessUnit(shop2);
+
+ var totalProfit = company.CalculateTotalProfit();
+
+ Assert.Equal(1700m, totalProfit); // (1000-500) + (2000-800)
+ }
+
+ [Fact]
+ public void Company_Tick_ShouldUpdateAllUnits()
+ {
+ var company = new Company();
+ var shop = new Shop { Employees = 5, MaxEmployees = 10 };
+ company.AddBusinessUnit(shop);
+
+ company.Tick();
+
+ Assert.Equal(50, shop.Efficiency); // 5/10 = 50%
+ }
+}
diff --git a/buffer.md b/buffer.md
new file mode 100644
index 0000000..6dfa2b2
--- /dev/null
+++ b/buffer.md
@@ -0,0 +1,4 @@
+## **sdfsdfsdfsdf**
+
+Ghbdt
+