From 070d915b12d93c1115139bacb6b91182a185a30d Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 6 Feb 2026 15:26:16 -0500 Subject: [PATCH] Add debug Dapper test script --- .../JdeScoping.Database.Tests/debug_test.csx | 72 +++ TestScripts/playwright/debug-screenshot.png | Bin 0 -> 8541 bytes .../playwright/playwright-report/index.html | 85 ++++ .../playwright/test-results/.last-run.json | 4 + aot_prep.md | 413 ++++++++++++++++++ test-results/.last-run.json | 4 + 6 files changed, 578 insertions(+) create mode 100644 NEW/tests/JdeScoping.Database.Tests/debug_test.csx create mode 100644 TestScripts/playwright/debug-screenshot.png create mode 100644 TestScripts/playwright/playwright-report/index.html create mode 100644 TestScripts/playwright/test-results/.last-run.json create mode 100644 aot_prep.md create mode 100644 test-results/.last-run.json diff --git a/NEW/tests/JdeScoping.Database.Tests/debug_test.csx b/NEW/tests/JdeScoping.Database.Tests/debug_test.csx new file mode 100644 index 0000000..0f1b9ac --- /dev/null +++ b/NEW/tests/JdeScoping.Database.Tests/debug_test.csx @@ -0,0 +1,72 @@ +#r "nuget: Dapper, 2.1.35" +#r "nuget: Microsoft.Data.SqlClient, 5.2.2" + +using System; +using System.Threading.Tasks; +using Dapper; +using Microsoft.Data.SqlClient; + +var connectionString = "Server=localhost,1434;Database=ScopingTool;User Id=sa;Password=ScopingTool_SA_2024Dev;TrustServerCertificate=true"; +var testMisNumber = "TEST_DAPPER_001"; + +using var connection = new SqlConnection(connectionString); +await connection.OpenAsync(); + +// Cleanup +await connection.ExecuteAsync("DELETE FROM MisData_Curr WHERE MisNumber = @MisNumber", new { MisNumber = testMisNumber }); +await connection.ExecuteAsync("DELETE FROM MisData_Hist WHERE MisNumber = @MisNumber", new { MisNumber = testMisNumber }); +await connection.ExecuteAsync("DELETE FROM mis_temp WHERE MIS_IIS_Number = @MisNumber OR MIS_IIS_Number = @MisNumberWithPrefix", + new { MisNumber = testMisNumber, MisNumberWithPrefix = $"IIS_{testMisNumber}" }); + +Console.WriteLine("Cleanup done"); + +// Insert test data +await connection.ExecuteAsync( + @"INSERT INTO mis_temp (MIS_IIS_Number, PartNumber, Site, Version, CharacterNumber, + TestDescription, SamplingType, SamplingValue, ToolsGauges, WorkInstructions, Release_Date) + VALUES (@MisNumber, '12345', 'SITE1', 'A', '1', + 'Test Description', 'Random', '5', 'Gauge1', 'Instruction1', @ReleaseDate)", + new { MisNumber = testMisNumber, ReleaseDate = DateTime.Now }); + +Console.WriteLine("Inserted staging data"); + +// Check staging data +var stagingCount = await connection.QuerySingleAsync( + "SELECT COUNT(*) FROM mis_temp WHERE MIS_IIS_Number = @MisNumber", + new { MisNumber = testMisNumber }); +Console.WriteLine($"Staging count before proc: {stagingCount}"); + +// Run procedure +Console.WriteLine("Running stored procedure..."); +await connection.ExecuteAsync("EXEC dbo.usp_ProcessMisStagingData @SaveChanges = 1"); +Console.WriteLine("Stored procedure completed"); + +// Check result +var record = await connection.QuerySingleOrDefaultAsync( + @"SELECT MisNumber, ItemNumber, BranchCode, RevID, CharNumber, Status, ObsoleteDate + FROM MisData_Curr + WHERE MisNumber = @MisNumber", + new { MisNumber = testMisNumber }); + +if (record == null) +{ + Console.WriteLine("FAIL: Record is null!"); + + // Additional debugging - check if it got deleted during the proc + var stagingCountAfter = await connection.QuerySingleAsync( + "SELECT COUNT(*) FROM mis_temp WHERE MIS_IIS_Number = @MisNumber", + new { MisNumber = testMisNumber }); + Console.WriteLine($"Staging count after proc: {stagingCountAfter}"); +} +else +{ + Console.WriteLine($"SUCCESS: Found record - Status={record.Status}"); +} + +// Cleanup +await connection.ExecuteAsync("DELETE FROM MisData_Curr WHERE MisNumber = @MisNumber", new { MisNumber = testMisNumber }); +await connection.ExecuteAsync("DELETE FROM MisData_Hist WHERE MisNumber = @MisNumber", new { MisNumber = testMisNumber }); +await connection.ExecuteAsync("DELETE FROM mis_temp WHERE MIS_IIS_Number = @MisNumber OR MIS_IIS_Number = @MisNumberWithPrefix", + new { MisNumber = testMisNumber, MisNumberWithPrefix = $"IIS_{testMisNumber}" }); + +Console.WriteLine("Test complete"); diff --git a/TestScripts/playwright/debug-screenshot.png b/TestScripts/playwright/debug-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..a5df24e6cf316a8a9a44adfe4b86458f16ced7a3 GIT binary patch literal 8541 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 literal 0 HcmV?d00001 diff --git a/TestScripts/playwright/playwright-report/index.html b/TestScripts/playwright/playwright-report/index.html new file mode 100644 index 0000000..fb857fd --- /dev/null +++ b/TestScripts/playwright/playwright-report/index.html @@ -0,0 +1,85 @@ + + + + + + + + + Playwright Test Report + + + + +
+ + + \ No newline at end of file diff --git a/TestScripts/playwright/test-results/.last-run.json b/TestScripts/playwright/test-results/.last-run.json new file mode 100644 index 0000000..cbcc1fb --- /dev/null +++ b/TestScripts/playwright/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "passed", + "failedTests": [] +} \ No newline at end of file diff --git a/aot_prep.md b/aot_prep.md new file mode 100644 index 0000000..e4d94ce --- /dev/null +++ b/aot_prep.md @@ -0,0 +1,413 @@ +# AOT Readiness Implementation Plan + +## Overview + +Make the JdeScopingTool .NET 10 solution ready for Native AOT compilation by addressing three critical areas: +1. **JSON Source Generation** - Replace reflection-based System.Text.Json with source generators +2. **Dapper AOT** - Enable compile-time SQL mapping generation +3. **Configuration Binding** - Replace reflection-based options binding and validation + +**Current AOT Score:** ~25/100 (1 of 14 projects ready) +**Target AOT Score:** 90/100 (all projects AOT-compatible with known library exceptions) + +--- + +## Current State Summary + +### Projects by AOT Readiness + +| Status | Project | Key Blockers | +|--------|---------|--------------| +| ✅ Ready | `JdeScoping.Domain` | None - pure domain models | +| ❌ Needs Work | `JdeScoping.DataAccess` | Dapper, SqlKata, Oracle driver | +| ❌ Needs Work | `JdeScoping.DataSync` | Dapper + STJ without source gen | +| ❌ Needs Work | `JdeScoping.Core` | STJ + Configuration.Binder | +| ❌ Needs Work | `JdeScoping.Infrastructure` | STJ + SecureStore + LDAP | +| ❌ Needs Work | `JdeScoping.Api` | STJ + Swashbuckle + config binding | +| ❌ Needs Work | `JdeScoping.Host` | Depends on API/DataSync | +| ❌ Needs Work | `JdeScoping.Client` | STJ + Radzen + SignalR | +| ❌ Needs Work | `JdeScoping.ExcelIO` | ClosedXML | +| ❌ Needs Work | `JdeScoping.Database` | dbup-sqlserver | +| ❌ Needs Work | ConfigManager.* | STJ + System.CommandLine + Avalonia | + +### No Existing AOT Configuration + +- No `PublishAot` property +- No `IsAotCompatible` markers +- No `TrimMode` settings +- No `[DynamicallyAccessedMembers]` attributes +- No `[RequiresUnreferencedCode]` attributes + +--- + +## Phase 1: Solution-Wide AOT Configuration + +### 1.1 Create Directory.Build.props +**File:** `NEW/src/Directory.Build.props` (new) + +```xml + + + true + true + true + true + true + false + + +``` + +### 1.2 Update Host Project for AOT Publishing +**File:** `NEW/src/JdeScoping.Host/JdeScoping.Host.csproj` + +Add: +```xml + + false + true + partial + +``` + +--- + +## Phase 2: JSON Source Generation (~45 types) + +### 2.1 Create JsonSerializerContext Classes + +| Project | Context File | Types Count | +|---------|-------------|-------------| +| JdeScoping.Core | `Json/CoreJsonContext.cs` | ~30 (ViewModels, Auth, Search) | +| JdeScoping.DataSync | `Json/DataSyncJsonContext.cs` | ~6 (ETL pipeline configs) | +| JdeScoping.Api | `Json/ApiJsonContext.cs` | ~10 (FileUploadResult, HealthCheck) | +| JdeScoping.Client | `Json/ClientJsonContext.cs` | ~15 (Client-side types) | +| ConfigManager.Core | `Json/ConfigManagerJsonContext.cs` | ~13 (ConfigModel sections) | + +### 2.2 Types Requiring [JsonSerializable] Attributes + +#### Core Authentication & Identity Types +- `UserInfoDto` - `JdeScoping.Core/Models/Auth/UserInfoDto.cs` +- `LoginResultModel` - `JdeScoping.Core/Models/Auth/LoginResultModel.cs` +- `LoginModel` - `JdeScoping.Core/Models/Auth/LoginModel.cs` + +#### Search & Criteria Types +- `SearchCriteria` - `JdeScoping.Core/Models/Search/SearchCriteria.cs` +- `Search` - `JdeScoping.Core/Models/Search/Search.cs` +- `SearchViewModel` - `JdeScoping.Core/ViewModels/SearchViewModel.cs` +- `SearchUpdateViewModel` - `JdeScoping.Core/ViewModels/SearchUpdateViewModel.cs` +- `StatusUpdateViewModel` - `JdeScoping.Core/ViewModels/StatusUpdateViewModel.cs` + +#### File Upload & Result Types +- `FileUploadResult` - `JdeScoping.Client/Models/FileUploadResult.cs` +- `FileUploadResult` - `JdeScoping.Api/Models/FileUploadResult.cs` + +#### Data Sync & Manual Sync Types +- `ManualSyncRequestViewModel` - `JdeScoping.Api/Contracts/ManualSync/ManualSyncRequestViewModel.cs` +- `CreateManualSyncRequestDto` - `JdeScoping.Core/ViewModels/CreateManualSyncRequestDto.cs` +- `PipelineInfoViewModel` - `JdeScoping.Api/Contracts/ManualSync/PipelineInfoViewModel.cs` + +#### ETL Pipeline Configuration Types +- `EtlPipelineConfig` - `JdeScoping.DataSync/Configuration/EtlPipelineConfig.cs` +- `SourceElement` - `JdeScoping.DataSync/Configuration/SourceElement.cs` +- `DestinationElement` - `JdeScoping.DataSync/Configuration/DestinationElement.cs` +- `TransformElement` - `JdeScoping.DataSync/Configuration/TransformElement.cs` +- `ScriptElement` - `JdeScoping.DataSync/Configuration/ScriptElement.cs` +- `ParameterElement` - `JdeScoping.DataSync/Configuration/ParameterElement.cs` + +#### Configuration Model Types +- `ConfigModel` - `Utils/JdeScoping.ConfigManager.Core/Models/ConfigModel.cs` +- `ConnectionStringsSection` - `Utils/JdeScoping.ConfigManager.Core/Models/ConnectionStringsSection.cs` +- `ConnectionStringEntry` - `Utils/JdeScoping.ConfigManager.Core/Models/ConnectionStringEntry.cs` + +#### Data ViewModels +- `WorkOrderViewModel`, `ItemViewModel`, `WorkCenterViewModel`, `ProfitCenterViewModel` +- `PartOperationViewModel`, `JdeUserViewModel`, `LotViewModel`, `ComponentLotViewModel`, `OperatorViewModel` + +### 2.3 Key Files to Create + +**`NEW/src/JdeScoping.Core/Json/CoreJsonContext.cs`** - Central context with: +- All ViewModels (SearchViewModel, WorkOrderViewModel, ItemViewModel, etc.) +- Auth models (UserInfoDto, LoginResultModel, LoginModel) +- SearchCriteria, ApiError, ValidationError + +**`NEW/src/JdeScoping.DataSync/Json/DataSyncJsonContext.cs`**: +- EtlPipelineConfig, SourceElement, DestinationElement, TransformElement + +**`NEW/src/JdeScoping.Api/Json/ApiJsonContext.cs`**: +- FileUploadResult instantiations for each ViewModel type +- HealthCheckResponse (replace anonymous type) + +### 2.4 Registration Updates + +**`NEW/src/JdeScoping.Api/DependencyInjection.cs`** (line ~80): +```csharp +.AddJsonOptions(options => +{ + options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, CoreJsonContext.Default); + options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, ApiJsonContext.Default); +}); +``` + +**`NEW/src/JdeScoping.Client/Services/ApiClientBase.cs`** (line ~16): +```csharp +private static readonly JsonSerializerOptions JsonOptions = new() +{ + TypeInfoResolver = JsonTypeInfoResolver.Combine( + ClientJsonContext.Default, + CoreJsonContext.Default) +}; +``` + +### 2.5 Fix Anonymous Type in Health Check +**`NEW/src/JdeScoping.Api/DependencyInjection.cs`** - Replace anonymous object with typed `HealthCheckResponse` class. + +--- + +## Phase 3: Dapper AOT (15+ model types) + +### 3.1 Dapper Usage Locations + +| File | Dapper Methods | Model Types | +|------|---------------|-------------| +| `LotFinderRepository.SearchManagement.cs` | QueryAsync, QueryFirstOrDefaultAsync | Search | +| `LotFinderRepository.Lookups.cs` | QueryAsync | Item, Lot, WorkOrder, WorkCenter, ProfitCenter, JdeUser | +| `LotFinderRepository.DataSync.cs` | QueryAsync | DataUpdate | +| `SearchProcessor.cs` | QueryAsync, QuerySingleOrDefaultAsync | SearchResult, MisSearchResult, MisNonMatchSearchResult | +| `ManualSyncRequestService.cs` | QueryAsync, QuerySingleAsync, ExecuteAsync | ManualSyncRequest | +| `WorkOrderTraversalService.cs` | QuerySingleAsync | (int, int) tuple | +| `DataUpdateRepository.cs` | QueryAsync, ExecuteScalarAsync, ExecuteAsync | DataUpdate, (string, int, DateTime?, int) tuple | +| `SearchRepository.cs` | QueryFirstOrDefaultAsync, ExecuteAsync | Search | + +### 3.2 Add Dapper.AOT Package +**Files:** `JdeScoping.DataAccess.csproj`, `JdeScoping.DataSync.csproj` + +```xml + + + $(InterceptorsPreviewNamespaces);Dapper.AOT + +``` + +### 3.3 Enable AOT for Assemblies +**`NEW/src/JdeScoping.DataAccess/DapperAotConfiguration.cs`** (new): +```csharp +[assembly: DapperAot] +``` + +### 3.4 Fix Private Field Mapping Pattern +Change 6 models from private to internal for Dapper.AOT compatibility: + +| Model | File | +|-------|------| +| Item | `Core/Models/Inventory/Item.cs` | +| Lot | `Core/Models/Inventory/Lot.cs` | +| WorkOrder | `Core/Models/WorkOrders/WorkOrder.cs` | +| WorkCenter | `Core/Models/Organization/WorkCenter.cs` | +| ProfitCenter | `Core/Models/Organization/ProfitCenter.cs` | +| JdeUser | `Core/Models/Organization/JdeUser.cs` | + +Change: +```csharp +private int LastUpdateDate { get; set; } // Before +[Column("LastUpdateDate")] +internal int LastUpdateDate { get; set; } // After +``` + +### 3.5 Replace Tuple Queries with Named Types +Create explicit result types: + +**`NEW/src/JdeScoping.DataAccess/Models/TraversalResult.cs`** (new): +```csharp +public sealed class TraversalResult +{ + public int IterationsCompleted { get; init; } + public int TotalWorkOrders { get; init; } +} +``` + +**`NEW/src/JdeScoping.DataSync/Models/TableSyncStatusRow.cs`** (new): +```csharp +public sealed class TableSyncStatusRow +{ + public string TableName { get; init; } = string.Empty; + public int UpdateType { get; init; } + public DateTime? LastSuccessfulSync { get; init; } + public int RecentFailures { get; init; } +} +``` + +--- + +## Phase 4: Configuration Binding + +### 4.1 Options Classes Inventory + +| Project | Options Class | Has DataAnnotations | Uses ValidateDataAnnotations | +|---------|--------------|---------------------|------------------------------| +| JdeScoping.Core | SecureStoreOptions | No | No | +| JdeScoping.Core | RsaKeyOptions | No | No | +| JdeScoping.Infrastructure | LdapOptions | Yes (1 Range) | No | +| JdeScoping.Api | AuthOptions | No | No | +| JdeScoping.DataAccess | DataAccessOptions | No | No | +| JdeScoping.DataAccess | SearchProcessingOptions | No | No | +| JdeScoping.DataAccess | SearchProcessingConfiguration | No | No | +| JdeScoping.DataSync | DataSyncOptions | Yes (6 Range) | **Yes** | +| JdeScoping.DataSync | WorkProcessorOptions | Yes (3 Range) | **Yes** | +| JdeScoping.ExcelIO | ExcelExportOptions | No | No | +| JdeScoping.DataSync.Dev | DevPipelineOptions | No | No | + +### 4.2 Create AOT-Compatible Validators +Replace `ValidateDataAnnotations()` with `IValidateOptions`: + +**`NEW/src/JdeScoping.DataSync/Validation/DataSyncOptionsValidator.cs`** (new) +**`NEW/src/JdeScoping.DataSync/Validation/WorkProcessorOptionsValidator.cs`** (new) + +### 4.3 Update DependencyInjection Files + +**`NEW/src/JdeScoping.DataSync/DependencyInjection.cs`** (lines 27-36): +```csharp +// Before +.ValidateDataAnnotations() + +// After +services.AddSingleton, DataSyncOptionsValidator>(); +``` + +**`NEW/src/JdeScoping.Infrastructure/DependencyInjection.cs`** (lines 33-35): +Replace `GetSection().Get()` with `GetSection().GetValue()`. + +**`NEW/src/JdeScoping.Api/DependencyInjection.cs`** (lines 33-35): +Replace `GetSection().Get()` with explicit property binding. + +### 4.4 Remove Unused Package +**`NEW/src/JdeScoping.DataSync/JdeScoping.DataSync.csproj`**: +Remove `Microsoft.Extensions.Options.DataAnnotations` reference. + +--- + +## Phase 5: Third-Party Library Handling + +### 5.1 Package Risk Assessment + +| Risk Level | Packages | +|------------|----------| +| 🔴 High | Dapper (mitigated by Dapper.AOT), SqlKata, Swashbuckle, ClosedXML, dbup-sqlserver | +| 🟠 Medium | protobuf-net-data, System.CommandLine, SecureStore | +| 🟡 Low | Microsoft.Data.SqlClient, Oracle driver, LDAP, Serilog | +| 🟢 Safe | Cronos, OneOf (has source generator), ZstdSharp | + +### 5.2 Create Trimmer Roots +**`NEW/src/JdeScoping.Host/TrimmerRoots.xml`** (new): + +```xml + + + + + + + + + + + + + + +``` + +### 5.3 Known Limitations +These packages have partial/no AOT support - preserved via trimmer roots: +- ClosedXML (Excel generation) +- Oracle.ManagedDataAccess.Core +- dbup-sqlserver (migrations - dev only) +- Swashbuckle (Swagger - dev only) + +--- + +## Implementation Sequence + +| Step | Task | Files | +|------|------|-------| +| 1 | Create Directory.Build.props | 1 new | +| 2 | Create CoreJsonContext | 1 new | +| 3 | Create DataSyncJsonContext | 1 new | +| 4 | Create ApiJsonContext + HealthCheckResponse | 1 new | +| 5 | Create ClientJsonContext | 1 new | +| 6 | Create ConfigManagerJsonContext | 1 new | +| 7 | Update Api DependencyInjection (JSON registration) | 1 edit | +| 8 | Update ApiClientBase (JSON options) | 1 edit | +| 9 | Update PipelineRegistry (JSON options) | 1 edit | +| 10 | Add Dapper.AOT packages | 2 edits | +| 11 | Create DapperAotConfiguration | 1 new | +| 12 | Update 6 models (private→internal) | 6 edits | +| 13 | Create TraversalResult, TableSyncStatusRow | 2 new | +| 14 | Update tuple queries to use named types | 2 edits | +| 15 | Create DataSyncOptionsValidator | 1 new | +| 16 | Create WorkProcessorOptionsValidator | 1 new | +| 17 | Update DataSync DependencyInjection | 1 edit | +| 18 | Update Infrastructure DependencyInjection | 1 edit | +| 19 | Update Api DependencyInjection (config) | 1 edit | +| 20 | Update Host csproj (AOT settings) | 1 edit | +| 21 | Create TrimmerRoots.xml | 1 new | +| 22 | Remove DataAnnotations package | 1 edit | + +**Total: 14 new files, 18 edits** + +--- + +## Verification + +### Build Verification +```bash +dotnet build NEW/JdeScoping.slnx +``` +Should complete with no new errors. + +### AOT Analysis +```bash +dotnet publish NEW/src/JdeScoping.Host -c Release -p:PublishAot=true --no-build +``` +Check for AOT compatibility warnings. + +### Trim Analysis +```bash +dotnet publish NEW/src/JdeScoping.Host -c Release -p:PublishTrimmed=true +``` +Review trim warnings and verify TrimmerRoots coverage. + +### Test Suite +```bash +dotnet test NEW/JdeScoping.slnx +``` +All existing tests should pass. + +### E2E Verification +Run Playwright tests to verify JSON serialization works end-to-end. + +--- + +## Critical Files Summary + +**New Files (14):** +- `NEW/src/Directory.Build.props` +- `NEW/src/JdeScoping.Core/Json/CoreJsonContext.cs` +- `NEW/src/JdeScoping.DataSync/Json/DataSyncJsonContext.cs` +- `NEW/src/JdeScoping.Api/Json/ApiJsonContext.cs` +- `NEW/src/JdeScoping.Client/Json/ClientJsonContext.cs` +- `NEW/src/Utils/JdeScoping.ConfigManager.Core/Json/ConfigManagerJsonContext.cs` +- `NEW/src/JdeScoping.DataAccess/DapperAotConfiguration.cs` +- `NEW/src/JdeScoping.DataAccess/Models/TraversalResult.cs` +- `NEW/src/JdeScoping.DataSync/Models/TableSyncStatusRow.cs` +- `NEW/src/JdeScoping.DataSync/Validation/DataSyncOptionsValidator.cs` +- `NEW/src/JdeScoping.DataSync/Validation/WorkProcessorOptionsValidator.cs` +- `NEW/src/JdeScoping.Host/TrimmerRoots.xml` + +**Key Edits:** +- `NEW/src/JdeScoping.Api/DependencyInjection.cs` - JSON + config +- `NEW/src/JdeScoping.Client/Services/ApiClientBase.cs` - JSON options +- `NEW/src/JdeScoping.DataSync/DependencyInjection.cs` - Validators +- `NEW/src/JdeScoping.Infrastructure/DependencyInjection.cs` - Config binding +- `NEW/src/JdeScoping.Host/JdeScoping.Host.csproj` - AOT settings +- 6 model files for private→internal field change diff --git a/test-results/.last-run.json b/test-results/.last-run.json new file mode 100644 index 0000000..5fca3f8 --- /dev/null +++ b/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "failed", + "failedTests": [] +} \ No newline at end of file