namespace AyCode.Core.Tests.TestModels; /// /// Generic factory for the 5-level test-data hierarchy (Order → OrderItem → Pallet → Measurement → /// MeasurementPoint) + cross-cutting shared types (Tag, Category, User, Metadata, UserPreferences). /// One closing-generic alias per family — see (the _All_True /// family, kept on the bare class name for MSTEST backward compatibility) and /// (the _All_False family). /// /// The static _idCounter below is per-closed-generic (verified via C# smoke): each family /// has an independent ID sequence, so calls like TestDataFactory.ResetIdCounter() reset only the /// _All_True counter, leaving any TestDataFactory_All_False.NextId() sequence intact. /// Each family's "Reset → Next" pattern stays internally consistent. /// /// All placeholder strings use Hungarian (UTF-8 multi-byte) content to exercise the UTF-8 /// encoder/decoder path rather than the ASCII fast-path. This makes the benchmark reflect realistic /// i18n payloads, not just the FixStrAscii / StringAscii marker fast-paths. /// public abstract class TestDataFactory where TOrder : TestOrderBase, new() where TItem : TestOrderItemBase, new() where TPallet : TestPalletBase, new() where TMeasurement : TestMeasurementBase, new() where TPoint : TestMeasurementPointBase, new() where TTag : SharedTagBase, new() where TUser : SharedUserBase, new() where TCategory : SharedCategoryBase, new() where TMetadata : MetadataInfoBase, new() where TPreferences : UserPreferencesBase, new() { private static int _idCounter = 1; /// /// Reset the ID counter (call in test setup). Resets ONLY this family's counter — sibling families /// keep their own independent counter state. /// public static void ResetIdCounter() => _idCounter = 1; /// /// Get the next unique ID. Per-family counter — see class docs for the isolation rationale. /// public static int NextId() => _idCounter++; #region Simple Object Creation /// /// Create a shared tag for cross-reference testing /// public static TTag CreateTag(string? name = null, string? color = null) { var id = _idCounter++; return new TTag { Id = id, Name = name ?? $"Tag-{id}", Color = color ?? $"Color-#{id:X2}{(id * 10) % 256:X2}{(id * 20) % 256:X2}", Priority = id % 5, IsActive = id % 2 == 0, CreatedAt = DateTime.UtcNow.AddDays(-id), Description = $"Tag description {id}" }; } /// /// Create a shared category /// public static TCategory CreateCategory(string? name = null, int? parentId = null) { var id = _idCounter++; return new TCategory { Id = id, Name = name ?? $"Category-{id}", Description = $"Category description {id}", SortOrder = id * 100, IsDefault = id == 1, ParentCategoryId = parentId, CreatedAt = DateTime.UtcNow.AddMonths(-id), UpdatedAt = DateTime.UtcNow.AddDays(-id) }; } /// /// Create a shared user for cross-reference testing /// public static TUser CreateUser(string? username = null, TestUserRole role = TestUserRole.User) { var id = _idCounter++; return new TUser { Id = id, Username = username ?? $"user{id}", Email = $"user{id}@test.com", FirstName = $"FirstName{id}", LastName = $"LastName{id}", IsActive = true, Role = role, LastLoginAt = DateTime.UtcNow.AddHours(-id), CreatedAt = DateTime.UtcNow.AddYears(-1), Preferences = new TPreferences { Theme = id % 2 == 0 ? "dark" : "light", Language = "english", NotificationsEnabled = true, EmailDigestFrequency = "daily" } }; } /// /// Create metadata info (non-IId) /// public static TMetadata CreateMetadata(string? key = null, bool withChild = false) { var id = _idCounter++; return new TMetadata { Key = key ?? $"Metadata-{id}", Value = $"MetadataValue-{id}", Timestamp = DateTime.UtcNow.AddMinutes(-id * 10), ChildMetadata = withChild ? CreateMetadata($"Child-{id}", false) : null }; } #endregion #region Hierarchy Creation (5 Levels) /// /// Create a deep order hierarchy with configurable depth. /// Supports both IId-based (Tag, User, Category) and Non-IId (Preferences) shared references. /// public static TOrder CreateOrder( int itemCount = 2, int palletsPerItem = 2, int measurementsPerPallet = 2, int pointsPerMeasurement = 3, TTag? sharedTag = null, TUser? sharedUser = null, TMetadata? sharedMetadata = null, TPreferences? sharedPreferences = null, TCategory? sharedCategory = null) { // If sharedUser is provided but no sharedPreferences, use the user's preferences as shared sharedPreferences ??= sharedUser?.Preferences; var order = new TOrder { Id = _idCounter++, OrderNumber = $"Order-{_idCounter:D4}", Status = TestStatus.Pending, CreatedAt = DateTime.UtcNow, TotalAmount = 1000m + _idCounter * 100, PrimaryTag = sharedTag, SecondaryTag = sharedTag, // Same reference for $ref testing Owner = sharedUser, Category = sharedCategory, OrderMetadata = sharedMetadata, AuditMetadata = sharedMetadata // Same reference for Newtonsoft $ref }; if (sharedTag != null) { order.Tags.Add(sharedTag); } for (int i = 0; i < itemCount; i++) { var item = CreateOrderItem( palletsPerItem, measurementsPerPallet, pointsPerMeasurement, sharedTag, sharedUser, sharedMetadata, sharedPreferences, sharedCategory); item.ParentOrder = order; order.Items.Add(item); } return order; } /// /// Create an order item with pallets. /// public static TItem CreateOrderItem( int palletCount = 2, int measurementsPerPallet = 2, int pointsPerMeasurement = 3, TTag? sharedTag = null, TUser? sharedUser = null, TMetadata? sharedMetadata = null, TPreferences? sharedPreferences = null, TCategory? sharedCategory = null) { // Create assignee - if sharedUser provided, use it. Otherwise create new user with sharedPreferences TUser? assignee = sharedUser; if (assignee == null && sharedPreferences != null) { // Create a new user but with shared preferences (Non-IId ref testing) assignee = CreateUser(); assignee.Preferences = sharedPreferences; } var item = new TItem { Id = _idCounter++, ProductName = $"Product-{_idCounter}", Quantity = 10 + _idCounter, UnitPrice = 5.5m * _idCounter, Status = TestStatus.Pending, Tag = sharedTag, Assignee = assignee, ItemMetadata = sharedMetadata }; for (int i = 0; i < palletCount; i++) { // Pass shared references to all levels - creates many shared refs! var pallet = CreatePallet( measurementsPerPallet, pointsPerMeasurement, sharedMetadata, sharedTag, // IId shared ref sharedUser, // IId shared ref sharedCategory); // IId shared ref pallet.ParentItem = item; item.Pallets.Add(pallet); } return item; } /// /// Create a pallet with measurements /// public static TPallet CreatePallet( int measurementCount = 2, int pointsPerMeasurement = 3, TMetadata? sharedMetadata = null, TTag? sharedTag = null, TUser? sharedInspector = null, TCategory? sharedCategory = null) { var pallet = new TPallet { Id = _idCounter++, PalletCode = $"PalletCode-{_idCounter:D4}", TrayCount = 5 + _idCounter % 10, Status = TestStatus.Pending, Weight = 100.5 + _idCounter, PalletMetadata = sharedMetadata, Tag = sharedTag, Inspector = sharedInspector, Category = sharedCategory }; for (int i = 0; i < measurementCount; i++) { var measurement = CreateMeasurement(pointsPerMeasurement, sharedTag, sharedInspector); measurement.ParentPallet = pallet; pallet.Measurements.Add(measurement); } return pallet; } /// /// Create a measurement with points /// public static TMeasurement CreateMeasurement( int pointCount = 3, TTag? sharedTag = null, TUser? sharedOperator = null) { var measurement = new TMeasurement { Id = _idCounter++, Name = $"Measurement-{_idCounter}", TotalWeight = 100.5 + _idCounter, CreatedAt = DateTime.UtcNow, Tag = sharedTag, Operator = sharedOperator }; for (int i = 0; i < pointCount; i++) { var point = CreateMeasurementPoint(sharedTag, sharedOperator); point.ParentMeasurement = measurement; measurement.Points.Add(point); } return measurement; } /// /// Create a measurement point /// public static TPoint CreateMeasurementPoint( TTag? sharedTag = null, TUser? sharedVerifier = null) { var id = _idCounter++; return new TPoint { Id = id, Label = $"MeasurePoint-{id}", Value = 10.5 + (id * 0.1), MeasuredAt = DateTime.UtcNow, Tag = sharedTag, Verifier = sharedVerifier }; } #endregion } // ============================================================================================ // Closing-generic aliases. Each family carries its own static _idCounter (per-closed-generic // isolation — see C# runtime semantics). The base-class generic methods are accessible through both // aliases unchanged. // ============================================================================================ /// /// _All_True family factory — preserves the bare-name API surface /// (TestDataFactory.CreateTag(...), etc.) that the MSTEST tests and benchmark consumers depend /// on. Adds family-specific extras (, , /// ) that the generic base intentionally doesn't carry. /// public sealed class TestDataFactory : TestDataFactory< TestOrder_All_True, TestOrderItem_All_True, TestPallet_All_True, TestMeasurement_All_True, TestMeasurementPoint_All_True, SharedTag_All_True, SharedUser_All_True, SharedCategory_All_True, MetadataInfo_All_True, UserPreferences_All_True> { /// /// Create a large graph for benchmarking with many cross-references. /// Creates approximately (itemCount * palletsPerItem * measurementsPerPallet * pointsPerMeasurement) objects. /// public static TestOrder_All_True CreateBenchmarkOrder( int itemCount = 5, int palletsPerItem = 4, int measurementsPerPallet = 3, int pointsPerMeasurement = 5) { ResetIdCounter(); // Create shared references that will be used throughout var sharedTags = Enumerable.Range(1, 10).Select(_ => CreateTag()).ToList(); var sharedUser = CreateUser("mérőfelhasználó", TestUserRole.Admin); var sharedMetadata = CreateMetadata("mérőteszt", withChild: true); var order = new TestOrder_All_True { Id = NextId(), OrderNumber = $"MÉRŐTESZT-{NextId():D6}", Status = TestStatus.Processing, CreatedAt = DateTime.UtcNow, TotalAmount = 999999.99m, PrimaryTag = sharedTags[0], SecondaryTag = sharedTags[0], Owner = sharedUser, Category = CreateCategory("Mérőteszt"), OrderMetadata = sharedMetadata, AuditMetadata = sharedMetadata, Tags = sharedTags.Take(3).ToList() }; for (int i = 0; i < itemCount; i++) { var item = new TestOrderItem_All_True { Id = NextId(), ProductName = $"MérőTermék-{i}", Quantity = 100 + i * 10, UnitPrice = 25.99m + i, Status = (TestStatus)(i % 5), Tag = sharedTags[i % sharedTags.Count], Assignee = sharedUser, ItemMetadata = sharedMetadata }; item.ParentOrder = order; for (int p = 0; p < palletsPerItem; p++) { var pallet = new TestPallet_All_True { Id = NextId(), PalletCode = $"Raklapkód-{i}-{p}", TrayCount = 10 + p, Status = (TestStatus)(p % 4), Weight = 500.0 + p * 50, PalletMetadata = sharedMetadata }; pallet.ParentItem = item; for (int m = 0; m < measurementsPerPallet; m++) { var measurement = new TestMeasurement_All_True { Id = NextId(), Name = $"Mérés-{i}-{p}-{m}", TotalWeight = 50.0 + m * 10, CreatedAt = DateTime.UtcNow.AddMinutes(-m) }; measurement.ParentPallet = pallet; for (int pt = 0; pt < pointsPerMeasurement; pt++) { var point = new TestMeasurementPoint_All_True { Id = NextId(), Label = $"MérőPnt-{i}-{p}-{m}-{pt}", Value = 1.0 + pt * 0.5, MeasuredAt = DateTime.UtcNow.AddSeconds(-pt) }; point.ParentMeasurement = measurement; measurement.Points.Add(point); } pallet.Measurements.Add(measurement); } item.Pallets.Add(pallet); } order.Items.Add(item); } return order; } /// /// Create a large-scale benchmark order similar to production workloads. /// Targets ~50,000-100,000+ IId objects with deep hierarchy and shared references. /// public static TestOrder_All_True CreateLargeScaleBenchmarkOrder( int rootItemCount = 500, int palletsPerItem = 3, int measurementsPerPallet = 3, int pointsPerMeasurement = 4) { ResetIdCounter(); // Create shared references - these will be heavily reused (tests $ref handling) var sharedTags = Enumerable.Range(1, 50).Select(_ => CreateTag()).ToList(); var sharedUsers = Enumerable.Range(1, 20).Select(i => CreateUser($"felhasználó{i}", (TestUserRole)(i % 4))).ToList(); var sharedMetadata = CreateMetadata("nagy-méretű", withChild: true); var sharedCategories = Enumerable.Range(1, 10).Select(i => CreateCategory($"Kategória-{i}")).ToList(); var order = new TestOrder_All_True { Id = NextId(), OrderNumber = $"NAGYMÉRET-{NextId():D8}", Status = TestStatus.Processing, CreatedAt = DateTime.UtcNow, TotalAmount = 9999999.99m, PrimaryTag = sharedTags[0], SecondaryTag = sharedTags[0], // Same ref Owner = sharedUsers[0], Category = sharedCategories[0], OrderMetadata = sharedMetadata, AuditMetadata = sharedMetadata, // Same ref Tags = sharedTags.Take(5).ToList() }; for (int i = 0; i < rootItemCount; i++) { var item = new TestOrderItem_All_True { Id = NextId(), ProductName = $"Termék-{i}", Quantity = 100 + i, UnitPrice = 10.99m + (i % 100), Status = (TestStatus)(i % 5), Tag = sharedTags[i % sharedTags.Count], // Shared ref Assignee = sharedUsers[i % sharedUsers.Count], // Shared ref ItemMetadata = sharedMetadata // Shared ref }; item.ParentOrder = order; for (int p = 0; p < palletsPerItem; p++) { var pallet = new TestPallet_All_True { Id = NextId(), PalletCode = $"Raklapkód-{i}-{p}", TrayCount = 5 + (p % 10), Status = (TestStatus)(p % 4), Weight = 100.0 + p * 10, PalletMetadata = sharedMetadata // Shared ref }; pallet.ParentItem = item; for (int m = 0; m < measurementsPerPallet; m++) { var measurement = new TestMeasurement_All_True { Id = NextId(), Name = $"Mérés-{i}-{p}-{m}", TotalWeight = 10.0 + m, CreatedAt = DateTime.UtcNow }; measurement.ParentPallet = pallet; for (int pt = 0; pt < pointsPerMeasurement; pt++) { var point = new TestMeasurementPoint_All_True { Id = NextId(), Label = $"MérőPnt-{i}-{p}-{m}-{pt}", Value = pt * 0.1, MeasuredAt = DateTime.UtcNow }; point.ParentMeasurement = measurement; measurement.Points.Add(point); } pallet.Measurements.Add(measurement); } item.Pallets.Add(pallet); } order.Items.Add(item); } return order; } /// /// Calculate approximate object count for large-scale benchmark. /// public static int CalculateObjectCount(int rootItems, int pallets, int measurements, int points) { // 1 order + rootItems + (rootItems * pallets) + (rootItems * pallets * measurements) + (rootItems * pallets * measurements * points) // Plus shared objects (tags, users, metadata, categories) var sharedObjects = 50 + 20 + 2 + 10; // tags + users + metadata + categories var hierarchyObjects = 1 + rootItems + (rootItems * pallets) + (rootItems * pallets * measurements) + (rootItems * pallets * measurements * points); return sharedObjects + hierarchyObjects; } /// /// Create primitive test data for all-types testing /// public static PrimitiveTestClass CreatePrimitiveTestData() { return new PrimitiveTestClass { IntValue = int.MaxValue, LongValue = long.MaxValue, DoubleValue = 3.14159265358979, DecimalValue = 12345.6789m, FloatValue = 1.5f, BoolValue = true, StringValue = "Teszt Szöveg árvíztűrőtükörfúrógép", GuidValue = Guid.Parse("12345678-1234-1234-1234-123456789abc"), DateTimeValue = new DateTime(2024, 12, 25, 12, 30, 45, DateTimeKind.Utc), EnumValue = TestStatus.Shipped, ByteValue = 255, ShortValue = short.MaxValue, NullableInt = 42, NullableIntNull = null }; } } /// /// _All_False family factory — benchmark Phase 1 target. The generic-base factory methods /// produce _All_False-typed graphs via the closed-generic new T() calls. No /// family-specific extras here (the legacy etc. /// stay on the _All_True alias because their existing consumers are _All_True-tied). /// public sealed class TestDataFactory_All_False : TestDataFactory< TestOrder_All_False, TestOrderItem_All_False, TestPallet_All_False, TestMeasurement_All_False, TestMeasurementPoint_All_False, SharedTag_All_False, SharedUser_All_False, SharedCategory_All_False, MetadataInfo_All_False, UserPreferences_All_False> { }