Add user management and business units

This commit is contained in:
sokol
2026-02-21 11:43:06 +03:00
parent c80afaaeaa
commit be0c844672
6 changed files with 1356 additions and 8 deletions

View File

@@ -0,0 +1,176 @@
namespace MyBiz.Core;
/// <summary>
/// Результат аутентификации
/// </summary>
public class AuthResult
{
/// <summary>
/// Успешна ли аутентификация
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Пользователь (если успешно)
/// </summary>
public User? User { get; set; }
/// <summary>
/// Токен доступа (заглушка)
/// </summary>
public string? Token { get; set; }
/// <summary>
/// Сообщение об ошибке
/// </summary>
public string? ErrorMessage { get; set; }
}
/// <summary>
/// Сервис аутентификации (заглушка для MVP)
/// </summary>
public class AuthService
{
private readonly Dictionary<string, User> _usersByUsername = new();
private readonly Dictionary<Guid, User> _usersById = new();
private readonly Dictionary<string, Guid> _tokens = new(); // token -> userId
/// <summary>
/// Зарегистрировать нового пользователя
/// </summary>
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
};
}
/// <summary>
/// Войти в систему
/// </summary>
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)
};
}
/// <summary>
/// Выйти из системы
/// </summary>
public void Logout(string token)
{
if (_tokens.ContainsKey(token))
{
_tokens.Remove(token);
}
}
/// <summary>
/// Проверить токен и получить пользователя
/// </summary>
public User? ValidateToken(string token)
{
if (_tokens.TryGetValue(token, out var userId))
{
return _usersById.TryGetValue(userId, out var user) ? user : null;
}
return null;
}
/// <summary>
/// Получить пользователя по ID
/// </summary>
public User? GetUserById(Guid userId)
{
return _usersById.TryGetValue(userId, out var user) ? user : null;
}
/// <summary>
/// Получить пользователя по имени
/// </summary>
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
}

View File

@@ -0,0 +1,437 @@
namespace MyBiz.Core;
/// <summary>
/// Тип бизнес-единицы
/// </summary>
public enum BusinessUnitType
{
/// <summary>
/// Магазин (розничная торговля)
/// </summary>
Shop,
/// <summary>
/// Фабрика (производство)
/// </summary>
Factory,
/// <summary>
/// Склад (хранение)
/// </summary>
Warehouse,
/// <summary>
/// Офис (управление)
/// </summary>
Office,
/// <summary>
/// Научный центр (исследования)
/// </summary>
ResearchLab
}
/// <summary>
/// Базовая бизнес-единица (магазин, фабрика и т.д.)
/// </summary>
public class BusinessUnit
{
public Guid Id { get; set; } = Guid.NewGuid();
/// <summary>
/// Название единицы
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Тип бизнес-единицы
/// </summary>
public BusinessUnitType Type { get; set; }
/// <summary>
/// ID компании-владельца
/// </summary>
public Guid CompanyId { get; set; }
/// <summary>
/// Город, где находится единица
/// </summary>
public string CityId { get; set; } = string.Empty;
/// <summary>
/// Координаты на карте
/// </summary>
public int X { get; set; }
public int Y { get; set; }
/// <summary>
/// Уровень развития (1-max)
/// </summary>
public int Level { get; set; } = 1;
/// <summary>
/// Максимальный уровень
/// </summary>
public int MaxLevel { get; set; } = 10;
/// <summary>
/// Количество сотрудников
/// </summary>
public int Employees { get; set; }
/// <summary>
/// Максимальное количество сотрудников
/// </summary>
public int MaxEmployees { get; set; } = 50;
/// <summary>
/// Эффективность работы (0-100)
/// </summary>
public int Efficiency { get; set; } = 100;
/// <summary>
/// Стоимость постройки
/// </summary>
public decimal BuildCost { get; set; }
/// <summary>
/// Содержание в тик (зарплаты, аренда и т.д.)
/// </summary>
public decimal UpkeepCost { get; set; }
/// <summary>
/// Доход за последний период
/// </summary>
public decimal LastPeriodIncome { get; set; }
/// <summary>
/// Расходы за последний период
/// </summary>
public decimal LastPeriodExpenses { get; set; }
/// <summary>
/// Дата постройки
/// </summary>
public int BuiltAtTick { get; set; }
/// <summary>
/// Активна ли единица (работает)
/// </summary>
public bool IsActive { get; set; } = true;
/// <summary>
/// Ссылка на здание (если есть)
/// </summary>
public Building? Building { get; set; }
/// <summary>
/// Запасы на складе (для магазинов/складов)
/// </summary>
public Dictionary<string, int> Inventory { get; set; } = new();
/// <summary>
/// Производственная цепочка (для фабрик)
/// </summary>
public ActiveProductionChain? ActiveProduction { get; set; }
/// <summary>
/// Рассчитать прибыль за период
/// </summary>
public decimal PeriodProfit => LastPeriodIncome - LastPeriodExpenses;
/// <summary>
/// Рассчитать рентабельность (%)
/// </summary>
public decimal Profitability => LastPeriodExpenses > 0
? (PeriodProfit / LastPeriodExpenses) * 100
: 0;
/// <summary>
/// Обновить эффективность на основе сотрудников
/// </summary>
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);
}
/// <summary>
/// Обновить состояние (вызывается каждый тик)
/// </summary>
public virtual void Tick()
{
// Обновление производства (если есть)
ActiveProduction?.Tick();
// Обновление эффективности
UpdateEfficiency();
}
/// <summary>
/// Нанять сотрудника
/// </summary>
public bool HireEmployee()
{
if (Employees < MaxEmployees)
{
Employees++;
return true;
}
return false;
}
/// <summary>
/// Уволить сотрудника
/// </summary>
public bool FireEmployee()
{
if (Employees > 0)
{
Employees--;
return true;
}
return false;
}
/// <summary>
/// Повысить уровень
/// </summary>
public bool Upgrade()
{
if (Level < MaxLevel)
{
Level++;
MaxEmployees += 10; // Бонус за уровень
return true;
}
return false;
}
}
/// <summary>
/// Магазин (розничная торговля)
/// </summary>
public class Shop : BusinessUnit
{
public Shop()
{
Type = BusinessUnitType.Shop;
MaxEmployees = 20;
BuildCost = 50000m;
UpkeepCost = 1000m;
}
/// <summary>
/// Торгуемые продукты
/// </summary>
public List<string> SoldProductTypes { get; set; } = new();
/// <summary>
/// Выручка за сегодня
/// </summary>
public decimal DailyRevenue { get; set; }
/// <summary>
/// Количество клиентов за сегодня
/// </summary>
public int DailyCustomers { get; set; }
/// <summary>
/// Средний чек
/// </summary>
public decimal AverageCheck => DailyCustomers > 0 ? DailyRevenue / DailyCustomers : 0;
}
/// <summary>
/// Фабрика (производство)
/// </summary>
public class Factory : BusinessUnit
{
public Factory()
{
Type = BusinessUnitType.Factory;
MaxEmployees = 100;
BuildCost = 200000m;
UpkeepCost = 5000m;
}
/// <summary>
/// Производимый продукт
/// </summary>
public string? OutputProductId { get; set; }
/// <summary>
/// Требуемые ресурсы (входные продукты)
/// </summary>
public Dictionary<string, int> RequiredInputs { get; set; } = new();
/// <summary>
/// Произведено за последний тик
/// </summary>
public int LastTickOutput { get; set; }
/// <summary>
/// Простой (нет ресурсов)
/// </summary>
public bool IsIdle => ActiveProduction == null || !ActiveProduction.IsActive;
}
/// <summary>
/// Склад (хранение)
/// </summary>
public class Warehouse : BusinessUnit
{
public Warehouse()
{
Type = BusinessUnitType.Warehouse;
MaxEmployees = 10;
BuildCost = 30000m;
UpkeepCost = 500m;
}
/// <summary>
/// Вместимость склада (единиц товара)
/// </summary>
public int Capacity { get; set; } = 10000;
/// <summary>
/// Использовано места
/// </summary>
public int UsedCapacity => Inventory.Values.Sum();
/// <summary>
/// Свободно места
/// </summary>
public int FreeCapacity => Capacity - UsedCapacity;
/// <summary>
/// Добавить товар на склад
/// </summary>
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;
}
/// <summary>
/// Взять товар со склада
/// </summary>
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;
}
}
/// <summary>
/// Офис (управление компанией)
/// </summary>
public class Office : BusinessUnit
{
public Office()
{
Type = BusinessUnitType.Office;
MaxEmployees = 50;
BuildCost = 100000m;
UpkeepCost = 3000m;
}
/// <summary>
/// Бонус к управлению (влияет на эффективность других зданий)
/// </summary>
public int ManagementBonus { get; set; } = 5;
/// <summary>
/// Количество управляемых зданий
/// </summary>
public int ManagedBuildings { get; set; }
}
/// <summary>
/// Научный центр (исследования)
/// </summary>
public class ResearchLab : BusinessUnit
{
public ResearchLab()
{
Type = BusinessUnitType.ResearchLab;
MaxEmployees = 30;
BuildCost = 150000m;
UpkeepCost = 4000m;
}
/// <summary>
/// Текущее исследование
/// </summary>
public string? CurrentResearch { get; set; }
/// <summary>
/// Прогресс исследования (0-100%)
/// </summary>
public int ResearchProgress { get; set; }
/// <summary>
/// Наука за тик
/// </summary>
public int SciencePerTick => Employees * 10 * Efficiency / 100;
/// <summary>
/// Начать исследование
/// </summary>
public void StartResearch(string technologyId)
{
CurrentResearch = technologyId;
ResearchProgress = 0;
}
/// <summary>
/// Обновление исследования
/// </summary>
public override void Tick()
{
base.Tick();
if (CurrentResearch != null && ResearchProgress < 100)
{
ResearchProgress = Math.Min(100, ResearchProgress + SciencePerTick);
}
}
}

View File

@@ -7,44 +7,124 @@ public class Company
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; } = string.Empty;
/// <summary>
/// ID владельца (пользователя)
/// </summary>
public Guid OwnerId { get; set; }
/// <summary>
/// Доступные деньги
/// </summary>
public decimal Cash { get; set; }
/// <summary>
/// Активы компании
/// </summary>
public decimal Assets { get; set; }
/// <summary>
/// Пассивы (долги)
/// </summary>
public decimal Liabilities { get; set; }
/// <summary>
/// Прибыль за последний период
/// </summary>
public decimal LastPeriodProfit { get; set; }
/// <summary>
/// Список зданий компании
/// </summary>
public List<Building> Buildings { get; set; } = new();
/// <summary>
/// Бизнес-единицы компании (магазины, фабрики и т.д.)
/// </summary>
public List<BusinessUnit> BusinessUnits { get; set; } = new();
/// <summary>
/// Складские запасы
/// </summary>
public Dictionary<ProductType, int> Inventory { get; set; } = new();
/// <summary>
/// Открытые технологии
/// </summary>
public HashSet<string> UnlockedTechnologies { get; set; } = new();
/// <summary>
/// Дата основания
/// </summary>
public DateTime FoundedAt { get; set; } = DateTime.UtcNow;
/// <summary>
/// Рассчитать чистую стоимость
/// </summary>
public decimal NetWorth => Assets - Liabilities + Cash;
/// <summary>
/// Получить все магазины
/// </summary>
public IEnumerable<Shop> Shops => BusinessUnits.OfType<Shop>();
/// <summary>
/// Получить все фабрики
/// </summary>
public IEnumerable<Factory> Factories => BusinessUnits.OfType<Factory>();
/// <summary>
/// Получить все склады
/// </summary>
public IEnumerable<Warehouse> Warehouses => BusinessUnits.OfType<Warehouse>();
/// <summary>
/// Добавить бизнес-единицу
/// </summary>
public void AddBusinessUnit(BusinessUnit unit)
{
unit.CompanyId = Id;
BusinessUnits.Add(unit);
}
/// <summary>
/// Удалить бизнес-единицу
/// </summary>
public void RemoveBusinessUnit(Guid unitId)
{
var unit = BusinessUnits.FirstOrDefault(u => u.Id == unitId);
if (unit != null)
{
BusinessUnits.Remove(unit);
}
}
/// <summary>
/// Обновить состояние всех единиц (вызывается каждый тик)
/// </summary>
public void Tick()
{
foreach (var unit in BusinessUnits)
{
unit.Tick();
}
}
/// <summary>
/// Рассчитать общую прибыль за период
/// </summary>
public decimal CalculateTotalProfit()
{
return BusinessUnits.Sum(u => u.PeriodProfit);
}
/// <summary>
/// Рассчитать общую стоимость активов
/// </summary>
public decimal CalculateAssets()
{
return Buildings.Sum(b => b.TypeConfig?.BuildCost ?? 0) +
BusinessUnits.Sum(u => u.BuildCost) +
Inventory.Sum(i => i.Key.BasePrice * i.Value);
}
}

View File

@@ -0,0 +1,90 @@
namespace MyBiz.Core;
/// <summary>
/// Пользователь системы
/// </summary>
public class User
{
public Guid Id { get; set; } = Guid.NewGuid();
/// <summary>
/// Имя пользователя (логин)
/// </summary>
public string Username { get; set; } = string.Empty;
/// <summary>
/// Email
/// </summary>
public string Email { get; set; } = string.Empty;
/// <summary>
/// Хеш пароля (не хранит сам пароль)
/// </summary>
public string PasswordHash { get; set; } = string.Empty;
/// <summary>
/// Дата регистрации
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
/// <summary>
/// Дата последнего входа
/// </summary>
public DateTime? LastLoginAt { get; set; }
/// <summary>
/// Активен ли пользователь
/// </summary>
public bool IsActive { get; set; } = true;
/// <summary>
/// Компания, принадлежащая пользователю
/// </summary>
public Company? Company { get; set; }
/// <summary>
/// ID компании (для быстрого доступа)
/// </summary>
public Guid? CompanyId { get; set; }
/// <summary>
/// Статистика игры
/// </summary>
public UserStats Stats { get; set; } = new();
}
/// <summary>
/// Статистика пользователя
/// </summary>
public class UserStats
{
/// <summary>
/// Всего игр сыграно
/// </summary>
public int GamesPlayed { get; set; }
/// <summary>
/// Всего часов в игре
/// </summary>
public int TotalHoursPlayed { get; set; }
/// <summary>
/// Максимальная чистая стоимость за всё время
/// </summary>
public decimal MaxNetWorth { get; set; }
/// <summary>
/// Всего зданий построено
/// </summary>
public int TotalBuildingsBuilt { get; set; }
/// <summary>
/// Дата начала первой игры
/// </summary>
public DateTime? FirstGameDate { get; set; }
/// <summary>
/// Дата последней игры
/// </summary>
public DateTime? LastGameDate { get; set; }
}

View File

@@ -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<Shop>(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<Factory>(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%
}
}

4
buffer.md Normal file
View File

@@ -0,0 +1,4 @@
## **sdfsdfsdfsdf**
Ghbdt