Ver Fonte

fix(investment): resolve final catalyst review findings

Keep the regression project local-only. The SDD supersedes the earlier pinned plug-identity requirement; plug roles remain derived from each exact socket pool.
y9522 há 2 semanas atrás
pai
commit
7fa4001853

+ 1 - 0
Sunrise/src/client/content/items/packages/package_socket_plug_build.cpp

@@ -25,6 +25,7 @@ constexpr std::uint16_t kTrackerSocketType = 518;
 /** FNV-1a constants make pool fingerprints stable and cheap. */
 constexpr std::uint64_t kHashOffsetBasis = 14695981039346656037ULL;
 constexpr std::uint64_t kHashPrime = 1099511628211ULL;
+
 /** Visitor adapter that appends one list member to a bounded lane candidate. */
 struct VisitorContext {
     SocketPlugBuild* build{};

+ 1 - 0
Sunrise/src/middleware/web_service/messages/opcode406_codec.cpp

@@ -17,6 +17,7 @@ constexpr std::uint8_t kValueWidth = 32;
 constexpr std::uint8_t kPaddingWidth = 7;
 /** Nonnegative signed 32-bit values have this bit set after native descriptor biasing. */
 constexpr std::uint64_t kValueBias = 0x80000000ULL;
+
 } // namespace
 
 /** Parses the exact native item-state descriptor. */

+ 2 - 4
Sunrise/src/state/build_data/cache/records/codec.h

@@ -139,8 +139,7 @@ namespace sunrise::state::build_data::cache::records {
 
 /**
  * @param value Runtime catalyst relation to pack.
- * @param record Receives the canonical disk
- * form.
+ * @param record Receives the canonical disk form.
  * @return True when the runtime relation has a supported availability value.
  */
 [[nodiscard]] bool encode(const items::catalysts::Definition& value,
@@ -148,8 +147,7 @@ namespace sunrise::state::build_data::cache::records {
 
 /**
  * @param record Canonical disk form to unpack.
- * @param value Receives the runtime catalyst
- * relation.
+ * @param value Receives the runtime catalyst relation.
  * @return True when the disk form has a supported availability value.
  */
 [[nodiscard]] bool decode(const ExoticCatalystRecord& record,

+ 9 - 0
Sunrise/src/state/build_data/items/catalysts/definition.h

@@ -34,6 +34,7 @@ enum class Error : std::uint8_t {
     missingReleased,
     ambiguousLifecycle,
     invalidSocket,
+    capacityExceeded,
 };
 
 /**
@@ -56,6 +57,8 @@ enum class Error : std::uint8_t {
         return "ambiguous_lifecycle";
     case Error::invalidSocket:
         return "invalid_socket";
+    case Error::capacityExceeded:
+        return "capacity_exceeded";
     }
     return "unknown";
 }
@@ -70,6 +73,12 @@ struct Definition {
     std::uint16_t effectDefinitionIndex{};
     std::uint8_t socketLane{};
     Availability availability{Availability::unsupported};
+
+    /**
+     * @param other Catalyst definition to compare.
+     * @return True when every derived field is equal.
+     */
+    [[nodiscard]] constexpr bool operator==(const Definition& other) const noexcept = default;
 };
 
 /**

+ 41 - 36
Sunrise/src/state/build_data/items/catalysts/exotic_catalyst_builder.cpp

@@ -272,6 +272,30 @@ classify_lane(const Source& source, const details::Definition& detail, std::uint
     return false;
 }
 
+/**
+ * Appends one complete row or fails the whole staged catalog.
+ * @param output Staged catalog rows.
+ * @param count Used row count.
+ * @param report Build report to update on failure.
+ * @param definition Complete row to append.
+ * @return True when the row fits.
+ */
+[[nodiscard]] bool append_definition(std::span<Definition> output,
+                                     std::size_t& count,
+                                     Report& report,
+                                     const Definition& definition) noexcept {
+    if (count >= output.size() || count >= kDefinitionCapacity) {
+        return fail(output,
+                    count,
+                    report,
+                    Error::capacityExceeded,
+                    definition.itemDefinitionHash,
+                    definition.socketLane);
+    }
+    output[count++] = definition;
+    return true;
+}
+
 /**
  * @param item Source item row.
  * @param detail Source item detail row.
@@ -348,31 +372,18 @@ bool derive(const Source& source,
             if (release.has_value()) {
                 return fail(output, count, report, *unclear, item->definitionHash, detectedLane);
             }
-            if (count >= output.size() || count >= kDefinitionCapacity) {
-                return fail(output,
-                            count,
-                            report,
-                            Error::invalidSocket,
-                            item->definitionHash,
-                            detectedLane);
-            }
-            output[count++] = Definition{item->definitionHash,
+            const Definition unsupported{item->definitionHash,
                                          item->definitionIndex,
                                          details::kUnavailableItemIndex,
                                          details::kUnavailableItemIndex,
                                          detectedLane,
                                          Availability::unsupported};
+            if (!append_definition(output, count, report, unsupported)) {
+                return false;
+            }
             ++report.unsupported;
             continue;
         }
-        if (count >= output.size() || count >= kDefinitionCapacity) {
-            return fail(output,
-                        count,
-                        report,
-                        Error::invalidSocket,
-                        item->definitionHash,
-                        completed->socketLane);
-        }
         if (release.has_value()) {
             if (releasedFound[*release]) {
                 return fail(output,
@@ -382,18 +393,23 @@ bool derive(const Source& source,
                             item->definitionHash,
                             completed->socketLane);
             }
+        }
+        const Definition definition{item->definitionHash,
+                                    item->definitionIndex,
+                                    completed->completedPlugDefinitionIndex,
+                                    completed->effectDefinitionIndex,
+                                    completed->socketLane,
+                                    release.has_value() ? Availability::released
+                                                        : Availability::placeholder};
+        if (!append_definition(output, count, report, definition)) {
+            return false;
+        }
+        if (release.has_value()) {
             releasedFound[*release] = true;
             ++report.released;
         } else {
             ++report.placeholder;
         }
-        output[count++] =
-            Definition{item->definitionHash,
-                       item->definitionIndex,
-                       completed->completedPlugDefinitionIndex,
-                       completed->effectDefinitionIndex,
-                       completed->socketLane,
-                       release.has_value() ? Availability::released : Availability::placeholder};
     }
 
     for (std::size_t index = 0; index < facts.releasedWeaponHashes.size(); ++index) {
@@ -416,18 +432,7 @@ bool matches_derived(const Source& source,
         || expectedCount != definitions.size()) {
         return false;
     }
-    return std::equal(expected.begin(),
-                      expected.begin() + expectedCount,
-                      definitions.begin(),
-                      [](const Definition& left, const Definition& right) {
-                          return left.itemDefinitionHash == right.itemDefinitionHash
-                                 && left.itemDefinitionIndex == right.itemDefinitionIndex
-                                 && left.completedPlugDefinitionIndex
-                                        == right.completedPlugDefinitionIndex
-                                 && left.effectDefinitionIndex == right.effectDefinitionIndex
-                                 && left.socketLane == right.socketLane
-                                 && left.availability == right.availability;
-                      });
+    return std::equal(expected.begin(), expected.begin() + expectedCount, definitions.begin());
 }
 
 } // namespace sunrise::state::build_data::items::catalysts

+ 8 - 8
Sunrise/src/state/build_data/runtime/persistence/build_data_persistence.cpp

@@ -98,15 +98,8 @@ to_record(const constants::InvestmentConstants& value) noexcept {
            && cache::records::canonicalize(scratch, counts);
 }
 
-} // namespace
-
-/** @return The process-wide persistence context, shared by lifecycle and writer code. */
-Context& context() noexcept {
-    return g_context;
-}
-
 /** @return True when every required extracted domain is complete in State. */
-[[nodiscard]] static bool required_domains_ready() noexcept {
+[[nodiscard]] bool required_domains_ready() noexcept {
     constants::InvestmentConstants published{};
     return runtime::named::ready() && item_definitions_ready() && configured_item_details_ready()
            && collectible_definitions_ready() && socket_plug_rules_ready()
@@ -116,6 +109,13 @@ Context& context() noexcept {
            && hash_names_ready() && constants::find(published);
 }
 
+} // namespace
+
+/** @return The process-wide persistence context, shared by lifecycle and writer code. */
+Context& context() noexcept {
+    return g_context;
+}
+
 /** Gives mutable views over every fixed snapshot buffer. */
 cache::records::MutableDomains scratch_domains(Context& state) noexcept {
     const auto named = ensure_scratch<content::Definition, content::kDefinitionCatalogCapacity>(

+ 0 - 118
tests/Sunrise.Tests.vcxproj

@@ -1,118 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|x64">
-      <Configuration>Debug</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|x64">
-      <Configuration>Release</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <VCProjectVersion>18.0</VCProjectVersion>
-    <ProjectGuid>{9D2A5866-9D2F-4A07-A88C-6E713A355C94}</ProjectGuid>
-    <RootNamespace>sunrise_tests</RootNamespace>
-    <WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
-    <ProjectName>Sunrise.Tests</ProjectName>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <PlatformToolset>v145</PlatformToolset>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
-    <UseDebugLibraries>true</UseDebugLibraries>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
-    <UseDebugLibraries>false</UseDebugLibraries>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <PropertyGroup>
-    <OutDir>$(ProjectDir)..\build\$(Platform)\$(Configuration)\</OutDir>
-    <IntDir>$(ProjectDir)..\build\obj\$(Platform)\$(Configuration)\tests\</IntDir>
-  </PropertyGroup>
-  <ItemDefinitionGroup>
-    <ClCompile>
-      <LanguageStandard>stdcpp20</LanguageStandard>
-      <WarningLevel>Level4</WarningLevel>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <ConformanceMode>true</ConformanceMode>
-      <MultiProcessorCompilation>false</MultiProcessorCompilation>
-      <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
-      <AdditionalIncludeDirectories>$(ProjectDir)..\Sunrise\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>WIN32;_WINDOWS;WIN32_LEAN_AND_MEAN;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-    </ClCompile>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
-    <ClCompile>
-      <Optimization>Disabled</Optimization>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
-    </ClCompile>
-    <Link>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
-    <ClCompile>
-      <Optimization>MaxSpeed</Optimization>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
-    </ClCompile>
-    <Link>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemGroup>
-    <ClCompile Include="settings_regression_tests.cpp" />
-    <ClCompile Include="catalyst_regression_tests.cpp" />
-    <ClCompile Include="loadout_catalyst_regression_tests.cpp" />
-    <ClCompile Include="opcode406_item_state_regression_tests.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\json_number.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\json_scalar.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\json_structure.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state_settings.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\address_text.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\client\client_settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\client\client_ui_settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\client\external\client_external_settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\server\activation\activation_settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\server\entitlement_settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\server\gameplay\gameplay_settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\server\gameplay\gameplay_settings_validation.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\server\server_settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\account_rows_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\account_settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\activity_arrival_override_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\activity_default_destination_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\audio_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\controls_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\display_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\family5_override_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\interface_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\inventory_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\key_bindings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\state\social_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\core\settings\steam\steam_settings_parser.cpp" />
-    <ClCompile Include="..\Sunrise\src\state\account\inventory\inventory_state.cpp" />
-    <ClCompile Include="..\Sunrise\src\state\build_data\items\catalysts\exotic_catalyst_builder.cpp" />
-    <ClCompile Include="..\Sunrise\src\state\build_data\items\catalysts\exotic_catalyst_catalog.cpp" />
-    <ClCompile Include="..\Sunrise\src\state\build_data\cache\records\cache_exotic_catalyst_record_codec.cpp" />
-    <ClCompile Include="..\Sunrise\src\middleware\datagen\family4\loadout\loadout_item_resolver.cpp" />
-    <ClCompile Include="..\Sunrise\src\middleware\datagen\family4\loadout\subclass_socket_selection.cpp" />
-    <ClCompile Include="..\Sunrise\src\middleware\datagen\character_record\appearance\character_appearance_banks.cpp" />
-    <ClCompile Include="..\Sunrise\src\middleware\datagen\character_record\appearance\character_appearance_render.cpp" />
-    <ClCompile Include="..\Sunrise\src\middleware\datagen\character_record\appearance\character_appearance_stats.cpp" />
-    <ClCompile Include="..\Sunrise\src\state\build_data\items\item_catalog.cpp" />
-    <ClCompile Include="..\Sunrise\src\state\build_data\items\details\item_detail_catalog.cpp" />
-    <ClCompile Include="..\Sunrise\src\state\build_data\inventory\buckets\inventory_bucket_catalog.cpp" />
-    <ClCompile Include="..\Sunrise\src\state\build_data\socket_entry_lists\socket_entry_list_catalog.cpp" />
-    <ClCompile Include="..\Sunrise\src\middleware\encoding\bit_reader.cpp" />
-    <ClCompile Include="..\Sunrise\src\middleware\web_service\messages\opcode406_codec.cpp" />
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-</Project>

+ 0 - 389
tests/catalyst_regression_tests.cpp

@@ -1,389 +0,0 @@
-#include <algorithm>
-#include <array>
-#include <cstddef>
-#include <cstdint>
-#include <cstdio>
-#include <optional>
-#include <span>
-
-#include "state/account/inventory/item_state.h"
-#include "state/build_data/cache/records/codec.h"
-#include "state/build_data/items/catalysts/exotic_catalyst_builder.h"
-#include "state/build_data/items/catalysts/exotic_catalyst_catalog.h"
-#include "state/build_data/runtime/persistence/build_data_persistence.h"
-
-extern int failures;
-
-namespace {
-
-namespace cache_records = sunrise::state::build_data::cache::records;
-namespace catalysts = sunrise::state::build_data::items::catalysts;
-namespace details = sunrise::state::build_data::items::details;
-namespace items = sunrise::state::build_data::items;
-namespace persistence = sunrise::state::build_data::runtime::persistence;
-namespace socket_plugs = sunrise::state::build_data::items::socket_plugs;
-
-constexpr std::uint32_t kTimestamp = 0x12345678U;
-constexpr std::uint32_t kImageSize = 0x01000000U;
-constexpr std::uint32_t kLegacyWeaponHash = 0x10000010U;
-constexpr std::uint32_t kLaterWeaponHash = 0x10000020U;
-constexpr std::uint32_t kPlaceholderWeaponHash = 0x10000030U;
-constexpr std::uint32_t kNonCatalystWeaponHash = 0x10000040U;
-
-void expect(bool value, const char* label) noexcept {
-    if (!value) {
-        std::fprintf(stderr, "FAIL %s\n", label);
-        ++failures;
-    }
-}
-
-items::Definition item(std::uint16_t index,
-                       std::uint32_t hash,
-                       items::Tier tier = items::Tier::none,
-                       std::uint32_t category = 0) noexcept {
-    items::Definition value{};
-    value.definitionIndex = index;
-    value.definitionHash = hash;
-    value.tier = static_cast<std::uint8_t>(tier);
-    value.plugCategoryHash = category;
-    return value;
-}
-
-details::Definition weapon_detail(std::uint16_t index,
-                                  std::uint32_t hash,
-                                  std::uint8_t lane,
-                                  std::uint16_t defaultPlug) noexcept {
-    details::Definition value{};
-    value.definitionIndex = index;
-    value.definitionHash = hash;
-    value.instancedDefinitionState = details::InstancedDefinitionState::instanced;
-    value.equipmentSlot = std::int8_t{7};
-    value.ordinarySocketState = details::OrdinarySocketState::present;
-    value.ordinarySocketCount = static_cast<std::uint8_t>(lane + 1);
-    value.initialPlugIndices[lane] = defaultPlug;
-    return value;
-}
-
-details::Definition plug_detail(std::uint16_t index,
-                                std::uint32_t hash,
-                                std::int32_t maxStackSize,
-                                bool hasEffect = false) noexcept {
-    details::Definition value{};
-    value.definitionIndex = index;
-    value.definitionHash = hash;
-    value.maxStackSize = maxStackSize;
-    value.instancedDefinitionState = details::InstancedDefinitionState::stackable;
-    if (hasEffect) {
-        value.sandboxPerkCount = 1;
-        value.sandboxPerks[0] = 7;
-    }
-    return value;
-}
-
-struct Fixture {
-    static constexpr std::uint16_t kLegacyWeapon = 10;
-    static constexpr std::uint16_t kLaterWeapon = 11;
-    static constexpr std::uint16_t kPlaceholderWeapon = 12;
-    static constexpr std::uint16_t kNonCatalystWeapon = 13;
-    static constexpr std::uint16_t kLegacyDefault = 20;
-    static constexpr std::uint16_t kLegacyProgress = 21;
-    static constexpr std::uint16_t kLegacyComplete = 22;
-    static constexpr std::uint16_t kLegacyEffect = 23;
-    static constexpr std::uint16_t kLaterDefault = 30;
-    static constexpr std::uint16_t kLaterActive = 31;
-    static constexpr std::uint16_t kPlaceholderDefault = 40;
-    static constexpr std::uint16_t kPlaceholderActive = 41;
-    static constexpr std::uint16_t kOtherDefault = 50;
-    static constexpr std::uint16_t kOtherActive = 51;
-
-    std::array<items::Definition, 13> itemRows{};
-    std::array<details::Definition, 11> detailRows{};
-    std::array<socket_plugs::Rule, 4> rules{{
-        {kLegacyWeapon, 7, 0, 1},
-        {kLaterWeapon, 6, 0, 2},
-        {kPlaceholderWeapon, 5, 0, 3},
-        {kNonCatalystWeapon, 4, 0, 4},
-    }};
-    std::array<socket_plugs::Pool, 5> pools{{
-        {0, 0},
-        {0, 3},
-        {3, 2},
-        {5, 2},
-        {7, 2},
-    }};
-    std::array<socket_plugs::Member, 9> members{{
-        kLegacyDefault,
-        kLegacyProgress,
-        kLegacyComplete,
-        kLaterDefault,
-        kLaterActive,
-        kPlaceholderDefault,
-        kPlaceholderActive,
-        kOtherDefault,
-        kOtherActive,
-    }};
-    std::array<std::uint32_t, 2> released{{kLegacyWeaponHash, kLaterWeaponHash}};
-    std::array<catalysts::Definition, catalysts::kDefinitionCapacity> output{};
-
-    Fixture() noexcept {
-        constexpr std::uint32_t kLegacyCategory = 0x20000010U;
-        constexpr std::uint32_t kLaterCategory = 0x20000020U;
-        constexpr std::uint32_t kPlaceholderCategory = 0x20000030U;
-        constexpr std::uint32_t kOtherCategory = 0x20000040U;
-        itemRows = {
-            item(kLegacyWeapon, kLegacyWeaponHash, items::Tier::exotic),
-            item(kLaterWeapon, kLaterWeaponHash, items::Tier::exotic),
-            item(kPlaceholderWeapon, kPlaceholderWeaponHash, items::Tier::exotic),
-            item(kNonCatalystWeapon, kNonCatalystWeaponHash, items::Tier::exotic),
-            item(kLegacyDefault, 0x30000010U, items::Tier::none, kLegacyCategory),
-            item(kLegacyProgress, 0x30000011U, items::Tier::none, kLegacyCategory),
-            item(kLegacyComplete, 0x30000012U, items::Tier::none, kLegacyCategory),
-            item(kLegacyEffect, 0x30000013U, items::Tier::exotic, kLegacyCategory),
-            item(kLaterDefault, 0x30000020U, items::Tier::none, kLaterCategory),
-            item(kLaterActive, 0x30000021U, items::Tier::exotic, kLaterCategory),
-            item(kPlaceholderDefault, 0x30000030U, items::Tier::none, kPlaceholderCategory),
-            item(kPlaceholderActive, 0x30000031U, items::Tier::exotic, kPlaceholderCategory),
-            item(kOtherActive, 0x30000041U, items::Tier::legendary, kOtherCategory),
-        };
-        detailRows = {
-            weapon_detail(kLegacyWeapon, kLegacyWeaponHash, 7, kLegacyDefault),
-            weapon_detail(kLaterWeapon, kLaterWeaponHash, 6, kLaterDefault),
-            weapon_detail(kPlaceholderWeapon, kPlaceholderWeaponHash, 5, kPlaceholderDefault),
-            weapon_detail(kNonCatalystWeapon, kNonCatalystWeaponHash, 4, kOtherDefault),
-            plug_detail(kLegacyDefault, 0x30000010U, 100),
-            plug_detail(kLegacyProgress, 0x30000011U, 1),
-            plug_detail(kLegacyComplete, 0x30000012U, 100),
-            plug_detail(kLegacyEffect, 0x30000013U, 100, true),
-            plug_detail(kLaterActive, 0x30000021U, 100, true),
-            plug_detail(kPlaceholderActive, 0x30000031U, 100, true),
-            plug_detail(kOtherActive, 0x30000041U, 100, true),
-        };
-    }
-
-    [[nodiscard]] catalysts::Source source() const noexcept {
-        return {{kTimestamp, kImageSize, 0}, itemRows, detailRows, rules, pools, members};
-    }
-
-    [[nodiscard]] catalysts::Facts facts() const noexcept {
-        return {kTimestamp, kImageSize, released};
-    }
-};
-
-bool derive(Fixture& fixture, std::size_t& count, catalysts::Report& report) noexcept {
-    return catalysts::derive(fixture.source(), fixture.facts(), fixture.output, count, report);
-}
-
-void test_structural_lifecycles() noexcept {
-    Fixture fixture;
-    std::size_t count = 0;
-    catalysts::Report report{};
-    expect(derive(fixture, count, report), "synthetic catalyst catalog derives");
-    expect(count == 3, "only two released and one placeholder catalyst are found");
-    expect(report.released == 2 && report.placeholder == 1 && report.unsupported == 0,
-           "catalog report separates released and placeholder rows");
-    if (count != 3) {
-        return;
-    }
-    expect(fixture.output[0].itemDefinitionIndex == Fixture::kLegacyWeapon
-               && fixture.output[0].completedPlugDefinitionIndex == Fixture::kLegacyComplete
-               && fixture.output[0].effectDefinitionIndex == Fixture::kLegacyEffect,
-           "legacy three-state lifecycle resolves display and effect rows");
-    expect(fixture.output[1].itemDefinitionIndex == Fixture::kLaterWeapon
-               && fixture.output[1].completedPlugDefinitionIndex == Fixture::kLaterActive
-               && fixture.output[1].effectDefinitionIndex == Fixture::kLaterActive,
-           "later two-state lifecycle resolves its direct active plug");
-    expect(fixture.output[2].availability == catalysts::Availability::placeholder,
-           "unreleased structural catalyst remains a placeholder");
-    expect(std::none_of(std::span(fixture.output).first(count).begin(),
-                        std::span(fixture.output).first(count).end(),
-                        [](const catalysts::Definition& value) {
-                            return value.itemDefinitionIndex == Fixture::kNonCatalystWeapon;
-                        }),
-           "a two-state lane without an exotic effect item is not a catalyst");
-    expect(catalysts::matches_derived(
-               fixture.source(), fixture.facts(), std::span(fixture.output).first(count)),
-           "stored catalog matches a fresh structural derivation");
-
-    auto altered = fixture.output;
-    altered[0].completedPlugDefinitionIndex = Fixture::kLegacyEffect;
-    expect(!catalysts::matches_derived(
-               fixture.source(), fixture.facts(), std::span(altered).first(count)),
-           "catalog rejects a completed plug outside its socket pool");
-
-    altered = fixture.output;
-    altered[0].completedPlugDefinitionIndex = Fixture::kLegacyProgress;
-    expect(!catalysts::matches_derived(
-               fixture.source(), fixture.facts(), std::span(altered).first(count)),
-           "catalog rejects a completed plug outside the derived role");
-
-    Fixture legendaryWeapon;
-    legendaryWeapon.itemRows[2].tier = static_cast<std::uint8_t>(items::Tier::legendary);
-    expect(derive(legendaryWeapon, count, report) && count == 2 && report.placeholder == 0,
-           "non-exotic weapons are outside catalyst scope");
-
-    Fixture exoticArmor;
-    exoticArmor.detailRows[2].equipmentSlot = std::int8_t{3};
-    expect(derive(exoticArmor, count, report) && count == 2 && report.placeholder == 0,
-           "exotic armor is outside catalyst scope");
-}
-
-void test_safe_derivation_failures() noexcept {
-    Fixture fixture;
-    std::size_t count = 0;
-    catalysts::Report report{};
-    const catalysts::Facts facts = fixture.facts();
-    expect(catalysts::supports_build(fixture.source().build, facts),
-           "target catalyst facts support their exact executable build");
-    catalysts::Source mismatched = fixture.source();
-    ++mismatched.build.imageSize;
-    expect(!catalysts::supports_build(mismatched.build, facts),
-           "target catalyst facts reject a different executable build");
-    expect(!catalysts::derive(mismatched, fixture.facts(), fixture.output, count, report)
-               && count == 0 && report.error == catalysts::Error::unsupportedBuild,
-           "build fingerprint mismatch fails only the catalog derivation");
-
-    std::array<std::uint32_t, 3> missingRelease{kLegacyWeaponHash, kLaterWeaponHash, 0x1FFFFFFFU};
-    catalysts::Facts missingFacts{kTimestamp, kImageSize, missingRelease};
-    expect(!catalysts::derive(fixture.source(), missingFacts, fixture.output, count, report)
-               && count == 0 && report.error == catalysts::Error::missingReleased,
-           "missing released weapon clears all staged catalog rows");
-
-    fixture.detailRows[5].maxStackSize = 100;
-    expect(!derive(fixture, count, report) && count == 0
-               && report.error == catalysts::Error::missingReleased,
-           "unclear released legacy lifecycle fails closed");
-}
-
-void test_catalog_application() noexcept {
-    Fixture fixture;
-    std::size_t count = 0;
-    catalysts::Report report{};
-    if (!derive(fixture, count, report)
-        || !catalysts::replace(std::span(fixture.output).first(count))) {
-        expect(false, "application fixture publishes");
-        return;
-    }
-
-    for (std::uint32_t flags = 0; flags <= 3; ++flags) {
-        std::array<std::optional<std::uint16_t>, 12> plugs{};
-        plugs[7] = Fixture::kLegacyDefault;
-        std::uint32_t changedFlags = flags;
-        expect(catalysts::apply_completed(Fixture::kLegacyWeapon, changedFlags, plugs)
-                   == catalysts::ApplyResult::completed,
-               "released legacy catalyst completes");
-        expect(changedFlags == (flags | sunrise::state::account::inventory::kMasterworkItemFlag),
-               "completion preserves all prior item-state bits");
-        expect(plugs[7] == Fixture::kLegacyComplete,
-               "completion sockets the display plug from the allowed pool");
-    }
-    expect(catalysts::resolve_effect(Fixture::kLegacyWeapon, 7, Fixture::kLegacyComplete)
-               == Fixture::kLegacyEffect,
-           "legacy display plug resolves its effect item");
-    expect(catalysts::resolve_effect(Fixture::kLegacyWeapon, 7, Fixture::kLegacyDefault)
-               == Fixture::kLegacyDefault,
-           "uncompleted display plug remains unchanged");
-
-    std::array<std::optional<std::uint16_t>, 12> laterPlugs{};
-    laterPlugs[6] = Fixture::kLaterDefault;
-    std::uint32_t laterFlags = 0;
-    expect(catalysts::apply_completed(Fixture::kLaterWeapon, laterFlags, laterPlugs)
-                   == catalysts::ApplyResult::completed
-               && laterPlugs[6] == Fixture::kLaterActive,
-           "later catalyst sockets its direct active plug");
-
-    std::array<std::optional<std::uint16_t>, 12> placeholderPlugs{};
-    placeholderPlugs[5] = Fixture::kPlaceholderDefault;
-    const auto placeholderBefore = placeholderPlugs;
-    std::uint32_t placeholderFlags = 3;
-    expect(
-        catalysts::apply_completed(Fixture::kPlaceholderWeapon, placeholderFlags, placeholderPlugs)
-                == catalysts::ApplyResult::unchanged
-            && placeholderFlags == 3 && placeholderPlugs == placeholderBefore,
-        "placeholder catalyst remains unchanged");
-
-    std::array<std::optional<std::uint16_t>, 12> invalidPlugs{};
-    invalidPlugs[7] = Fixture::kLegacyDefault;
-    const auto invalidBefore = invalidPlugs;
-    std::uint32_t invalidFlags = 0x8U;
-    expect(catalysts::apply_completed(Fixture::kLegacyWeapon, invalidFlags, invalidPlugs)
-                   == catalysts::ApplyResult::failed
-               && invalidFlags == 0x8U && invalidPlugs == invalidBefore,
-           "invalid item state fails without a partial change");
-
-    std::array<std::optional<std::uint16_t>, 7> shortPlugs{};
-    const auto shortBefore = shortPlugs;
-    std::uint32_t shortFlags = 1;
-    expect(catalysts::apply_completed(Fixture::kLegacyWeapon, shortFlags, shortPlugs)
-                   == catalysts::ApplyResult::failed
-               && shortFlags == 1 && shortPlugs == shortBefore,
-           "missing catalyst lane fails without a partial change");
-
-    catalysts::set_completion_enabled(false);
-    std::array<std::optional<std::uint16_t>, 12> disabledPlugs{};
-    disabledPlugs[7] = Fixture::kLegacyDefault;
-    const auto disabledBefore = disabledPlugs;
-    std::uint32_t disabledFlags = 2;
-    expect(catalysts::apply_completed(Fixture::kLegacyWeapon, disabledFlags, disabledPlugs)
-                   == catalysts::ApplyResult::unchanged
-               && disabledFlags == 2 && disabledPlugs == disabledBefore,
-           "global policy disables completion without changing the item");
-    catalysts::clear();
-    expect(!catalysts::completion_enabled(),
-           "clearing catalyst records preserves the configured completion policy");
-    catalysts::set_completion_enabled(true);
-}
-
-void test_cache_record() noexcept {
-    const catalysts::Definition definition{
-        0x11111111U, 3, 7, 9, 5, catalysts::Availability::released};
-    cache_records::ExoticCatalystRecord record{};
-    catalysts::Definition decoded{};
-    expect(cache_records::encode(definition, record), "catalyst cache record encodes");
-    expect(cache_records::decode(record, decoded)
-               && decoded.itemDefinitionHash == definition.itemDefinitionHash
-               && decoded.itemDefinitionIndex == definition.itemDefinitionIndex
-               && decoded.completedPlugDefinitionIndex == definition.completedPlugDefinitionIndex
-               && decoded.effectDefinitionIndex == definition.effectDefinitionIndex
-               && decoded.socketLane == definition.socketLane
-               && decoded.availability == definition.availability,
-           "catalyst cache record round-trips");
-    record.availability = 0xFFU;
-    expect(!cache_records::decode(record, decoded), "invalid catalyst availability is rejected");
-    expect(cache_records::kCacheFormatVersion == 45,
-           "catalyst records use one cache bump over upstream version 44");
-}
-
-void test_persistence_action() noexcept {
-    using enum persistence::CacheAction;
-    expect(persistence::cache_action(false, false, catalysts::Error::unsupportedBuild)
-               == waitForDomains,
-           "persistence waits for required domains");
-    expect(persistence::cache_action(true, false, catalysts::Error::unsupportedBuild)
-               == writeRequiredDomains,
-           "unsupported builds cache every required domain without a catalyst catalog");
-    constexpr std::array rejectedErrors{
-        catalysts::Error::none,
-        catalysts::Error::noCatalyst,
-        catalysts::Error::placeholderOnly,
-        catalysts::Error::missingReleased,
-        catalysts::Error::ambiguousLifecycle,
-        catalysts::Error::invalidSocket,
-    };
-    for (const catalysts::Error error : rejectedErrors) {
-        expect(persistence::cache_action(true, false, error) == waitForDomains,
-               "non-build catalyst failures stay failed closed");
-    }
-    expect(persistence::cache_action(true, true, catalysts::Error::none) == writeCompleteCache,
-           "a complete catalog permits one cache write");
-}
-
-} // namespace
-
-void test_exotic_catalysts() noexcept {
-    test_structural_lifecycles();
-    test_safe_derivation_failures();
-    test_catalog_application();
-    test_cache_record();
-    test_persistence_action();
-    catalysts::clear();
-}

+ 0 - 226
tests/loadout_catalyst_regression_tests.cpp

@@ -1,226 +0,0 @@
-#include <array>
-#include <cstdint>
-#include <cstdio>
-#include <optional>
-#include <span>
-
-#include "middleware/datagen/character_record/appearance/internal.h"
-#include "middleware/datagen/family4/loadout/loadout_item_resolver.h"
-#include "state/build_data/inventory/buckets/inventory_bucket_catalog.h"
-#include "state/build_data/items/catalysts/exotic_catalyst_catalog.h"
-#include "state/build_data/items/details/item_detail_catalog.h"
-#include "state/build_data/items/item_catalog.h"
-#include "state/build_data/runtime.h"
-#include "state/build_data/socket_entry_lists/socket_entry_list_catalog.h"
-
-extern int failures;
-
-namespace {
-
-bool forceCompletionFailure{};
-
-} // namespace
-
-namespace sunrise::state::build_data {
-
-bool find_item_definition_hash(std::uint32_t definitionHash,
-                               items::Definition& definition) noexcept {
-    return items::find_hash(definitionHash, definition);
-}
-
-bool find_configured_item_detail(std::uint16_t definitionIndex,
-                                 items::details::Definition& definition) noexcept {
-    return items::details::find(definitionIndex, definition);
-}
-
-bool find_inventory_bucket_descriptor(std::uint8_t bucketId,
-                                      inventory::buckets::Descriptor& descriptor) noexcept {
-    return inventory::buckets::find(bucketId, descriptor);
-}
-
-bool find_socket_entry_list(std::uint16_t definitionIndex,
-                            socket_entry_lists::Definition& definition) noexcept {
-    return socket_entry_lists::find(definitionIndex, definition);
-}
-
-bool find_socket_entry_table(std::uint16_t definitionIndex,
-                             socket_entry_lists::EntryTable& table) noexcept {
-    return socket_entry_lists::find_entry_table(definitionIndex, table);
-}
-
-items::catalysts::ApplyResult
-complete_exotic_catalyst(std::uint16_t itemDefinitionIndex,
-                         std::uint32_t& flags,
-                         std::span<std::optional<std::uint16_t>> plugs) noexcept {
-    if (forceCompletionFailure) {
-        flags = 7;
-        if (!plugs.empty()) {
-            plugs.front() = std::uint16_t{4};
-            plugs.back() = std::uint16_t{4};
-        }
-        return items::catalysts::ApplyResult::failed;
-    }
-    return items::catalysts::apply_completed(itemDefinitionIndex, flags, plugs);
-}
-
-bool find_investment_constants(constants::InvestmentConstants& value) noexcept {
-    value = {.extracted = true, .lightStatRow = 1, .characterStatRows = {2, 3, 4, 5, 6, 7}};
-    return true;
-}
-
-std::uint16_t resolve_exotic_catalyst_effect(std::uint16_t itemDefinitionIndex,
-                                             std::uint8_t socketLane,
-                                             std::uint16_t plugDefinitionIndex) noexcept {
-    return items::catalysts::resolve_effect(itemDefinitionIndex, socketLane, plugDefinitionIndex);
-}
-
-} // namespace sunrise::state::build_data
-
-namespace {
-
-namespace appearance = sunrise::middleware::datagen::character_record::appearance;
-namespace buckets = sunrise::state::build_data::inventory::buckets;
-namespace catalysts = sunrise::state::build_data::items::catalysts;
-namespace character_layout = sunrise::middleware::datagen::character_record::layout;
-namespace details = sunrise::state::build_data::items::details;
-namespace items = sunrise::state::build_data::items;
-namespace loadout = sunrise::middleware::datagen::family4::loadout;
-namespace socket_lists = sunrise::state::build_data::socket_entry_lists;
-
-constexpr std::uint32_t kWeaponHash = 0x12345678U;
-constexpr std::uint32_t kDefaultPlugHash = 0x23456789U;
-constexpr std::uint32_t kProgressPlugHash = 0x3456789AU;
-constexpr std::uint32_t kCompletedPlugHash = 0x456789ABU;
-constexpr std::uint32_t kEffectHash = 0x56789ABCU;
-constexpr std::uint16_t kCatalystPerk = 77;
-constexpr std::uint8_t kCatalystStatRow = 24;
-constexpr std::int32_t kCatalystStatValue = 7;
-
-void expect(bool value, const char* label) noexcept {
-    if (!value) {
-        std::fprintf(stderr, "FAIL %s\n", label);
-        ++failures;
-    }
-}
-
-items::Definition
-item(std::uint16_t index, std::uint32_t hash, items::Tier tier = items::Tier::none) noexcept {
-    items::Definition value{};
-    value.definitionHash = hash;
-    value.definitionIndex = index;
-    value.tier = static_cast<std::uint8_t>(tier);
-    return value;
-}
-
-void clear_catalogs() noexcept {
-    forceCompletionFailure = false;
-    catalysts::clear();
-    socket_lists::clear();
-    buckets::clear();
-    details::clear();
-    items::clear();
-}
-
-} // namespace
-
-void test_resolved_catalyst_output() noexcept {
-    clear_catalogs();
-    std::array<items::Definition, 5> itemRows{
-        item(0, kWeaponHash, items::Tier::exotic),
-        item(1, kDefaultPlugHash),
-        item(2, kProgressPlugHash),
-        item(3, kCompletedPlugHash),
-        item(4, kEffectHash, items::Tier::exotic),
-    };
-    itemRows[0].bucketId = 0;
-
-    details::Definition detail{};
-    detail.definitionIndex = 0;
-    detail.definitionHash = kWeaponHash;
-    detail.bucketId = 0;
-    detail.maxStackSize = 1;
-    detail.instancedDefinitionState = details::InstancedDefinitionState::instanced;
-    detail.equipmentSlot = std::int8_t{7};
-    detail.ordinarySocketState = details::OrdinarySocketState::present;
-    detail.ordinarySocketCount = 8;
-    detail.initialPlugIndices[7] = 1;
-    detail.socketEntryListIndex = 0;
-    details::Definition effectDetail{};
-    effectDetail.definitionIndex = 4;
-    effectDetail.definitionHash = kEffectHash;
-    effectDetail.bucketId = 0;
-    effectDetail.maxStackSize = 1;
-    effectDetail.instancedDefinitionState = details::InstancedDefinitionState::stackable;
-    effectDetail.sandboxPerkCount = 1;
-    effectDetail.sandboxPerks[0] = kCatalystPerk;
-    effectDetail.statCount = 1;
-    effectDetail.stats[0] = {kCatalystStatRow, kCatalystStatValue};
-    const std::array detailRows{detail, effectDetail};
-
-    const std::array bucketRows{
-        buckets::Descriptor{0, buckets::ArraySelector::character, 0, 10, 7, 0},
-    };
-    const std::array socketListRows{
-        socket_lists::Definition{0x12345678U, 0, 0, 0},
-    };
-    const std::array catalystRows{
-        catalysts::Definition{kWeaponHash, 0, 3, 4, 7, catalysts::Availability::released},
-    };
-
-    const bool ready = items::replace(itemRows) && details::replace(detailRows)
-                       && buckets::replace(bucketRows) && socket_lists::replace(socketListRows)
-                       && catalysts::replace(catalystRows);
-    expect(ready, "resolved-output fixture publishes");
-    if (!ready) {
-        clear_catalogs();
-        return;
-    }
-
-    sunrise::state::account::inventory::Item authored{};
-    authored.instanceSoid = 1;
-    authored.definitionHash = kWeaponHash;
-    authored.level = 50;
-    authored.quantity = 1;
-    authored.flags = 3;
-    sunrise::state::CharacterState character{};
-    loadout::Candidate output{};
-    expect(
-        loadout::resolve_item(authored, character, itemRows.size(), socketListRows.size(), output),
-        "resolved client item accepts catalyst completion");
-    expect(output.item.flags == 7, "resolved client item carries all item-state bits");
-    expect(output.item.instance.ordinarySockets.plugs[7] == 3,
-           "resolved client item carries the completed display plug");
-
-    loadout::ResolvedInstances instances{};
-    instances.itemCount = 1;
-    instances.items[0].equipmentSlot = 7;
-    instances.items[0].instance = output.item.instance;
-    character_layout::Appearance appearanceRecord{};
-    appearanceRecord.smallBankA.fill(character_layout::kEmptyDefinitionIndex);
-    appearanceRecord.overflowHashes.fill(character_layout::kNoHash);
-    appearance::apply_perk_banks(instances, appearanceRecord);
-    expect(appearanceRecord.smallBankA[0] == kCatalystPerk,
-           "display plug resolves the catalyst perk from its effect row");
-    appearance::apply_overflow_hashes(instances, appearanceRecord);
-    expect(appearanceRecord.overflowHashes[0] == kEffectHash,
-           "display plug resolves the catalyst hash from its effect row");
-    expect(appearance::apply_stats(instances, 50, appearanceRecord),
-           "display plug builds catalyst stats");
-    expect(appearanceRecord.weaponStats[0][0].key == static_cast<std::int8_t>(kCatalystStatRow)
-               && appearanceRecord.weaponStats[0][0].value == kCatalystStatValue,
-           "display plug resolves the catalyst stat from its effect row");
-
-    forceCompletionFailure = true;
-    loadout::Candidate failedOutput{};
-    failedOutput.item.flags = 0xA5A5U;
-    expect(loadout::resolve_item(
-               authored, character, itemRows.size(), socketListRows.size(), failedOutput),
-           "catalyst apply failure preserves the resolved base item");
-    expect(failedOutput.item.flags == authored.flags,
-           "catalyst apply failure does not leak changed flags");
-    expect(failedOutput.item.instance.ordinarySockets.plugs[7] == 1
-               && !failedOutput.item.instance.ordinarySockets.plugs[0].has_value(),
-           "catalyst apply failure does not leak changed plugs");
-
-    clear_catalogs();
-}

+ 0 - 75
tests/opcode406_item_state_regression_tests.cpp

@@ -1,75 +0,0 @@
-#include <array>
-#include <cstddef>
-#include <cstdint>
-#include <cstdio>
-#include <span>
-
-#include "middleware/web_service/messages/opcode406.h"
-
-extern int failures;
-
-namespace {
-
-namespace opcode406 = sunrise::middleware::web_service::messages::opcode406;
-
-void expect(bool value, const char* label) noexcept {
-    if (!value) {
-        std::fprintf(stderr, "FAIL %s\n", label);
-        ++failures;
-    }
-}
-
-void write_bits(std::span<std::byte> output,
-                std::size_t& position,
-                std::uint64_t value,
-                std::uint8_t width) noexcept {
-    for (std::uint8_t index = 0; index < width; ++index) {
-        const unsigned shift = static_cast<unsigned>(width - index - 1);
-        const auto bit = static_cast<unsigned>((value >> shift) & 1U);
-        const std::size_t byteIndex = position / 8;
-        const unsigned byteShift = static_cast<unsigned>(7 - (position % 8));
-        output[byteIndex] |= std::byte{static_cast<unsigned char>(bit << byteShift)};
-        ++position;
-    }
-}
-
-std::array<std::byte, 15> payload(std::uint32_t flags) noexcept {
-    std::array<std::byte, 15> output{};
-    std::size_t position = 0;
-    write_bits(output, position, 1, 1);
-    write_bits(output, position, 0x1234U, 64);
-    write_bits(output, position, 1, 1);
-    write_bits(output, position, 42, 15);
-    write_bits(output, position, 0x80000000ULL + flags, 32);
-    write_bits(output, position, 0, 7);
-    return output;
-}
-
-} // namespace
-
-void test_opcode406_item_state() noexcept {
-    for (std::uint32_t flags = 0; flags <= 7; ++flags) {
-        const auto bytes = payload(flags);
-        const sunrise::middleware::web_service::Message message{opcode406::kOpcode, 1, bytes};
-        opcode406::Request request{};
-        expect(opcode406::parse_request(message, request),
-               "opcode 406 accepts known item-state bits");
-        expect(request.instanceSoid == 0x1234U && request.definitionIndex == 42
-                   && request.flags == flags,
-               "opcode 406 preserves known item-state bits");
-    }
-
-    const auto bytes = payload(8);
-    const sunrise::middleware::web_service::Message message{opcode406::kOpcode, 1, bytes};
-    opcode406::Request request{};
-    expect(!opcode406::parse_request(message, request),
-           "opcode 406 rejects unknown item-state bits");
-
-    const auto maximumBytes = payload(0x7FFFFFFFU);
-    const sunrise::middleware::web_service::Message maximumMessage{
-        opcode406::kOpcode, 1, maximumBytes};
-    opcode406::Request maximumRequest{};
-    expect(!opcode406::parse_request(maximumMessage, maximumRequest)
-               && maximumRequest.flags == 0x7FFFFFFFU,
-           "opcode 406 decodes the maximum biased value without overflow");
-}

+ 0 - 190
tests/settings_regression_tests.cpp

@@ -1,190 +0,0 @@
-#include <Windows.h>
-
-#include <array>
-#include <crtdbg.h>
-#include <cstdint>
-#include <cstdio>
-#include <string_view>
-
-#include "core/settings/settings.h"
-#include "state/account/inventory/item_state.h"
-
-namespace sunrise::core::log {
-
-Settings defaults() noexcept {
-    return {};
-}
-
-void early(std::string_view) noexcept {}
-
-void write(Channel, Level, std::string_view) noexcept {}
-
-} // namespace sunrise::core::log
-
-namespace sunrise::state::activity::defaults {
-
-ActivityDefaults authored() noexcept {
-    return {};
-}
-
-bool valid(const DefaultDestination&) noexcept {
-    return true;
-}
-
-bool valid(const ActivityDefaults&) noexcept {
-    return true;
-}
-
-} // namespace sunrise::state::activity::defaults
-
-namespace sunrise::state::entitlements {
-
-Table authored() noexcept {
-    return {};
-}
-
-bool valid(const Entitlement&) noexcept {
-    return true;
-}
-
-bool valid(const Table&) noexcept {
-    return true;
-}
-
-} // namespace sunrise::state::entitlements
-
-namespace sunrise::state::account {
-
-bool valid_authored(const AccountState&) noexcept {
-    return true;
-}
-
-} // namespace sunrise::state::account
-
-namespace sunrise::state::account::settings {
-
-bool valid(const AccountSettings&) noexcept {
-    return true;
-}
-
-} // namespace sunrise::state::account::settings
-
-int failures{};
-
-namespace {
-
-void expect(bool value, const char* label) noexcept {
-    if (!value) {
-        std::fprintf(stderr, "FAIL %s\n", label);
-        ++failures;
-    }
-}
-
-bool parse_catalyst_policy(std::string_view json, bool& output) noexcept {
-    sunrise::core::settings::Settings settings{};
-    if (!sunrise::core::settings::parse(json, settings)) {
-        return false;
-    }
-    output = settings.completeExoticCatalysts;
-    return true;
-}
-
-void test_item_state_contract() noexcept {
-    for (std::uint32_t flags = 0; flags <= 0x7U; ++flags) {
-        expect(sunrise::state::account::inventory::valid_item_state(flags),
-               "known item-state bits are valid");
-    }
-    expect(!sunrise::state::account::inventory::valid_item_state(0x8U),
-           "unknown item-state bits are invalid");
-}
-
-void test_optional_catalyst_policy() noexcept {
-    bool enabled = false;
-    expect(sunrise::core::settings::kSettingsVersion == 8,
-           "optional catalyst key keeps settings version 8");
-    expect(parse_catalyst_policy(R"({"version":8})", enabled) && enabled,
-           "version 8 without catalyst key keeps the true default");
-    expect(parse_catalyst_policy(R"({"version":8,"complete_exotic_catalysts":false})", enabled)
-               && !enabled,
-           "version 8 accepts an explicit false catalyst policy");
-    expect(
-        !parse_catalyst_policy(
-            R"({"version":8,"complete_exotic_catalysts":true,"complete_exotic_catalysts":false})",
-            enabled),
-        "duplicate catalyst policy is rejected");
-    expect(!parse_catalyst_policy(R"({"version":8,"complete_exotic_catalysts":1})", enabled),
-           "non-boolean catalyst policy is rejected");
-}
-
-void test_settings_file_round_trip() noexcept {
-    constexpr std::string_view document = R"({"version":8,"complete_exotic_catalysts":false})";
-    std::array<char, MAX_PATH + 1> directory{};
-    std::array<char, MAX_PATH + 1> path{};
-    const DWORD directoryLength =
-        GetTempPathA(static_cast<DWORD>(directory.size()), directory.data());
-    if (directoryLength == 0 || directoryLength >= directory.size()
-        || GetTempFileNameA(directory.data(), "sun", 0, path.data()) == 0) {
-        expect(false, "settings round trip creates a temporary file");
-        return;
-    }
-
-    HANDLE file = CreateFileA(
-        path.data(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, nullptr);
-    DWORD written = 0;
-    const bool saved =
-        file != INVALID_HANDLE_VALUE
-        && WriteFile(file, document.data(), static_cast<DWORD>(document.size()), &written, nullptr)
-        && written == document.size();
-    if (file != INVALID_HANDLE_VALUE) {
-        CloseHandle(file);
-    }
-
-    std::array<char, 128> reloaded{};
-    file = CreateFileA(path.data(),
-                       GENERIC_READ,
-                       FILE_SHARE_READ,
-                       nullptr,
-                       OPEN_EXISTING,
-                       FILE_ATTRIBUTE_NORMAL,
-                       nullptr);
-    DWORD read = 0;
-    const bool loaded =
-        file != INVALID_HANDLE_VALUE
-        && ReadFile(file, reloaded.data(), static_cast<DWORD>(reloaded.size()), &read, nullptr)
-        && read == document.size();
-    if (file != INVALID_HANDLE_VALUE) {
-        CloseHandle(file);
-    }
-    DeleteFileA(path.data());
-
-    bool enabled = true;
-    expect(saved && loaded
-               && parse_catalyst_policy(std::string_view{reloaded.data(), read}, enabled)
-               && !enabled,
-           "settings file round trip preserves the catalyst policy");
-}
-
-} // namespace
-
-void test_exotic_catalysts() noexcept;
-void test_resolved_catalyst_output() noexcept;
-void test_opcode406_item_state() noexcept;
-
-int main() {
-#if defined(_DEBUG)
-    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
-    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
-#endif
-    test_item_state_contract();
-    test_optional_catalyst_policy();
-    test_settings_file_round_trip();
-    test_opcode406_item_state();
-    test_exotic_catalysts();
-    test_resolved_catalyst_output();
-    if (failures != 0) {
-        std::fprintf(stderr, "%d regression test(s) failed\n", failures);
-        return 1;
-    }
-    std::puts("All regression tests passed");
-    return 0;
-}