#!/usr/bin/env python3 """ test_price_calculator.py — Unit tests для price_calculator.py Montana Protocol Тестирование ценового калькулятора и якоря Beeple """ import sys import os import unittest from datetime import datetime, timezone # Добавляем путь к модулю sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../экономика/банк_времени/код')) from price_calculator import ( BeepleAnchor, mta_to_usd, usd_to_mta, seconds_to_usd, usd_to_seconds, days_to_usd, usd_to_days, get_anchor_info, format_price, PizzaDayCalculator ) class TestBeepleAnchorConstants(unittest.TestCase): """Тесты констант якоря Beeple""" # ═══════════════════════════════════════════════════════════════════════ # BEEPLE ANCHOR CONSTANTS # ═══════════════════════════════════════════════════════════════════════ def test_sale_price(self): """Цена продажи Beeple NFT""" self.assertEqual(BeepleAnchor.SALE_PRICE_USD, 69_346_250.00) def test_total_days(self): """Количество дней работы""" self.assertEqual(BeepleAnchor.TOTAL_DAYS, 5000) def test_sale_date(self): """Дата продажи""" self.assertEqual(BeepleAnchor.SALE_DATE, "2021-03-11") def test_usd_per_day(self): """USD за день работы""" expected = BeepleAnchor.SALE_PRICE_USD / BeepleAnchor.TOTAL_DAYS self.assertAlmostEqual(BeepleAnchor.USD_PER_DAY, expected, places=2) self.assertAlmostEqual(BeepleAnchor.USD_PER_DAY, 13869.25, places=2) def test_usd_per_hour(self): """USD за час работы""" expected = BeepleAnchor.USD_PER_DAY / 24 self.assertAlmostEqual(BeepleAnchor.USD_PER_HOUR, expected, places=2) self.assertAlmostEqual(BeepleAnchor.USD_PER_HOUR, 577.885, places=2) def test_usd_per_minute(self): """USD за минуту работы""" expected = BeepleAnchor.USD_PER_HOUR / 60 self.assertAlmostEqual(BeepleAnchor.USD_PER_MINUTE, expected, places=2) self.assertAlmostEqual(BeepleAnchor.USD_PER_MINUTE, 9.6314, places=2) def test_usd_per_second(self): """USD за секунду работы""" expected = BeepleAnchor.USD_PER_MINUTE / 60 self.assertAlmostEqual(BeepleAnchor.USD_PER_SECOND, expected, places=4) self.assertAlmostEqual(BeepleAnchor.USD_PER_SECOND, 0.1605, places=4) def test_mta_per_second(self): """MTA за секунду""" self.assertEqual(BeepleAnchor.MTA_PER_SECOND, 1.0) def test_usd_per_mta(self): """USD за 1 MTA""" self.assertEqual(BeepleAnchor.USD_PER_MTA, BeepleAnchor.USD_PER_SECOND) class TestMTAtoUSD(unittest.TestCase): """Тесты конвертации MTA → USD""" # ═══════════════════════════════════════════════════════════════════════ # MTA TO USD # ═══════════════════════════════════════════════════════════════════════ def test_mta_to_usd_one_second(self): """1 MTA (1 секунда) → USD""" result = mta_to_usd(1.0) self.assertAlmostEqual(result, 0.1605, places=4) def test_mta_to_usd_ten_minutes(self): """600 MTA (10 минут) → USD""" result = mta_to_usd(600) expected = 600 * BeepleAnchor.USD_PER_SECOND self.assertAlmostEqual(result, expected, places=2) self.assertAlmostEqual(result, 96.3, places=1) def test_mta_to_usd_one_hour(self): """3600 MTA (1 час) → USD""" result = mta_to_usd(3600) expected = 3600 * BeepleAnchor.USD_PER_SECOND self.assertAlmostEqual(result, expected, places=2) self.assertAlmostEqual(result, 577.8, places=1) def test_mta_to_usd_one_day(self): """86400 MTA (1 день) → USD""" result = mta_to_usd(86400) expected = 86400 * BeepleAnchor.USD_PER_SECOND self.assertAlmostEqual(result, expected, places=2) self.assertAlmostEqual(result, 13869.2, places=1) def test_mta_to_usd_zero(self): """0 MTA → $0""" result = mta_to_usd(0) self.assertEqual(result, 0.0) def test_mta_to_usd_large_number(self): """1,000,000 MTA → USD""" result = mta_to_usd(1_000_000) expected = 1_000_000 * BeepleAnchor.USD_PER_SECOND self.assertAlmostEqual(result, expected, places=2) class TestUSDtoMTA(unittest.TestCase): """Тесты конвертации USD → MTA""" # ═══════════════════════════════════════════════════════════════════════ # USD TO MTA # ═══════════════════════════════════════════════════════════════════════ def test_usd_to_mta_one_second_price(self): """$0.1605 → 1 MTA""" result = usd_to_mta(0.1605) self.assertAlmostEqual(result, 1.0, places=2) def test_usd_to_mta_one_hundred(self): """$100 → MTA""" result = usd_to_mta(100) expected = 100 / BeepleAnchor.USD_PER_SECOND self.assertAlmostEqual(result, expected, places=2) self.assertAlmostEqual(result, 623.0, places=0) def test_usd_to_mta_zero(self): """$0 → 0 MTA""" result = usd_to_mta(0) self.assertEqual(result, 0.0) def test_usd_to_mta_roundtrip(self): """Roundtrip: MTA → USD → MTA""" original = 1000.0 converted = mta_to_usd(original) back = usd_to_mta(converted) self.assertAlmostEqual(back, original, places=2) class TestSecondsConversion(unittest.TestCase): """Тесты конвертации секунд""" # ═══════════════════════════════════════════════════════════════════════ # SECONDS CONVERSION # ═══════════════════════════════════════════════════════════════════════ def test_seconds_to_usd_one(self): """1 секунда → USD""" result = seconds_to_usd(1) self.assertAlmostEqual(result, 0.1605, places=4) def test_seconds_to_usd_sixty(self): """60 секунд (1 минута) → USD""" result = seconds_to_usd(60) expected = 60 * BeepleAnchor.USD_PER_SECOND self.assertAlmostEqual(result, expected, places=2) def test_seconds_to_usd_ten_minutes(self): """600 секунд (10 минут) → USD""" result = seconds_to_usd(600) self.assertAlmostEqual(result, 96.3, places=1) def test_usd_to_seconds_one_second_price(self): """$0.1605 → 1 секунда""" result = usd_to_seconds(0.1605) self.assertAlmostEqual(result, 1.0, places=2) def test_usd_to_seconds_one_hundred(self): """$100 → секунды""" result = usd_to_seconds(100) expected = 100 / BeepleAnchor.USD_PER_SECOND self.assertAlmostEqual(result, expected, places=2) def test_seconds_roundtrip(self): """Roundtrip: секунды → USD → секунды""" original = 3600 # 1 час converted = seconds_to_usd(original) back = usd_to_seconds(converted) self.assertAlmostEqual(back, original, places=2) class TestDaysConversion(unittest.TestCase): """Тесты конвертации дней""" # ═══════════════════════════════════════════════════════════════════════ # DAYS CONVERSION # ═══════════════════════════════════════════════════════════════════════ def test_days_to_usd_one(self): """1 день → USD""" result = days_to_usd(1) self.assertAlmostEqual(result, BeepleAnchor.USD_PER_DAY, places=2) self.assertAlmostEqual(result, 13869.25, places=2) def test_days_to_usd_5000(self): """5000 дней → USD (полная работа Beeple)""" result = days_to_usd(5000) self.assertAlmostEqual(result, BeepleAnchor.SALE_PRICE_USD, places=2) def test_days_to_usd_fractional(self): """0.5 дня → USD""" result = days_to_usd(0.5) expected = BeepleAnchor.USD_PER_DAY * 0.5 self.assertAlmostEqual(result, expected, places=2) def test_usd_to_days_one_day_price(self): """$13,869.25 → 1 день""" result = usd_to_days(13869.25) self.assertAlmostEqual(result, 1.0, places=2) def test_usd_to_days_full_sale(self): """$69.3M → 5000 дней""" result = usd_to_days(BeepleAnchor.SALE_PRICE_USD) self.assertAlmostEqual(result, 5000, places=2) def test_days_roundtrip(self): """Roundtrip: дни → USD → дни""" original = 365 # 1 год converted = days_to_usd(original) back = usd_to_days(converted) self.assertAlmostEqual(back, original, places=2) class TestAnchorInfo(unittest.TestCase): """Тесты получения информации о якоре""" # ═══════════════════════════════════════════════════════════════════════ # ANCHOR INFO # ═══════════════════════════════════════════════════════════════════════ def test_get_anchor_info_structure(self): """Структура информации о якоре""" info = get_anchor_info() self.assertIsInstance(info, dict) self.assertIn("sale_price_usd", info) self.assertIn("total_days", info) self.assertIn("sale_date", info) self.assertIn("usd_per_second", info) self.assertIn("usd_per_minute", info) self.assertIn("usd_per_hour", info) self.assertIn("usd_per_day", info) self.assertIn("mta_per_second", info) self.assertIn("usd_per_mta", info) def test_get_anchor_info_values(self): """Значения в информации о якоре""" info = get_anchor_info() self.assertEqual(info["sale_price_usd"], 69_346_250.00) self.assertEqual(info["total_days"], 5000) self.assertEqual(info["sale_date"], "2021-03-11") self.assertAlmostEqual(info["usd_per_second"], 0.1605, places=4) self.assertEqual(info["mta_per_second"], 1.0) class TestFormatPrice(unittest.TestCase): """Тесты форматирования цены""" # ═══════════════════════════════════════════════════════════════════════ # FORMAT PRICE # ═══════════════════════════════════════════════════════════════════════ def test_format_price_usd(self): """Форматирование в USD""" result = format_price(96.31, "USD") self.assertEqual(result, "$96.31 USD") def test_format_price_mta(self): """Форматирование в MTA""" usd_amount = 0.1605 # 1 MTA result = format_price(usd_amount, "MTA") self.assertIn("MTA", result) self.assertIn("1.00", result) def test_format_price_large_number(self): """Форматирование большого числа""" result = format_price(13869.25, "USD") self.assertEqual(result, "$13869.25 USD") def test_format_price_zero(self): """Форматирование нуля""" result = format_price(0.0, "USD") self.assertEqual(result, "$0.00 USD") class TestPizzaDayCalculator(unittest.TestCase): """Тесты Pizza Day механизма""" # ═══════════════════════════════════════════════════════════════════════ # PIZZA DAY MECHANISM # ═══════════════════════════════════════════════════════════════════════ def test_is_pizza_day_true(self): """22 мая — это Pizza Day""" pizza_day = datetime(2025, 5, 22, tzinfo=timezone.utc) result = PizzaDayCalculator.is_pizza_day(pizza_day) self.assertTrue(result) def test_is_pizza_day_false(self): """23 мая — НЕ Pizza Day""" not_pizza_day = datetime(2025, 5, 23, tzinfo=timezone.utc) result = PizzaDayCalculator.is_pizza_day(not_pizza_day) self.assertFalse(result) def test_is_pizza_day_different_year(self): """Pizza Day в другом году""" pizza_day_2030 = datetime(2030, 5, 22, tzinfo=timezone.utc) result = PizzaDayCalculator.is_pizza_day(pizza_day_2030) self.assertTrue(result) def test_days_until_pizza_day_before(self): """Дней до Pizza Day (до 22 мая)""" date = datetime(2025, 5, 1, tzinfo=timezone.utc) result = PizzaDayCalculator.days_until_pizza_day(date) self.assertEqual(result, 21) # 22 - 1 = 21 день def test_days_until_pizza_day_after(self): """Дней до Pizza Day (после 22 мая)""" date = datetime(2025, 6, 1, tzinfo=timezone.utc) result = PizzaDayCalculator.days_until_pizza_day(date) # Должно быть ~355 дней до следующего 22 мая self.assertGreater(result, 300) self.assertLess(result, 366) def test_days_until_pizza_day_on_pizza_day(self): """Дней до Pizza Day в сам Pizza Day""" pizza_day = datetime(2025, 5, 22, tzinfo=timezone.utc) result = PizzaDayCalculator.days_until_pizza_day(pizza_day) # В сам Pizza Day — 0 дней до него (или 365 до следующего) # Зависит от времени суток self.assertIn(result, [0, 365]) def test_recalculate_anchor(self): """Пересчёт якоря с новыми значениями""" new_price = 100_000_000 # $100M new_days = 10_000 result = PizzaDayCalculator.recalculate_anchor(new_price, new_days) self.assertEqual(result["sale_price_usd"], new_price) self.assertEqual(result["total_days"], new_days) self.assertAlmostEqual(result["usd_per_day"], 10_000, places=2) self.assertAlmostEqual(result["usd_per_second"], 0.1157, places=4) def test_recalculate_anchor_doubles_price(self): """Пересчёт с удвоенной ценой""" original_price = BeepleAnchor.SALE_PRICE_USD doubled_price = original_price * 2 result = PizzaDayCalculator.recalculate_anchor(doubled_price, BeepleAnchor.TOTAL_DAYS) # USD/секунда должен удвоиться expected_usd_per_sec = BeepleAnchor.USD_PER_SECOND * 2 self.assertAlmostEqual(result["usd_per_second"], expected_usd_per_sec, places=4) class TestConsistency(unittest.TestCase): """Тесты консистентности расчётов""" # ═══════════════════════════════════════════════════════════════════════ # CONSISTENCY # ═══════════════════════════════════════════════════════════════════════ def test_seconds_equals_mta(self): """1 секунда = 1 MTA""" seconds_value = seconds_to_usd(1) mta_value = mta_to_usd(1) self.assertAlmostEqual(seconds_value, mta_value, places=4) def test_day_to_seconds_consistency(self): """86400 секунд = 1 день""" usd_from_seconds = seconds_to_usd(86400) usd_from_days = days_to_usd(1) self.assertAlmostEqual(usd_from_seconds, usd_from_days, places=2) def test_beeple_calculation_consistency(self): """$69.3M / 5000 дней / 86400 сек = $0.1605/сек""" calculated = BeepleAnchor.SALE_PRICE_USD / BeepleAnchor.TOTAL_DAYS / 86400 self.assertAlmostEqual(calculated, BeepleAnchor.USD_PER_SECOND, places=4) def test_all_units_consistent(self): """Все единицы согласованы""" # 1 день = 24 часа = 1440 минут = 86400 секунд day_price = BeepleAnchor.USD_PER_DAY hour_price = BeepleAnchor.USD_PER_HOUR * 24 minute_price = BeepleAnchor.USD_PER_MINUTE * 1440 second_price = BeepleAnchor.USD_PER_SECOND * 86400 self.assertAlmostEqual(day_price, hour_price, places=2) self.assertAlmostEqual(day_price, minute_price, places=2) self.assertAlmostEqual(day_price, second_price, places=2) # ═══════════════════════════════════════════════════════════════════════════ # RUN TESTS # ═══════════════════════════════════════════════════════════════════════════ if __name__ == '__main__': # Запускаем с verbose output unittest.main(verbosity=2)