diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..64d38d8c41 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,141 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Shesha is an open-source Low-Code development framework for .NET developers. It combines ASP.NET Core, ABP.io, React, and Next.js with drag-and-drop form building capabilities to reduce code requirements by 80%+ for typical business applications. + +## Repository Structure + +``` +shesha-framework/ +├── shesha-core/ # .NET backend framework (main solution) +├── shesha-reactjs/ # React/TypeScript component library (@shesha-io/reactjs) +├── shesha-starter/ # Starter template for new projects +├── shesha-functional-tests/ # Functional test suite +``` + +## Build Commands + +### Backend (.NET 8.0) + +```bash +# Build the framework +dotnet build shesha-core/Shesha.sln --configuration Release + +# Run backend tests (requires Docker for Testcontainers) +cd shesha-core/test/Shesha.Tests +dotnet test Shesha.Tests.csproj --configuration Release +``` + +### Frontend (Node.js 20+, npm 10+) + +```bash +# Install dependencies +cd shesha-reactjs +npm install + +# Build the library (includes lint check) +npm run build + +# Run tests +npm test + +# Run single test file +npm test -- --testPathPattern="path/to/test" + +# Watch mode for development +npm run start + +# Lint +npm run lint +npm run lint-fix + +# Type check (strict null checks) +npm run type-check +``` + +### Starter Application + +```bash +# Frontend development +cd shesha-starter/frontend +npm install +npm run dev + +# Production build +npm run build +npm run start +``` + +## Architecture + +### Backend Layered Architecture + +1. **Domain Layer** (`Shesha.Core`) - Domain entities, interfaces, business logic +2. **Framework Layer** (`Shesha.Framework`) - Dynamic CRUD, metadata, configuration studio, JSON logic engine +3. **Application Layer** (`Shesha.Application`) - Application services, DTOs, CRUD app services +4. **Web Layer** (`Shesha.Web.Host`, `Shesha.Web.Core`) - API controllers, authorization, Swagger + +### ABP Module System + +Modules inherit from `SheshaModule` with lifecycle hooks: +- `PreInitialize()` - Register services +- `Initialize()` - Configure services +- `InitializeConfigurationAsync()` - Async initialization + +Key modules: `Shesha.NHibernate` (ORM), `Shesha.GraphQL`, `Shesha.Scheduler` (HangFire), `Shesha.FluentMigrator` + +### Frontend Architecture + +**Provider Pattern** - Context providers for state management: +- `SheshaApplicationProvider` - App-level configuration +- `FormDesignerProvider` - Form builder context +- `DataContextProvider` - Data management (appContext, pageContext, formContext) + +**Key Directories** (`shesha-reactjs/src/`): +- `components/` - 40+ UI components (dataTable, autocomplete, modal, etc.) +- `designer-components/` - Form builder components +- `providers/` - Context providers +- `hooks/` - Custom React hooks (useGet, useMutate, useFormExpression, etc.) +- `apis/` - API integration layer + +### Dynamic CRUD Pattern + +Backend automatically generates CRUD APIs from domain entities via `DynamicCrudAppService`. Enable through entity configuration - no boilerplate code needed. + +### DataContext (v0.44+) + +Replaces GlobalState to prevent unnecessary re-renders: +- `appContext` - App-wide data +- `pageContext` - Page-scoped data +- `formContext` - Form-scoped data + +## Testing + +### Backend Tests +- Framework: xUnit with Testcontainers +- **Requires Docker daemon running** +- Tests run against SQL Server/PostgreSQL in containers + +### Frontend Tests +- Framework: Jest with React Testing Library +- Config: `jest.config.js` +- Test files: `*.test.ts` or `*.spec.tsx` in `src/` + +## Key Patterns + +### Form Configuration +- Default values set via `onAfterDataLoad` scripts (not DefaultValue property) +- Scripts execute in dependency order +- Direct write access to `data` and `context` without SetFieldValue + +### Commits +Use conventional commits via Commitizen: `npm run commit` + +## Database Support + +- Primary: SQL Server, PostgreSQL (via NHibernate) +- Additional: MongoDB (Shesha.MongoRepository), PostGIS for geographic data +- Migrations: FluentMigrator diff --git a/shesha-core/Shesha.sln b/shesha-core/Shesha.sln index 141b129d9f..35980e6485 100644 --- a/shesha-core/Shesha.sln +++ b/shesha-core/Shesha.sln @@ -64,108 +64,318 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shesha.Tests.ModuleA", "tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shesha.Tests.ModuleB", "test\Shesha.Tests.ModuleB\Shesha.Tests.ModuleB.csproj", "{D140AFF0-4485-6214-3EB7-793EC812B972}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shesha.Testing", "src\Shesha.Testing\Shesha.Testing.csproj", "{E43DF8E7-E385-4791-AD9D-B8A968B4D020}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Debug|x64.ActiveCfg = Debug|Any CPU + {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Debug|x64.Build.0 = Debug|Any CPU + {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Debug|x86.ActiveCfg = Debug|Any CPU + {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Debug|x86.Build.0 = Debug|Any CPU {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Release|Any CPU.ActiveCfg = Release|Any CPU {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Release|Any CPU.Build.0 = Release|Any CPU + {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Release|x64.ActiveCfg = Release|Any CPU + {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Release|x64.Build.0 = Release|Any CPU + {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Release|x86.ActiveCfg = Release|Any CPU + {0FA75A5B-AB83-4FD0-B545-279774C01E87}.Release|x86.Build.0 = Release|Any CPU {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Debug|x64.ActiveCfg = Debug|Any CPU + {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Debug|x64.Build.0 = Debug|Any CPU + {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Debug|x86.ActiveCfg = Debug|Any CPU + {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Debug|x86.Build.0 = Debug|Any CPU {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Release|Any CPU.ActiveCfg = Release|Any CPU {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Release|Any CPU.Build.0 = Release|Any CPU + {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Release|x64.ActiveCfg = Release|Any CPU + {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Release|x64.Build.0 = Release|Any CPU + {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Release|x86.ActiveCfg = Release|Any CPU + {3870C648-4AEA-4B85-BA3F-F2F63B96136A}.Release|x86.Build.0 = Release|Any CPU {0D4C5D00-C144-4213-A007-4B8944113AB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0D4C5D00-C144-4213-A007-4B8944113AB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D4C5D00-C144-4213-A007-4B8944113AB1}.Debug|x64.ActiveCfg = Debug|Any CPU + {0D4C5D00-C144-4213-A007-4B8944113AB1}.Debug|x64.Build.0 = Debug|Any CPU + {0D4C5D00-C144-4213-A007-4B8944113AB1}.Debug|x86.ActiveCfg = Debug|Any CPU + {0D4C5D00-C144-4213-A007-4B8944113AB1}.Debug|x86.Build.0 = Debug|Any CPU {0D4C5D00-C144-4213-A007-4B8944113AB1}.Release|Any CPU.ActiveCfg = Release|Any CPU {0D4C5D00-C144-4213-A007-4B8944113AB1}.Release|Any CPU.Build.0 = Release|Any CPU + {0D4C5D00-C144-4213-A007-4B8944113AB1}.Release|x64.ActiveCfg = Release|Any CPU + {0D4C5D00-C144-4213-A007-4B8944113AB1}.Release|x64.Build.0 = Release|Any CPU + {0D4C5D00-C144-4213-A007-4B8944113AB1}.Release|x86.ActiveCfg = Release|Any CPU + {0D4C5D00-C144-4213-A007-4B8944113AB1}.Release|x86.Build.0 = Release|Any CPU {38E184BD-E874-4633-A947-AED4FDB73F40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {38E184BD-E874-4633-A947-AED4FDB73F40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38E184BD-E874-4633-A947-AED4FDB73F40}.Debug|x64.ActiveCfg = Debug|Any CPU + {38E184BD-E874-4633-A947-AED4FDB73F40}.Debug|x64.Build.0 = Debug|Any CPU + {38E184BD-E874-4633-A947-AED4FDB73F40}.Debug|x86.ActiveCfg = Debug|Any CPU + {38E184BD-E874-4633-A947-AED4FDB73F40}.Debug|x86.Build.0 = Debug|Any CPU {38E184BD-E874-4633-A947-AED4FDB73F40}.Release|Any CPU.ActiveCfg = Release|Any CPU {38E184BD-E874-4633-A947-AED4FDB73F40}.Release|Any CPU.Build.0 = Release|Any CPU + {38E184BD-E874-4633-A947-AED4FDB73F40}.Release|x64.ActiveCfg = Release|Any CPU + {38E184BD-E874-4633-A947-AED4FDB73F40}.Release|x64.Build.0 = Release|Any CPU + {38E184BD-E874-4633-A947-AED4FDB73F40}.Release|x86.ActiveCfg = Release|Any CPU + {38E184BD-E874-4633-A947-AED4FDB73F40}.Release|x86.Build.0 = Release|Any CPU {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Debug|x64.ActiveCfg = Debug|Any CPU + {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Debug|x64.Build.0 = Debug|Any CPU + {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Debug|x86.ActiveCfg = Debug|Any CPU + {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Debug|x86.Build.0 = Debug|Any CPU {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Release|Any CPU.ActiveCfg = Release|Any CPU {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Release|Any CPU.Build.0 = Release|Any CPU + {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Release|x64.ActiveCfg = Release|Any CPU + {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Release|x64.Build.0 = Release|Any CPU + {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Release|x86.ActiveCfg = Release|Any CPU + {22CFE0D2-8DCA-42D7-AD7D-784C3862493F}.Release|x86.Build.0 = Release|Any CPU {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Debug|x64.ActiveCfg = Debug|Any CPU + {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Debug|x64.Build.0 = Debug|Any CPU + {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Debug|x86.ActiveCfg = Debug|Any CPU + {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Debug|x86.Build.0 = Debug|Any CPU {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Release|Any CPU.ActiveCfg = Release|Any CPU {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Release|Any CPU.Build.0 = Release|Any CPU + {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Release|x64.ActiveCfg = Release|Any CPU + {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Release|x64.Build.0 = Release|Any CPU + {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Release|x86.ActiveCfg = Release|Any CPU + {684B7DB0-1CE1-4FEE-870A-E7B2A0B1F15E}.Release|x86.Build.0 = Release|Any CPU {9088F6F3-47EB-4C10-813D-EA6161796380}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9088F6F3-47EB-4C10-813D-EA6161796380}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9088F6F3-47EB-4C10-813D-EA6161796380}.Debug|x64.ActiveCfg = Debug|Any CPU + {9088F6F3-47EB-4C10-813D-EA6161796380}.Debug|x64.Build.0 = Debug|Any CPU + {9088F6F3-47EB-4C10-813D-EA6161796380}.Debug|x86.ActiveCfg = Debug|Any CPU + {9088F6F3-47EB-4C10-813D-EA6161796380}.Debug|x86.Build.0 = Debug|Any CPU {9088F6F3-47EB-4C10-813D-EA6161796380}.Release|Any CPU.ActiveCfg = Release|Any CPU {9088F6F3-47EB-4C10-813D-EA6161796380}.Release|Any CPU.Build.0 = Release|Any CPU + {9088F6F3-47EB-4C10-813D-EA6161796380}.Release|x64.ActiveCfg = Release|Any CPU + {9088F6F3-47EB-4C10-813D-EA6161796380}.Release|x64.Build.0 = Release|Any CPU + {9088F6F3-47EB-4C10-813D-EA6161796380}.Release|x86.ActiveCfg = Release|Any CPU + {9088F6F3-47EB-4C10-813D-EA6161796380}.Release|x86.Build.0 = Release|Any CPU {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Debug|x64.ActiveCfg = Debug|Any CPU + {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Debug|x64.Build.0 = Debug|Any CPU + {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Debug|x86.ActiveCfg = Debug|Any CPU + {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Debug|x86.Build.0 = Debug|Any CPU {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Release|Any CPU.Build.0 = Release|Any CPU + {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Release|x64.ActiveCfg = Release|Any CPU + {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Release|x64.Build.0 = Release|Any CPU + {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Release|x86.ActiveCfg = Release|Any CPU + {2B80EFED-CB47-4EDC-976E-F764C63C0F6C}.Release|x86.Build.0 = Release|Any CPU {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Debug|x64.Build.0 = Debug|Any CPU + {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Debug|x86.Build.0 = Debug|Any CPU {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Release|Any CPU.Build.0 = Release|Any CPU + {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Release|x64.ActiveCfg = Release|Any CPU + {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Release|x64.Build.0 = Release|Any CPU + {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Release|x86.ActiveCfg = Release|Any CPU + {18CA4C5D-DFA1-420B-A379-50D7A74A71BA}.Release|x86.Build.0 = Release|Any CPU {CC013418-4BDE-4D1D-93C9-7065C865838C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CC013418-4BDE-4D1D-93C9-7065C865838C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC013418-4BDE-4D1D-93C9-7065C865838C}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC013418-4BDE-4D1D-93C9-7065C865838C}.Debug|x64.Build.0 = Debug|Any CPU + {CC013418-4BDE-4D1D-93C9-7065C865838C}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC013418-4BDE-4D1D-93C9-7065C865838C}.Debug|x86.Build.0 = Debug|Any CPU {CC013418-4BDE-4D1D-93C9-7065C865838C}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC013418-4BDE-4D1D-93C9-7065C865838C}.Release|Any CPU.Build.0 = Release|Any CPU + {CC013418-4BDE-4D1D-93C9-7065C865838C}.Release|x64.ActiveCfg = Release|Any CPU + {CC013418-4BDE-4D1D-93C9-7065C865838C}.Release|x64.Build.0 = Release|Any CPU + {CC013418-4BDE-4D1D-93C9-7065C865838C}.Release|x86.ActiveCfg = Release|Any CPU + {CC013418-4BDE-4D1D-93C9-7065C865838C}.Release|x86.Build.0 = Release|Any CPU {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Debug|Any CPU.Build.0 = Debug|Any CPU + {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Debug|x64.ActiveCfg = Debug|Any CPU + {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Debug|x64.Build.0 = Debug|Any CPU + {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Debug|x86.ActiveCfg = Debug|Any CPU + {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Debug|x86.Build.0 = Debug|Any CPU {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Release|Any CPU.ActiveCfg = Release|Any CPU {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Release|Any CPU.Build.0 = Release|Any CPU + {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Release|x64.ActiveCfg = Release|Any CPU + {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Release|x64.Build.0 = Release|Any CPU + {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Release|x86.ActiveCfg = Release|Any CPU + {835F8022-4918-4BD9-83EB-A4C6EACBF942}.Release|x86.Build.0 = Release|Any CPU {26E40397-1D7F-4C4C-846C-3F3B46408054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {26E40397-1D7F-4C4C-846C-3F3B46408054}.Debug|Any CPU.Build.0 = Debug|Any CPU + {26E40397-1D7F-4C4C-846C-3F3B46408054}.Debug|x64.ActiveCfg = Debug|Any CPU + {26E40397-1D7F-4C4C-846C-3F3B46408054}.Debug|x64.Build.0 = Debug|Any CPU + {26E40397-1D7F-4C4C-846C-3F3B46408054}.Debug|x86.ActiveCfg = Debug|Any CPU + {26E40397-1D7F-4C4C-846C-3F3B46408054}.Debug|x86.Build.0 = Debug|Any CPU {26E40397-1D7F-4C4C-846C-3F3B46408054}.Release|Any CPU.ActiveCfg = Release|Any CPU {26E40397-1D7F-4C4C-846C-3F3B46408054}.Release|Any CPU.Build.0 = Release|Any CPU + {26E40397-1D7F-4C4C-846C-3F3B46408054}.Release|x64.ActiveCfg = Release|Any CPU + {26E40397-1D7F-4C4C-846C-3F3B46408054}.Release|x64.Build.0 = Release|Any CPU + {26E40397-1D7F-4C4C-846C-3F3B46408054}.Release|x86.ActiveCfg = Release|Any CPU + {26E40397-1D7F-4C4C-846C-3F3B46408054}.Release|x86.Build.0 = Release|Any CPU {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Debug|x64.ActiveCfg = Debug|Any CPU + {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Debug|x64.Build.0 = Debug|Any CPU + {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Debug|x86.ActiveCfg = Debug|Any CPU + {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Debug|x86.Build.0 = Debug|Any CPU {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Release|Any CPU.Build.0 = Release|Any CPU + {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Release|x64.ActiveCfg = Release|Any CPU + {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Release|x64.Build.0 = Release|Any CPU + {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Release|x86.ActiveCfg = Release|Any CPU + {6A20EB25-A7AC-45D4-8965-67EB190F862A}.Release|x86.Build.0 = Release|Any CPU {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Debug|x64.ActiveCfg = Debug|Any CPU + {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Debug|x64.Build.0 = Debug|Any CPU + {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Debug|x86.ActiveCfg = Debug|Any CPU + {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Debug|x86.Build.0 = Debug|Any CPU {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Release|Any CPU.Build.0 = Release|Any CPU + {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Release|x64.ActiveCfg = Release|Any CPU + {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Release|x64.Build.0 = Release|Any CPU + {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Release|x86.ActiveCfg = Release|Any CPU + {9B1081F8-9C48-4F2B-9944-E8B2BC231461}.Release|x86.Build.0 = Release|Any CPU {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Debug|x64.ActiveCfg = Debug|Any CPU + {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Debug|x64.Build.0 = Debug|Any CPU + {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Debug|x86.ActiveCfg = Debug|Any CPU + {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Debug|x86.Build.0 = Debug|Any CPU {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Release|Any CPU.ActiveCfg = Release|Any CPU {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Release|Any CPU.Build.0 = Release|Any CPU + {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Release|x64.ActiveCfg = Release|Any CPU + {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Release|x64.Build.0 = Release|Any CPU + {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Release|x86.ActiveCfg = Release|Any CPU + {5CD8D75B-DDF6-444D-81A8-92346BB65B86}.Release|x86.Build.0 = Release|Any CPU {2A550F70-7843-4188-BD57-C994E0D27DA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2A550F70-7843-4188-BD57-C994E0D27DA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A550F70-7843-4188-BD57-C994E0D27DA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {2A550F70-7843-4188-BD57-C994E0D27DA2}.Debug|x64.Build.0 = Debug|Any CPU + {2A550F70-7843-4188-BD57-C994E0D27DA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {2A550F70-7843-4188-BD57-C994E0D27DA2}.Debug|x86.Build.0 = Debug|Any CPU {2A550F70-7843-4188-BD57-C994E0D27DA2}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A550F70-7843-4188-BD57-C994E0D27DA2}.Release|Any CPU.Build.0 = Release|Any CPU + {2A550F70-7843-4188-BD57-C994E0D27DA2}.Release|x64.ActiveCfg = Release|Any CPU + {2A550F70-7843-4188-BD57-C994E0D27DA2}.Release|x64.Build.0 = Release|Any CPU + {2A550F70-7843-4188-BD57-C994E0D27DA2}.Release|x86.ActiveCfg = Release|Any CPU + {2A550F70-7843-4188-BD57-C994E0D27DA2}.Release|x86.Build.0 = Release|Any CPU {ADC60E62-AAD0-44A0-87DC-126170557958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ADC60E62-AAD0-44A0-87DC-126170557958}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADC60E62-AAD0-44A0-87DC-126170557958}.Debug|x64.ActiveCfg = Debug|Any CPU + {ADC60E62-AAD0-44A0-87DC-126170557958}.Debug|x64.Build.0 = Debug|Any CPU + {ADC60E62-AAD0-44A0-87DC-126170557958}.Debug|x86.ActiveCfg = Debug|Any CPU + {ADC60E62-AAD0-44A0-87DC-126170557958}.Debug|x86.Build.0 = Debug|Any CPU {ADC60E62-AAD0-44A0-87DC-126170557958}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADC60E62-AAD0-44A0-87DC-126170557958}.Release|Any CPU.Build.0 = Release|Any CPU + {ADC60E62-AAD0-44A0-87DC-126170557958}.Release|x64.ActiveCfg = Release|Any CPU + {ADC60E62-AAD0-44A0-87DC-126170557958}.Release|x64.Build.0 = Release|Any CPU + {ADC60E62-AAD0-44A0-87DC-126170557958}.Release|x86.ActiveCfg = Release|Any CPU + {ADC60E62-AAD0-44A0-87DC-126170557958}.Release|x86.Build.0 = Release|Any CPU {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Debug|x64.ActiveCfg = Debug|Any CPU + {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Debug|x64.Build.0 = Debug|Any CPU + {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Debug|x86.ActiveCfg = Debug|Any CPU + {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Debug|x86.Build.0 = Debug|Any CPU {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Release|Any CPU.ActiveCfg = Release|Any CPU {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Release|Any CPU.Build.0 = Release|Any CPU + {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Release|x64.ActiveCfg = Release|Any CPU + {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Release|x64.Build.0 = Release|Any CPU + {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Release|x86.ActiveCfg = Release|Any CPU + {97D1FF0C-72EF-4C45-93B3-C169BCD654FA}.Release|x86.Build.0 = Release|Any CPU {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Debug|x64.ActiveCfg = Debug|Any CPU + {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Debug|x64.Build.0 = Debug|Any CPU + {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Debug|x86.ActiveCfg = Debug|Any CPU + {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Debug|x86.Build.0 = Debug|Any CPU {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Release|Any CPU.ActiveCfg = Release|Any CPU {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Release|Any CPU.Build.0 = Release|Any CPU + {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Release|x64.ActiveCfg = Release|Any CPU + {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Release|x64.Build.0 = Release|Any CPU + {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Release|x86.ActiveCfg = Release|Any CPU + {588EA4DF-22E2-64C6-2DD1-6E8ADEDCBF1B}.Release|x86.Build.0 = Release|Any CPU {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Debug|x64.ActiveCfg = Debug|Any CPU + {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Debug|x64.Build.0 = Debug|Any CPU + {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Debug|x86.ActiveCfg = Debug|Any CPU + {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Debug|x86.Build.0 = Debug|Any CPU {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Release|Any CPU.Build.0 = Release|Any CPU + {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Release|x64.ActiveCfg = Release|Any CPU + {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Release|x64.Build.0 = Release|Any CPU + {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Release|x86.ActiveCfg = Release|Any CPU + {05DC5A74-CCC2-3A28-008A-F69B34CF66E5}.Release|x86.Build.0 = Release|Any CPU {18145AAA-0E16-6212-F535-4A82F5069A17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {18145AAA-0E16-6212-F535-4A82F5069A17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18145AAA-0E16-6212-F535-4A82F5069A17}.Debug|x64.ActiveCfg = Debug|Any CPU + {18145AAA-0E16-6212-F535-4A82F5069A17}.Debug|x64.Build.0 = Debug|Any CPU + {18145AAA-0E16-6212-F535-4A82F5069A17}.Debug|x86.ActiveCfg = Debug|Any CPU + {18145AAA-0E16-6212-F535-4A82F5069A17}.Debug|x86.Build.0 = Debug|Any CPU {18145AAA-0E16-6212-F535-4A82F5069A17}.Release|Any CPU.ActiveCfg = Release|Any CPU {18145AAA-0E16-6212-F535-4A82F5069A17}.Release|Any CPU.Build.0 = Release|Any CPU + {18145AAA-0E16-6212-F535-4A82F5069A17}.Release|x64.ActiveCfg = Release|Any CPU + {18145AAA-0E16-6212-F535-4A82F5069A17}.Release|x64.Build.0 = Release|Any CPU + {18145AAA-0E16-6212-F535-4A82F5069A17}.Release|x86.ActiveCfg = Release|Any CPU + {18145AAA-0E16-6212-F535-4A82F5069A17}.Release|x86.Build.0 = Release|Any CPU {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Debug|x64.ActiveCfg = Debug|Any CPU + {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Debug|x64.Build.0 = Debug|Any CPU + {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Debug|x86.ActiveCfg = Debug|Any CPU + {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Debug|x86.Build.0 = Debug|Any CPU {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Release|Any CPU.ActiveCfg = Release|Any CPU {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Release|Any CPU.Build.0 = Release|Any CPU + {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Release|x64.ActiveCfg = Release|Any CPU + {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Release|x64.Build.0 = Release|Any CPU + {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Release|x86.ActiveCfg = Release|Any CPU + {C2196593-1C13-50DC-5C68-2C3B3193BB38}.Release|x86.Build.0 = Release|Any CPU {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Debug|x64.ActiveCfg = Debug|Any CPU + {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Debug|x64.Build.0 = Debug|Any CPU + {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Debug|x86.ActiveCfg = Debug|Any CPU + {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Debug|x86.Build.0 = Debug|Any CPU {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Release|Any CPU.Build.0 = Release|Any CPU + {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Release|x64.ActiveCfg = Release|Any CPU + {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Release|x64.Build.0 = Release|Any CPU + {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Release|x86.ActiveCfg = Release|Any CPU + {79C8061A-C56C-CD48-CA80-5053CDE0A5F9}.Release|x86.Build.0 = Release|Any CPU {D140AFF0-4485-6214-3EB7-793EC812B972}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D140AFF0-4485-6214-3EB7-793EC812B972}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D140AFF0-4485-6214-3EB7-793EC812B972}.Debug|x64.ActiveCfg = Debug|Any CPU + {D140AFF0-4485-6214-3EB7-793EC812B972}.Debug|x64.Build.0 = Debug|Any CPU + {D140AFF0-4485-6214-3EB7-793EC812B972}.Debug|x86.ActiveCfg = Debug|Any CPU + {D140AFF0-4485-6214-3EB7-793EC812B972}.Debug|x86.Build.0 = Debug|Any CPU {D140AFF0-4485-6214-3EB7-793EC812B972}.Release|Any CPU.ActiveCfg = Release|Any CPU {D140AFF0-4485-6214-3EB7-793EC812B972}.Release|Any CPU.Build.0 = Release|Any CPU + {D140AFF0-4485-6214-3EB7-793EC812B972}.Release|x64.ActiveCfg = Release|Any CPU + {D140AFF0-4485-6214-3EB7-793EC812B972}.Release|x64.Build.0 = Release|Any CPU + {D140AFF0-4485-6214-3EB7-793EC812B972}.Release|x86.ActiveCfg = Release|Any CPU + {D140AFF0-4485-6214-3EB7-793EC812B972}.Release|x86.Build.0 = Release|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Debug|x64.ActiveCfg = Debug|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Debug|x64.Build.0 = Debug|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Debug|x86.ActiveCfg = Debug|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Debug|x86.Build.0 = Debug|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Release|Any CPU.Build.0 = Release|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Release|x64.ActiveCfg = Release|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Release|x64.Build.0 = Release|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Release|x86.ActiveCfg = Release|Any CPU + {E43DF8E7-E385-4791-AD9D-B8A968B4D020}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -196,6 +406,7 @@ Global {C2196593-1C13-50DC-5C68-2C3B3193BB38} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {79C8061A-C56C-CD48-CA80-5053CDE0A5F9} = {F10AA149-2626-486E-85BB-9CD5365F3016} {D140AFF0-4485-6214-3EB7-793EC812B972} = {F10AA149-2626-486E-85BB-9CD5365F3016} + {E43DF8E7-E385-4791-AD9D-B8A968B4D020} = {AFAA0841-BD93-466F-B8F4-FB4EEC86F1FC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {95ED23F3-8DEA-456E-A44B-87A161CB696A} diff --git a/shesha-core/src/Shesha.NHibernate/NHibernate/Interceptors/SheshaNHibernateInterceptor.cs b/shesha-core/src/Shesha.NHibernate/NHibernate/Interceptors/SheshaNHibernateInterceptor.cs index d77fdb7cf5..d46fc7da9b 100644 --- a/shesha-core/src/Shesha.NHibernate/NHibernate/Interceptors/SheshaNHibernateInterceptor.cs +++ b/shesha-core/src/Shesha.NHibernate/NHibernate/Interceptors/SheshaNHibernateInterceptor.cs @@ -41,7 +41,7 @@ public class SheshaNHibernateInterceptor : EmptyInterceptor public SheshaNHibernateInterceptor(IIocManager iocManager) { - EntityChangeEventHelper = default!; + EntityChangeEventHelper = NullEntityChangeEventHelper.Instance; _iocManager = iocManager; _abpSession = @@ -103,8 +103,8 @@ public override bool OnSave(object entity, object id, object[] state, string[] p } } - EntityChangeEventHelper.TriggerEntityCreatingEvent(entity); - EntityChangeEventHelper.TriggerEntityCreatedEventOnUowCompleted(entity); + EntityChangeEventHelper?.TriggerEntityCreatingEvent(entity); + EntityChangeEventHelper?.TriggerEntityCreatedEventOnUowCompleted(entity); TriggerDomainEvents(entity); EntityHistoryHelper?.AddEntityChange(entity); @@ -141,7 +141,7 @@ public override bool OnFlushDirty(object entity, object id, object[] currentStat } } - if (entity is ISoftDelete && entity.As().IsDeleted) + if (entity is ISoftDelete && entity.As().IsDeleted && previousState != null) { //Is deleted before? Normally, a deleted entity should not be updated later but I preferred to check it. var previousIsDeleted = false; @@ -182,19 +182,19 @@ public override bool OnFlushDirty(object entity, object id, object[] currentStat } } - EntityChangeEventHelper.TriggerEntityDeletingEvent(entity); - EntityChangeEventHelper.TriggerEntityDeletedEventOnUowCompleted(entity); + EntityChangeEventHelper?.TriggerEntityDeletingEvent(entity); + EntityChangeEventHelper?.TriggerEntityDeletedEventOnUowCompleted(entity); } else { - EntityChangeEventHelper.TriggerEntityUpdatingEvent(entity); - EntityChangeEventHelper.TriggerEntityUpdatedEventOnUowCompleted(entity); + EntityChangeEventHelper?.TriggerEntityUpdatingEvent(entity); + EntityChangeEventHelper?.TriggerEntityUpdatedEventOnUowCompleted(entity); } } else { - EntityChangeEventHelper.TriggerEntityUpdatingEvent(entity); - EntityChangeEventHelper.TriggerEntityUpdatedEventOnUowCompleted(entity); + EntityChangeEventHelper?.TriggerEntityUpdatingEvent(entity); + EntityChangeEventHelper?.TriggerEntityUpdatedEventOnUowCompleted(entity); } TriggerDomainEvents(entity); @@ -206,8 +206,8 @@ public override bool OnFlushDirty(object entity, object id, object[] currentStat public override void OnDelete(object entity, object id, object[] state, string[] propertyNames, IType[] types) { - EntityChangeEventHelper.TriggerEntityDeletingEvent(entity); - EntityChangeEventHelper.TriggerEntityDeletedEventOnUowCompleted(entity); + EntityChangeEventHelper?.TriggerEntityDeletingEvent(entity); + EntityChangeEventHelper?.TriggerEntityDeletedEventOnUowCompleted(entity); TriggerDomainEvents(entity); diff --git a/shesha-core/test/Shesha.Tests/DependencyInjection/ServiceCollectionRegistrar.cs b/shesha-core/src/Shesha.Testing/DependencyInjection/ServiceCollectionRegistrar.cs similarity index 87% rename from shesha-core/test/Shesha.Tests/DependencyInjection/ServiceCollectionRegistrar.cs rename to shesha-core/src/Shesha.Testing/DependencyInjection/ServiceCollectionRegistrar.cs index e883e55649..fd22574876 100644 --- a/shesha-core/test/Shesha.Tests/DependencyInjection/ServiceCollectionRegistrar.cs +++ b/shesha-core/src/Shesha.Testing/DependencyInjection/ServiceCollectionRegistrar.cs @@ -1,9 +1,9 @@ -using Abp.Dependency; +using Abp.Dependency; using Castle.Windsor.MsDependencyInjection; using Microsoft.Extensions.DependencyInjection; using Shesha.Identity; -namespace Shesha.Tests.DependencyInjection +namespace Shesha.Testing.DependencyInjection { public static class ServiceCollectionRegistrar { diff --git a/shesha-core/test/Shesha.Tests/Fixtures/IDatabaseFixture.cs b/shesha-core/src/Shesha.Testing/Fixtures/IDatabaseFixture.cs similarity index 72% rename from shesha-core/test/Shesha.Tests/Fixtures/IDatabaseFixture.cs rename to shesha-core/src/Shesha.Testing/Fixtures/IDatabaseFixture.cs index 229239e7f7..ade3f5633f 100644 --- a/shesha-core/test/Shesha.Tests/Fixtures/IDatabaseFixture.cs +++ b/shesha-core/src/Shesha.Testing/Fixtures/IDatabaseFixture.cs @@ -1,7 +1,7 @@ -namespace Shesha.Tests.Fixtures +namespace Shesha.Testing.Fixtures { /// - /// Database fixture + /// Database fixture providing connection configuration for integration tests. /// public interface IDatabaseFixture { diff --git a/shesha-core/src/Shesha.Testing/Fixtures/LocalSqlServerCollection.cs b/shesha-core/src/Shesha.Testing/Fixtures/LocalSqlServerCollection.cs new file mode 100644 index 0000000000..a616d55c2b --- /dev/null +++ b/shesha-core/src/Shesha.Testing/Fixtures/LocalSqlServerCollection.cs @@ -0,0 +1,13 @@ +using Xunit; + +namespace Shesha.Testing.Fixtures +{ + /// + /// Shared local SQL Server fixture collection. + /// + [CollectionDefinition(Name)] + public class LocalSqlServerCollection : ICollectionFixture + { + public const string Name = "LocalSqlServer"; + } +} diff --git a/shesha-core/test/Shesha.Tests/Fixtures/LocalSqlServerFixture.cs b/shesha-core/src/Shesha.Testing/Fixtures/LocalSqlServerFixture.cs similarity index 67% rename from shesha-core/test/Shesha.Tests/Fixtures/LocalSqlServerFixture.cs rename to shesha-core/src/Shesha.Testing/Fixtures/LocalSqlServerFixture.cs index c800dcf783..cdf9c9c0ed 100644 --- a/shesha-core/test/Shesha.Tests/Fixtures/LocalSqlServerFixture.cs +++ b/shesha-core/src/Shesha.Testing/Fixtures/LocalSqlServerFixture.cs @@ -1,12 +1,15 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using System.Threading.Tasks; using Xunit; -namespace Shesha.Tests.Fixtures +namespace Shesha.Testing.Fixtures { + /// + /// xUnit fixture for a local SQL Server instance. Reads connection from appsettings.Test.json. + /// public class LocalSqlServerFixture : IDatabaseFixture, IAsyncLifetime { - public string ConnectionString { get; private set; } + public string ConnectionString { get; private set; } = default!; public DbmsType DbmsType { get; private set; } = DbmsType.SQLServer; @@ -16,7 +19,7 @@ public LocalSqlServerFixture() public Task InitializeAsync() { - var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + var config = new ConfigurationBuilder().AddJsonFile("appsettings.Test.json").Build(); DbmsType = config.GetDbmsType(); ConnectionString = config.GetRequiredConnectionString("TestDB"); diff --git a/shesha-core/src/Shesha.Testing/Fixtures/PostgreSqlCollection.cs b/shesha-core/src/Shesha.Testing/Fixtures/PostgreSqlCollection.cs new file mode 100644 index 0000000000..ba5547f4fc --- /dev/null +++ b/shesha-core/src/Shesha.Testing/Fixtures/PostgreSqlCollection.cs @@ -0,0 +1,13 @@ +using Xunit; + +namespace Shesha.Testing.Fixtures +{ + /// + /// Shared PostgreSql fixture (Testcontainers). + /// + [CollectionDefinition(Name)] + public class PostgreSqlCollection : ICollectionFixture + { + public const string Name = "SharedPostgreSql"; + } +} diff --git a/shesha-core/test/Shesha.Tests/Fixtures/PostgreSqlFixture.cs b/shesha-core/src/Shesha.Testing/Fixtures/PostgreSqlFixture.cs similarity index 62% rename from shesha-core/test/Shesha.Tests/Fixtures/PostgreSqlFixture.cs rename to shesha-core/src/Shesha.Testing/Fixtures/PostgreSqlFixture.cs index 22d29909c2..8fb0e08acd 100644 --- a/shesha-core/test/Shesha.Tests/Fixtures/PostgreSqlFixture.cs +++ b/shesha-core/src/Shesha.Testing/Fixtures/PostgreSqlFixture.cs @@ -1,9 +1,12 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Testcontainers.PostgreSql; using Xunit; -namespace Shesha.Tests.Fixtures +namespace Shesha.Testing.Fixtures { + /// + /// xUnit fixture that spins up a PostgreSQL Docker container via Testcontainers. + /// [Collection("Sequential")] public class PostgreSqlFixture : IDatabaseFixture, IAsyncLifetime { @@ -13,32 +16,29 @@ public class PostgreSqlFixture : IDatabaseFixture, IAsyncLifetime private readonly PostgreSqlContainer _postgresContainer; - public string ConnectionString { get; private set; } + public string ConnectionString { get; private set; } = default!; public DbmsType DbmsType => DbmsType.PostgreSQL; public PostgreSqlFixture() { - // Configure the PostgreSQL container _postgresContainer = new PostgreSqlBuilder() - .WithImage("boxfusionregistry.azurecr.io/shesha-framework-postgresql:1.0") // Use the latest PostgreSQL image - .WithDatabase(DbName) // Set the database name - .WithUsername(Login) // Set the username - .WithPassword(Password) // Set the password - .WithExposedPort(5432) // Expose the default PostgreSQL port + .WithImage("boxfusionregistry.azurecr.io/shesha-framework-postgresql:1.0") + .WithDatabase(DbName) + .WithUsername(Login) + .WithPassword(Password) + .WithExposedPort(5432) .Build(); } public async Task InitializeAsync() { - // Start the container and initialize the connection string await _postgresContainer.StartAsync(); ConnectionString = $"Host=127.0.0.1;Port={_postgresContainer.GetMappedPublicPort(5432)};Database={DbName};Username={Login};Password={Password};"; } public async Task DisposeAsync() { - // Stop and remove the container await _postgresContainer.StopAsync(); } } diff --git a/shesha-core/src/Shesha.Testing/Fixtures/SqlServerCollection.cs b/shesha-core/src/Shesha.Testing/Fixtures/SqlServerCollection.cs new file mode 100644 index 0000000000..255952a2d8 --- /dev/null +++ b/shesha-core/src/Shesha.Testing/Fixtures/SqlServerCollection.cs @@ -0,0 +1,13 @@ +using Xunit; + +namespace Shesha.Testing.Fixtures +{ + /// + /// Shared Sql Server fixture (Testcontainers). + /// + [CollectionDefinition(Name)] + public class SqlServerCollection : ICollectionFixture + { + public const string Name = "SharedSqlServer"; + } +} diff --git a/shesha-core/test/Shesha.Tests/Fixtures/SqlServerFixture.cs b/shesha-core/src/Shesha.Testing/Fixtures/SqlServerFixture.cs similarity index 66% rename from shesha-core/test/Shesha.Tests/Fixtures/SqlServerFixture.cs rename to shesha-core/src/Shesha.Testing/Fixtures/SqlServerFixture.cs index 4365bdb8fb..d1319fdb5b 100644 --- a/shesha-core/test/Shesha.Tests/Fixtures/SqlServerFixture.cs +++ b/shesha-core/src/Shesha.Testing/Fixtures/SqlServerFixture.cs @@ -1,9 +1,12 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Testcontainers.MsSql; using Xunit; -namespace Shesha.Tests.Fixtures +namespace Shesha.Testing.Fixtures { + /// + /// xUnit fixture that spins up a SQL Server Docker container via Testcontainers. + /// public class SqlServerFixture : IDatabaseFixture, IAsyncLifetime { private const string Login = "sa"; @@ -11,30 +14,27 @@ public class SqlServerFixture : IDatabaseFixture, IAsyncLifetime private const string DbName = "TestDB"; private readonly MsSqlContainer _mssqlContainer; - public string ConnectionString { get; private set; } + public string ConnectionString { get; private set; } = default!; public DbmsType DbmsType => DbmsType.SQLServer; public SqlServerFixture() { - // Configure the SQL Server container _mssqlContainer = new MsSqlBuilder() - .WithImage("boxfusionregistry.azurecr.io/shesha-framework-mssql:1.0") // Specify the SQL Server image - .WithPassword(Password) // Set the SA password - .WithExposedPort(1433) // Expose the default SQL Server port + .WithImage("boxfusionregistry.azurecr.io/shesha-framework-mssql:1.0") + .WithPassword(Password) + .WithExposedPort(1433) .Build(); } public async Task InitializeAsync() { - // Start the container and initialize the connection string await _mssqlContainer.StartAsync(); ConnectionString = $"Server=127.0.0.1,{_mssqlContainer.GetMappedPublicPort(1433)};Database={DbName};User Id={Login};Password={Password};TrustServerCertificate=True;"; } public Task DisposeAsync() { - // Stop and remove the container return _mssqlContainer.StopAsync(); } } diff --git a/shesha-core/src/Shesha.Testing/README.md b/shesha-core/src/Shesha.Testing/README.md new file mode 100644 index 0000000000..8377f1dc74 --- /dev/null +++ b/shesha-core/src/Shesha.Testing/README.md @@ -0,0 +1,13 @@ +# Shesha.Testing + +Reusable integration test infrastructure for Shesha framework applications. + +Provides base classes, xUnit fixtures, and helpers for writing NHibernate-backed integration tests against ABP modules. + +## Key Components + +- **`ShaIntegratedTestBase`** - ABP test base with IoC, UoW helpers, and `Resolve()` +- **`SheshaNhTestBase`** - NHibernate test base with session helpers and login helpers +- **`SheshaTestModuleHelper.ConfigureForTesting()`** - Extension method that configures common test module boilerplate +- **Database Fixtures** - `LocalSqlServerFixture`, `SqlServerFixture` (Testcontainers), `PostgreSqlFixture` (Testcontainers) +- **`UnitTestHelper`** - Mock helpers for `IWebHostEnvironment`, API explorer, and fake service registration diff --git a/shesha-core/test/Shesha.Tests/ShaIntegratedTestBase.cs b/shesha-core/src/Shesha.Testing/ShaIntegratedTestBase.cs similarity index 89% rename from shesha-core/test/Shesha.Tests/ShaIntegratedTestBase.cs rename to shesha-core/src/Shesha.Testing/ShaIntegratedTestBase.cs index ddbdc1b0bd..d72207d96c 100644 --- a/shesha-core/test/Shesha.Tests/ShaIntegratedTestBase.cs +++ b/shesha-core/src/Shesha.Testing/ShaIntegratedTestBase.cs @@ -1,4 +1,4 @@ -using Abp; +using Abp; using Abp.Dependency; using Abp.Domain.Uow; using Abp.Modules; @@ -6,12 +6,12 @@ using Abp.TestBase.Runtime.Session; using Castle.MicroKernel.Registration; using Shesha.Services; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using System; using System.Reflection; using System.Threading.Tasks; -namespace Shesha.Tests +namespace Shesha.Testing { /// /// This is the base class for all tests integrated to ABP. @@ -74,7 +74,6 @@ protected void InitializeAbp() /// protected virtual void PreInitialize() { - } /// @@ -82,7 +81,6 @@ protected virtual void PreInitialize() /// protected virtual void PostInitialize() { - } public virtual void Dispose() @@ -96,8 +94,6 @@ public virtual void Dispose() /// A shortcut to resolve an object from . /// Also registers as transient if it's not registered before. /// - /// Type of the object to get - /// The object instance protected T Resolve() { EnsureClassRegistered(typeof(T)); @@ -108,9 +104,6 @@ protected T Resolve() /// A shortcut to resolve an object from . /// Also registers as transient if it's not registered before. /// - /// Type of the object to get - /// Constructor arguments - /// The object instance protected T Resolve(object argumentsAsAnonymousType) { EnsureClassRegistered(typeof(T)); @@ -121,8 +114,6 @@ protected T Resolve(object argumentsAsAnonymousType) /// A shortcut to resolve an object from . /// Also registers as transient if it's not registered before. /// - /// Type of the object to get - /// The object instance protected object Resolve(Type type) { EnsureClassRegistered(type); @@ -133,9 +124,6 @@ protected object Resolve(Type type) /// A shortcut to resolve an object from . /// Also registers as transient if it's not registered before. /// - /// Type of the object to get - /// Constructor arguments - /// The object instance protected object Resolve(Type type, object argumentsAsAnonymousType) { EnsureClassRegistered(type); @@ -145,8 +133,6 @@ protected object Resolve(Type type, object argumentsAsAnonymousType) /// /// Registers given type if it's not registered before. /// - /// Type to check and register - /// Lifestyle protected void EnsureClassRegistered(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Transient) { if (!LocalIocManager.IsRegistered(type)) diff --git a/shesha-core/src/Shesha.Testing/Shesha.Testing.csproj b/shesha-core/src/Shesha.Testing/Shesha.Testing.csproj new file mode 100644 index 0000000000..c06a745e36 --- /dev/null +++ b/shesha-core/src/Shesha.Testing/Shesha.Testing.csproj @@ -0,0 +1,38 @@ + + + net8.0 + Shesha.Testing + Shesha.Testing + Shesha.Testing + Shesha.Testing + Shesha + 1.0.0 + Reusable integration test infrastructure for Shesha applications. + false + True + README.md + .\Shesha.Testing.xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shesha-core/src/Shesha.Testing/Shesha.Testing.xml b/shesha-core/src/Shesha.Testing/Shesha.Testing.xml new file mode 100644 index 0000000000..a975b4c848 --- /dev/null +++ b/shesha-core/src/Shesha.Testing/Shesha.Testing.xml @@ -0,0 +1,155 @@ + + + + Shesha.Testing + + + + + Database fixture providing connection configuration for integration tests. + + + + + Connection string + + + + + DBMS type + + + + + Shared local SQL Server fixture collection. + + + + + xUnit fixture for a local SQL Server instance. Reads connection from appsettings.Test.json. + + + + + Shared PostgreSql fixture (Testcontainers). + + + + + xUnit fixture that spins up a PostgreSQL Docker container via Testcontainers. + + + + + Shared Sql Server fixture (Testcontainers). + + + + + xUnit fixture that spins up a SQL Server Docker container via Testcontainers. + + + + + This is the base class for all tests integrated to ABP. + + + + + Local used for this test. + + + + + Gets Session object. Can be used to change current user and tenant in tests. + + + + + This method can be overrided to replace some services with fakes. + + + + + This method can be overrided to replace some services with fakes. + + + + + A shortcut to resolve an object from . + Also registers as transient if it's not registered before. + + + + + A shortcut to resolve an object from . + Also registers as transient if it's not registered before. + + + + + A shortcut to resolve an object from . + Also registers as transient if it's not registered before. + + + + + A shortcut to resolve an object from . + Also registers as transient if it's not registered before. + + + + + Registers given type if it's not registered before. + + + + + Gets current user if is not null. + Throws exception if it's null. + + + + + Gets current tenant if is not null. + Throws exception if there is no current tenant. + + + + + Extension methods for configuring Shesha test modules with common boilerplate. + + + + + Configures common test module settings. Call this from your test module's PreInitialize(). + Registers mocks, configures NHibernate, disables background services, and sets up + standard service replacements needed by all Shesha integration test modules. + + The ABP module being configured. + The IoC manager to register services with. + The name of the appsettings JSON file (default: "appsettings.Test.json"). + + + + Concrete implementation for test contexts. + Avoids requiring Moq just for environment mocking. + + + + + Mock using a concrete . + + + + + Register fake service using NSubstitute. + + + + + Mock ASP.NET Core API explorer and Swagger infrastructure to prevent errors during test module initialization. + + + + diff --git a/shesha-core/src/Shesha.Testing/SheshaNhTestBase.cs b/shesha-core/src/Shesha.Testing/SheshaNhTestBase.cs new file mode 100644 index 0000000000..f293e1b8ce --- /dev/null +++ b/shesha-core/src/Shesha.Testing/SheshaNhTestBase.cs @@ -0,0 +1,226 @@ +using Abp; +using Abp.Authorization.Users; +using Abp.Dependency; +using Abp.Domain.Uow; +using Abp.Modules; +using Abp.MultiTenancy; +using Abp.Runtime.Session; +using NHibernate; +using NHibernate.Linq; +using Shesha.Authorization.Users; +using Shesha.Domain; +using Shesha.MultiTenancy; +using Shesha.NHibernate.UoW; +using Shesha.Testing.Fixtures; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Shesha.Testing +{ + public abstract class SheshaNhTestBase : ShaIntegratedTestBase where TStartupModule : AbpModule + { + protected SheshaNhTestBase(IDatabaseFixture fixture) : base(fixture) + { + LoginAsHostAdmin(); + + EntityHelper.RefreshStore(LocalIocManager); + } + + #region UsingDbSession + + protected IDisposable UsingTenantId(int? tenantId) + { + var previousTenantId = AbpSession.TenantId; + AbpSession.TenantId = tenantId; + return new DisposeAction(() => AbpSession.TenantId = previousTenantId); + } + + protected void UsingDbSession(Action action) + { + UsingDbSession(AbpSession.TenantId, action); + } + + protected Task UsingDbSessionAsync(Func action) + { + return UsingDbSessionAsync(AbpSession.TenantId, action); + } + + protected T UsingDbSession(Func func) + { + return UsingDbSession(AbpSession.TenantId, func); + } + + protected Task UsingDbSessionAsync(Func> func) + { + return UsingDbSessionAsync(AbpSession.TenantId, func); + } + + protected void UsingDbSession(int? tenantId, Action action) + { + using (UsingTenantId(tenantId)) + { + using (var session = OpenSession()) + { + action(session); + } + } + } + + private ISession OpenSession() + { + return LocalIocManager.Resolve().OpenSession(); + } + + protected async Task UsingDbSessionAsync(int? tenantId, Func action) + { + using (UsingTenantId(tenantId)) + { + using (var session = OpenSession()) + { + await action(session); + await session.FlushAsync(); + } + } + } + + protected T UsingDbSession(int? tenantId, Func func) + { + T result; + + using (UsingTenantId(tenantId)) + { + using (var session = OpenSession()) + { + result = func(session); + session.Flush(); + } + } + + return result; + } + + protected async Task UsingDbSessionAsync(int? tenantId, Func> func) + { + T result; + + using (UsingTenantId(tenantId)) + { + using (var session = OpenSession()) + { + result = await func(session); + await session.FlushAsync(); + } + } + + return result; + } + + #endregion + + #region Login + + protected void LoginAsHostAdmin() + { + LoginAsHost(AbpUserBase.AdminUserName); + } + + protected void LoginAsDefaultTenantAdmin() + { + LoginAsTenant(AbpTenantBase.DefaultTenantName, AbpUserBase.AdminUserName); + } + + protected void LoginAsHost(string userName) + { + AbpSession.TenantId = null; + + var user = + UsingDbSession( + session => + session.Query().FirstOrDefault(u => u.TenantId == AbpSession.TenantId && u.UserName == userName)); + if (user == null) + { + throw new Exception("There is no user: " + userName + " for host."); + } + + AbpSession.UserId = user.Id; + } + + protected void LoginAsTenant(string tenancyName, string userName) + { + var tenant = UsingDbSession(session => session.Query().FirstOrDefault(t => t.TenancyName == tenancyName)); + if (tenant == null) + { + throw new Exception("There is no tenant: " + tenancyName); + } + + AbpSession.TenantId = tenant.Id; + + var user = + UsingDbSession( + session => + session.Query().FirstOrDefault(u => u.TenantId == AbpSession.TenantId && u.UserName == userName)); + if (user == null) + { + throw new Exception("There is no user: " + userName + " for tenant: " + tenancyName); + } + + AbpSession.UserId = user.Id; + } + + #endregion + + /// + /// Gets current user if is not null. + /// Throws exception if it's null. + /// + protected Task GetCurrentUserAsync() + { + var userId = AbpSession.GetUserId(); + return UsingDbSession(session => session.Query().SingleAsync(u => u.Id == userId)); + } + + /// + /// Gets current tenant if is not null. + /// Throws exception if there is no current tenant. + /// + protected Task GetCurrentTenantAsync() + { + var tenantId = AbpSession.GetTenantId(); + return UsingDbSession(session => session.Query().SingleAsync(t => t.Id == tenantId)); + } + + protected void UsingNhSession(Action action) + { + using (var uow = NewNhUnitOfWork()) + { +#pragma warning disable IDISP001 // Dispose created + var session = uow.GetSession(); +#pragma warning restore IDISP001 // Dispose created + action.Invoke(session); + uow.Complete(); + } + } + + protected NhUnitOfWork NewNhUnitOfWork() + { + var unitOfWorkManager = Resolve(); + return unitOfWorkManager.Begin() is NhUnitOfWork nhuow + ? nhuow + : throw new Exception($"Unexpected type of UnitOfWork. Expected '{nameof(NhUnitOfWork)}'"); + } + + protected virtual async Task WithUnitOfWorkAsync(Func> action, UnitOfWorkOptions? options = null) + { + using (var uowManager = LocalIocManager.ResolveAsDisposable()) + { + using (var uow = uowManager.Object.Begin(options ?? new UnitOfWorkOptions())) + { + var result = await action(); + await uow.CompleteAsync(); + return result; + } + } + } + } +} diff --git a/shesha-core/src/Shesha.Testing/SheshaTestModuleHelper.cs b/shesha-core/src/Shesha.Testing/SheshaTestModuleHelper.cs new file mode 100644 index 0000000000..9c7fcf8116 --- /dev/null +++ b/shesha-core/src/Shesha.Testing/SheshaTestModuleHelper.cs @@ -0,0 +1,119 @@ +using Abp.AspNetCore; +using Abp.AspNetCore.Configuration; +using Abp.AutoMapper; +using Abp.Configuration.Startup; +using Abp.Dependency; +using Abp.Domain.Uow; +using Abp.Modules; +using Abp.Net.Mail; +using Abp.Zero.Configuration; +using Castle.MicroKernel.Registration; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Moq; +using Shesha.Configuration.Startup; +using Shesha.FluentMigrator; +using Shesha.NHibernate; +using Shesha.Services; +using Shesha.Testing.DependencyInjection; +using Shesha.Testing.Fixtures; +using System; + +namespace Shesha.Testing +{ + /// + /// Extension methods for configuring Shesha test modules with common boilerplate. + /// + public static class SheshaTestModuleHelper + { + /// + /// Configures common test module settings. Call this from your test module's PreInitialize(). + /// Registers mocks, configures NHibernate, disables background services, and sets up + /// standard service replacements needed by all Shesha integration test modules. + /// + /// The ABP module being configured. + /// The IoC manager to register services with. + /// The name of the appsettings JSON file (default: "appsettings.Test.json"). + public static void ConfigureForTesting(this AbpModule module, IIocManager iocManager, string configFileName = "appsettings.Test.json") + { + var configuration = iocManager.Resolve(); + + // --- Register service mocks --- + iocManager.MockWebHostEnvironment(); + + if (!iocManager.IsRegistered()) + { + iocManager.IocContainer.Register( + Component.For() + .ImplementedBy() + .LifestyleSingleton() + ); + } + + if (!iocManager.IsRegistered()) + { + var appLifetimeMock = new Mock(); + iocManager.IocContainer.Register( + Component.For() + .Instance(appLifetimeMock.Object) + .LifestyleSingleton() + ); + } + + // Register IConfiguration from appsettings file + var testConfiguration = new ConfigurationBuilder().AddJsonFile(configFileName).Build(); + if (!iocManager.IsRegistered()) + { + iocManager.IocContainer.Register( + Component.For() + .Instance(testConfiguration) + .Named("test-configuration") + .IsDefault() + .LifestyleSingleton() + ); + } + + iocManager.MockApiExplorer(); + + // --- Configure NHibernate --- + var nhConfig = configuration.Modules.ShaNHibernate(); + + var dbFixture = iocManager.IsRegistered() + ? iocManager.Resolve() + : null; + if (dbFixture != null) + { + nhConfig.UseDbms(c => dbFixture.DbmsType, c => dbFixture.ConnectionString); + } + else + { + nhConfig.UseDbms(c => testConfiguration.GetDbmsType(), c => testConfiguration.GetRequiredConnectionString("TestDB")); + } + + // --- Unit of Work --- + configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(30); + configuration.UnitOfWork.IsTransactional = false; + + // --- Disable features that interfere with testing --- + configuration.Modules.AbpAutoMapper().UseStaticMapper = false; + configuration.BackgroundJobs.IsJobExecutionEnabled = false; + configuration.EntityHistory.IsEnabled = false; + + // --- DB localization --- + configuration.Modules.Zero().LanguageManagement.EnableDbLocalization(); + + // --- Service replacements --- + iocManager.RegisterFakeService(); + configuration.ReplaceService(DependencyLifeStyle.Transient); + configuration.ReplaceService(DependencyLifeStyle.Transient); + configuration.ReplaceService(DependencyLifeStyle.Singleton); + + if (!iocManager.IsRegistered()) + iocManager.IocContainer.Register(Component.For().ImplementedBy()); + + // --- Identity services --- + ServiceCollectionRegistrar.Register(iocManager); + } + } +} diff --git a/shesha-core/src/Shesha.Testing/TestWebHostEnvironment.cs b/shesha-core/src/Shesha.Testing/TestWebHostEnvironment.cs new file mode 100644 index 0000000000..81852e7b99 --- /dev/null +++ b/shesha-core/src/Shesha.Testing/TestWebHostEnvironment.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.FileProviders; +using System; +using System.IO; + +namespace Shesha.Testing +{ + /// + /// Concrete implementation for test contexts. + /// Avoids requiring Moq just for environment mocking. + /// + public class TestWebHostEnvironment : IWebHostEnvironment + { + public string ApplicationName { get; set; } = "Test Application"; + public string WebRootPath { get; set; } = Path.Combine(Environment.CurrentDirectory, "wwwroot"); + public string EnvironmentName { get; set; } = "Test"; + public string ContentRootPath { get; set; } = Environment.CurrentDirectory; + public IFileProvider WebRootFileProvider { get; set; } = null!; + public IFileProvider ContentRootFileProvider { get; set; } = null!; + } +} diff --git a/shesha-core/test/Shesha.Tests/UnitTestHelper.cs b/shesha-core/src/Shesha.Testing/UnitTestHelper.cs similarity index 68% rename from shesha-core/test/Shesha.Tests/UnitTestHelper.cs rename to shesha-core/src/Shesha.Testing/UnitTestHelper.cs index f19375a580..8fb949ad2a 100644 --- a/shesha-core/test/Shesha.Tests/UnitTestHelper.cs +++ b/shesha-core/src/Shesha.Testing/UnitTestHelper.cs @@ -1,90 +1,84 @@ -using Abp.Dependency; -using Castle.MicroKernel.Registration; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Abstractions; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Moq; -using NSubstitute; -using Shesha.Reflection; -using Swashbuckle.AspNetCore.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; -using System.Collections.Generic; -using System.IO; -using System.Reflection; - -namespace Shesha.Tests -{ - public static class UnitTestHelper - { - /// - /// Mock - /// - /// - public static void MockWebHostEnvirtonment(this IIocManager iocManager) - { - var hostingEnvironment = new Mock(); - hostingEnvironment.Setup(x => x.ApplicationName).Returns("test"); - hostingEnvironment.Setup(x => x.EnvironmentName).Returns("test"); - - var rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).NotNullOrWhiteSpace(); - hostingEnvironment.Setup(x => x.ContentRootPath).Returns(rootPath); - var hh = hostingEnvironment.As().Object; - - iocManager.IocContainer.Register( - Component.For() - .Instance(hh) - .LifestyleSingleton() - ); - } - - /// - /// Register fake service - /// - /// - /// - public static void RegisterFakeService(this IIocManager iocManager) where TService : class - { - iocManager.IocContainer.Register( - Component.For() - .UsingFactoryMethod(() => Substitute.For()) - .LifestyleSingleton() - ); - } - - public static void MockApiExplorer(this IIocManager iocManager) - { - var actionDescriptorCollectionProviderMock = new Mock(); - actionDescriptorCollectionProviderMock.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List(), 0)); - - iocManager.IocContainer.Register( - Component.For() - .Instance(actionDescriptorCollectionProviderMock.Object) - .LifestyleSingleton() - ); - - var actionGroupDescriptorCollectionProviderMock = new Mock(); - actionGroupDescriptorCollectionProviderMock.Setup(m => m.ApiDescriptionGroups).Returns(new ApiDescriptionGroupCollection(new List(), 1)); - iocManager.IocContainer.Register( - Component.For() - .Instance(actionGroupDescriptorCollectionProviderMock.Object) - .LifestyleSingleton() - ); - - - var swaggerProviderMock = new Mock(); - iocManager.IocContainer.Register( - Component.For() - .Instance(swaggerProviderMock.Object) - .LifestyleSingleton() - ); - - var schemaGeneratorMock = new Mock(); - iocManager.IocContainer.Register( - Component.For() - .Instance(schemaGeneratorMock.Object) - .LifestyleSingleton() - ); - } - } -} +using Abp.Dependency; +using Castle.MicroKernel.Registration; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Moq; +using NSubstitute; +using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; +using System.Collections.Generic; + +namespace Shesha.Testing +{ + public static class UnitTestHelper + { + /// + /// Mock using a concrete . + /// + public static void MockWebHostEnvironment(this IIocManager iocManager) + { + if (!iocManager.IsRegistered()) + { + iocManager.IocContainer.Register( + Component.For() + .ImplementedBy() + .LifestyleSingleton() + ); + } + } + + /// + /// Register fake service using NSubstitute. + /// + public static void RegisterFakeService(this IIocManager iocManager) where TService : class + { + if (iocManager.IsRegistered()) + return; + + iocManager.IocContainer.Register( + Component.For() + .UsingFactoryMethod(() => Substitute.For()) + .LifestyleSingleton() + ); + } + + /// + /// Mock ASP.NET Core API explorer and Swagger infrastructure to prevent errors during test module initialization. + /// + public static void MockApiExplorer(this IIocManager iocManager) + { + var actionDescriptorCollectionProviderMock = new Mock(); + actionDescriptorCollectionProviderMock.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List(), 0)); + + iocManager.IocContainer.Register( + Component.For() + .Instance(actionDescriptorCollectionProviderMock.Object) + .LifestyleSingleton() + ); + + var actionGroupDescriptorCollectionProviderMock = new Mock(); + actionGroupDescriptorCollectionProviderMock.Setup(m => m.ApiDescriptionGroups).Returns(new ApiDescriptionGroupCollection(new List(), 1)); + iocManager.IocContainer.Register( + Component.For() + .Instance(actionGroupDescriptorCollectionProviderMock.Object) + .LifestyleSingleton() + ); + + var swaggerProviderMock = new Mock(); + iocManager.IocContainer.Register( + Component.For() + .Instance(swaggerProviderMock.Object) + .LifestyleSingleton() + ); + + var schemaGeneratorMock = new Mock(); + iocManager.IocContainer.Register( + Component.For() + .Instance(schemaGeneratorMock.Object) + .LifestyleSingleton() + ); + } + } +} diff --git a/shesha-core/test/Shesha.Tests/CiSheshaTestBase.cs b/shesha-core/test/Shesha.Tests/CiSheshaTestBase.cs index be6c363ee9..589bbf4335 100644 --- a/shesha-core/test/Shesha.Tests/CiSheshaTestBase.cs +++ b/shesha-core/test/Shesha.Tests/CiSheshaTestBase.cs @@ -3,7 +3,7 @@ using Abp.Domain.Uow; using Shesha.Domain; using Shesha.Extensions; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using System; using System.Linq; using System.Linq.Expressions; diff --git a/shesha-core/test/Shesha.Tests/ConfigurationItems/FormConfiguration_Tests.cs b/shesha-core/test/Shesha.Tests/ConfigurationItems/FormConfiguration_Tests.cs index 646266e19c..cf9cab3ecb 100644 --- a/shesha-core/test/Shesha.Tests/ConfigurationItems/FormConfiguration_Tests.cs +++ b/shesha-core/test/Shesha.Tests/ConfigurationItems/FormConfiguration_Tests.cs @@ -15,7 +15,7 @@ using Shesha.Permissions; using Shesha.Reflection; using Shesha.Services; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shesha.Utilities; using Shesha.Web.FormsDesigner.Services.Distribution; using Shouldly; diff --git a/shesha-core/test/Shesha.Tests/DomainModel/LocalDbPersistenceTests.cs b/shesha-core/test/Shesha.Tests/DomainModel/LocalDbPersistenceTests.cs index 842edc387a..a737778899 100644 --- a/shesha-core/test/Shesha.Tests/DomainModel/LocalDbPersistenceTests.cs +++ b/shesha-core/test/Shesha.Tests/DomainModel/LocalDbPersistenceTests.cs @@ -1,4 +1,4 @@ -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Xunit; namespace Shesha.Tests.DomainModel diff --git a/shesha-core/test/Shesha.Tests/DomainModel/PersistenceTestsBase.cs b/shesha-core/test/Shesha.Tests/DomainModel/PersistenceTestsBase.cs index 76adfc99ad..79ec926d25 100644 --- a/shesha-core/test/Shesha.Tests/DomainModel/PersistenceTestsBase.cs +++ b/shesha-core/test/Shesha.Tests/DomainModel/PersistenceTestsBase.cs @@ -6,7 +6,7 @@ using Shesha.Extensions; using Shesha.Reflection; using Shesha.Services; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shouldly; using System; using System.Collections.Generic; diff --git a/shesha-core/test/Shesha.Tests/DomainModel/PostgreSqlPersistenceTests.cs b/shesha-core/test/Shesha.Tests/DomainModel/PostgreSqlPersistenceTests.cs index 3b96b7464a..068926d87f 100644 --- a/shesha-core/test/Shesha.Tests/DomainModel/PostgreSqlPersistenceTests.cs +++ b/shesha-core/test/Shesha.Tests/DomainModel/PostgreSqlPersistenceTests.cs @@ -1,4 +1,4 @@ -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Xunit; namespace Shesha.Tests.DomainModel diff --git a/shesha-core/test/Shesha.Tests/DomainModel/SqlServerPersistenceTests.cs b/shesha-core/test/Shesha.Tests/DomainModel/SqlServerPersistenceTests.cs index f3b79b8a2a..6ff923f27b 100644 --- a/shesha-core/test/Shesha.Tests/DomainModel/SqlServerPersistenceTests.cs +++ b/shesha-core/test/Shesha.Tests/DomainModel/SqlServerPersistenceTests.cs @@ -1,4 +1,4 @@ -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Xunit; namespace Shesha.Tests.DomainModel diff --git a/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicDtoModelBinder_Tests.cs b/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicDtoModelBinder_Tests.cs index ec0fd6f18a..2f0ea1cfcb 100644 --- a/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicDtoModelBinder_Tests.cs +++ b/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicDtoModelBinder_Tests.cs @@ -14,7 +14,7 @@ using Shesha.Exceptions; using Shesha.Reflection; using Shesha.Tests.DynamicEntities.Mvc; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shesha.Utilities; using Shouldly; using System; diff --git a/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicDto_Tests.cs b/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicDto_Tests.cs index a473e580f3..700884f51b 100644 --- a/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicDto_Tests.cs +++ b/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicDto_Tests.cs @@ -6,7 +6,7 @@ using Shesha.DynamicEntities.Dtos; using Shesha.Metadata; using Shesha.Tests.DynamicEntities.Dtos; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shouldly; using System; using System.Collections.Generic; @@ -18,11 +18,11 @@ namespace Shesha.Tests.DynamicEntities { [Collection(SqlServerCollection.Name)] public class DynamicDto_Tests : SheshaNhTestBase - { - public DynamicDto_Tests(SqlServerFixture fixture) : base(fixture) - { - } - + { + public DynamicDto_Tests(SqlServerFixture fixture) : base(fixture) + { + } + [Fact] public async Task BuildFullDynamicDto_TestAsync() { @@ -39,7 +39,7 @@ public async Task BuildFullDynamicDto_TestAsync() return Task.FromResult?>(r); }); - var entityConfigStore = LocalIocManager.Resolve(); + var entityConfigStore = LocalIocManager.Resolve(); var fullProxyCacheHolder = LocalIocManager.Resolve(); var dynamicTypeCacheHolder = LocalIocManager.Resolve(); diff --git a/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicEntityTypeBuilder_Test.cs b/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicEntityTypeBuilder_Test.cs index f9486c7930..7c64f45552 100644 --- a/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicEntityTypeBuilder_Test.cs +++ b/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicEntityTypeBuilder_Test.cs @@ -1,7 +1,7 @@ using Abp.Domain.Repositories; using Shesha.Domain; using Shesha.DynamicEntities.EntityTypeBuilder; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using System; using Xunit; diff --git a/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicPropertyManager_Tests.cs b/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicPropertyManager_Tests.cs index 1e742c70d3..9f15e55eef 100644 --- a/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicPropertyManager_Tests.cs +++ b/shesha-core/test/Shesha.Tests/DynamicEntities/DynamicPropertyManager_Tests.cs @@ -9,7 +9,7 @@ using Shesha.Metadata; using Shesha.Reflection; using Shesha.Services; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shouldly; using System; using System.Collections.Generic; diff --git a/shesha-core/test/Shesha.Tests/DynamicEntities/EntityModelBinder_Tests.cs b/shesha-core/test/Shesha.Tests/DynamicEntities/EntityModelBinder_Tests.cs index c9980b57a6..83b37a2eaf 100644 --- a/shesha-core/test/Shesha.Tests/DynamicEntities/EntityModelBinder_Tests.cs +++ b/shesha-core/test/Shesha.Tests/DynamicEntities/EntityModelBinder_Tests.cs @@ -7,7 +7,7 @@ using Shesha.DynamicEntities; using Shesha.DynamicEntities.Binder; using Shesha.Reflection; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/shesha-core/test/Shesha.Tests/DynamicEntities/NumericToEnumTypeConverter_Test.cs b/shesha-core/test/Shesha.Tests/DynamicEntities/NumericToEnumTypeConverter_Test.cs index 160bcd3e75..314598f331 100644 --- a/shesha-core/test/Shesha.Tests/DynamicEntities/NumericToEnumTypeConverter_Test.cs +++ b/shesha-core/test/Shesha.Tests/DynamicEntities/NumericToEnumTypeConverter_Test.cs @@ -1,7 +1,7 @@ using AutoMapper; using Shesha.Domain.Enums; using Shesha.DynamicEntities.Mapper; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using System; using Xunit; diff --git a/shesha-core/test/Shesha.Tests/Fixtures/LocalSqlServerCollection.cs b/shesha-core/test/Shesha.Tests/Fixtures/LocalSqlServerCollection.cs index 38e9cbf8b3..7cb0b6f710 100644 --- a/shesha-core/test/Shesha.Tests/Fixtures/LocalSqlServerCollection.cs +++ b/shesha-core/test/Shesha.Tests/Fixtures/LocalSqlServerCollection.cs @@ -1,10 +1,11 @@ -using Xunit; +using Shesha.Testing.Fixtures; +using Xunit; namespace Shesha.Tests.Fixtures { /// - /// Local Shared Sql Server fixture. - /// Note: This class has no code, just marks the collection + /// xUnit collection definition for local SQL Server fixture (no Docker). + /// Must be in the same assembly as the tests that use it. /// [CollectionDefinition(Name)] public class LocalSqlServerCollection : ICollectionFixture diff --git a/shesha-core/test/Shesha.Tests/Fixtures/PostgreSqlCollection.cs b/shesha-core/test/Shesha.Tests/Fixtures/PostgreSqlCollection.cs index 35134c6e4c..bf6fb28830 100644 --- a/shesha-core/test/Shesha.Tests/Fixtures/PostgreSqlCollection.cs +++ b/shesha-core/test/Shesha.Tests/Fixtures/PostgreSqlCollection.cs @@ -1,10 +1,11 @@ -using Xunit; +using Shesha.Testing.Fixtures; +using Xunit; namespace Shesha.Tests.Fixtures { /// - /// Shared PostgreSql fixture. - /// Note: This class has no code, just marks the collection + /// xUnit collection definition for Testcontainers-based PostgreSQL fixture. + /// Must be in the same assembly as the tests that use it. /// [CollectionDefinition(Name)] public class PostgreSqlCollection : ICollectionFixture diff --git a/shesha-core/test/Shesha.Tests/Fixtures/SqlServerCollection.cs b/shesha-core/test/Shesha.Tests/Fixtures/SqlServerCollection.cs index 8d3f740043..0af4113c1a 100644 --- a/shesha-core/test/Shesha.Tests/Fixtures/SqlServerCollection.cs +++ b/shesha-core/test/Shesha.Tests/Fixtures/SqlServerCollection.cs @@ -1,10 +1,11 @@ -using Xunit; +using Shesha.Testing.Fixtures; +using Xunit; namespace Shesha.Tests.Fixtures { /// - /// Shared Sql Server fixture. - /// Note: This class has no code, just marks the collection + /// xUnit collection definition for Testcontainers-based SQL Server fixture. + /// Must be in the same assembly as the tests that use it. /// [CollectionDefinition(Name)] public class SqlServerCollection : ICollectionFixture diff --git a/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverterBaseTests.cs b/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverterBaseTests.cs index 5d56ceaa46..1c18a6b6ea 100644 --- a/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverterBaseTests.cs +++ b/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverterBaseTests.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json.Linq; using Shesha.JsonLogic; using Shesha.Reflection; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shesha.Tests.TestingUtils; using System; using System.Collections.Generic; diff --git a/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverter_FieldTests.cs b/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverter_FieldTests.cs index a7505e6f61..07b5183c32 100644 --- a/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverter_FieldTests.cs +++ b/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverter_FieldTests.cs @@ -1,6 +1,6 @@ using Shesha.Domain; using Shesha.JsonLogic; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using System; using System.Threading.Tasks; using Xunit; diff --git a/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverter_ValueTests.cs b/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverter_ValueTests.cs index 1a819ce5ae..1b80d6cb75 100644 --- a/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverter_ValueTests.cs +++ b/shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverter_ValueTests.cs @@ -5,7 +5,7 @@ using Shesha.Extensions; using Shesha.JsonLogic; using Shesha.Services; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shesha.Tests.JsonLogic.Models; using Shesha.Utilities; using System; diff --git a/shesha-core/test/Shesha.Tests/Metadata/MetadataProvider_Tests.cs b/shesha-core/test/Shesha.Tests/Metadata/MetadataProvider_Tests.cs index 32fc155e96..b6f942348b 100644 --- a/shesha-core/test/Shesha.Tests/Metadata/MetadataProvider_Tests.cs +++ b/shesha-core/test/Shesha.Tests/Metadata/MetadataProvider_Tests.cs @@ -1,5 +1,5 @@ using Shesha.Metadata; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using System.Linq; using System.Threading.Tasks; using Xunit; diff --git a/shesha-core/test/Shesha.Tests/ModuleHierarchy/ModuleHierarchy_Tests.cs b/shesha-core/test/Shesha.Tests/ModuleHierarchy/ModuleHierarchy_Tests.cs index 131627ce7c..2864a80632 100644 --- a/shesha-core/test/Shesha.Tests/ModuleHierarchy/ModuleHierarchy_Tests.cs +++ b/shesha-core/test/Shesha.Tests/ModuleHierarchy/ModuleHierarchy_Tests.cs @@ -1,6 +1,6 @@ using Abp.Domain.Uow; using Shesha.ConfigurationItems; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shesha.Tests.ModuleA; using Shesha.Tests.ModuleB; using Shesha.Web.FormsDesigner.Services; diff --git a/shesha-core/test/Shesha.Tests/Notifications/TemplateDataEntitySerialization_Tests.cs b/shesha-core/test/Shesha.Tests/Notifications/TemplateDataEntitySerialization_Tests.cs index c31c76c5b8..458edcc641 100644 --- a/shesha-core/test/Shesha.Tests/Notifications/TemplateDataEntitySerialization_Tests.cs +++ b/shesha-core/test/Shesha.Tests/Notifications/TemplateDataEntitySerialization_Tests.cs @@ -4,7 +4,7 @@ using Shesha.Domain; using Shesha.EntityReferences; using Shesha.Reflection; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shesha.Utilities; using Shouldly; using Stubble.Core.Builders; diff --git a/shesha-core/test/Shesha.Tests/Otp/OtpManager_Tests.cs b/shesha-core/test/Shesha.Tests/Otp/OtpManager_Tests.cs index 55eaf3ee9b..bdc950d32e 100644 --- a/shesha-core/test/Shesha.Tests/Otp/OtpManager_Tests.cs +++ b/shesha-core/test/Shesha.Tests/Otp/OtpManager_Tests.cs @@ -5,7 +5,7 @@ using Shesha.Otp.Configuration; using Shesha.Otp.Dto; using Shesha.Sms; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shouldly; using System; using System.Collections.Generic; diff --git a/shesha-core/test/Shesha.Tests/QuickSearch/QuickSearcher_Tests.cs b/shesha-core/test/Shesha.Tests/QuickSearch/QuickSearcher_Tests.cs index e71482f724..737b83e62e 100644 --- a/shesha-core/test/Shesha.Tests/QuickSearch/QuickSearcher_Tests.cs +++ b/shesha-core/test/Shesha.Tests/QuickSearch/QuickSearcher_Tests.cs @@ -13,7 +13,7 @@ using Shesha.QuickSearch; using Shesha.QuickSearch.Cache; using Shesha.Services; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using System; using System.Collections.Generic; using System.Linq; diff --git a/shesha-core/test/Shesha.Tests/Settings/SettingsCache_Tests.cs b/shesha-core/test/Shesha.Tests/Settings/SettingsCache_Tests.cs index 58417616e1..eb2b4ce69b 100644 --- a/shesha-core/test/Shesha.Tests/Settings/SettingsCache_Tests.cs +++ b/shesha-core/test/Shesha.Tests/Settings/SettingsCache_Tests.cs @@ -6,7 +6,7 @@ using Shesha.Services.Settings; using Shesha.Services.Settings.Cache; using Shesha.Settings; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shesha.Utilities; using System; using System.Collections.Generic; diff --git a/shesha-core/test/Shesha.Tests/Shesha.Tests.csproj b/shesha-core/test/Shesha.Tests/Shesha.Tests.csproj index bceefd9e40..6ef2630182 100644 --- a/shesha-core/test/Shesha.Tests/Shesha.Tests.csproj +++ b/shesha-core/test/Shesha.Tests/Shesha.Tests.csproj @@ -44,6 +44,7 @@ + @@ -55,20 +56,20 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive - - all - runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive - - all - runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -90,14 +91,14 @@ Always - - Always + + Always - - + + \ No newline at end of file diff --git a/shesha-core/test/Shesha.Tests/SheshaNhTestBase.cs b/shesha-core/test/Shesha.Tests/SheshaNhTestBase.cs index 2115f3ff1d..2c783c3d00 100644 --- a/shesha-core/test/Shesha.Tests/SheshaNhTestBase.cs +++ b/shesha-core/test/Shesha.Tests/SheshaNhTestBase.cs @@ -1,233 +1,16 @@ -using Abp; -using Abp.Authorization.Users; -using Abp.Dependency; -using Abp.Domain.Uow; -using Abp.Modules; -using Abp.MultiTenancy; -using Abp.Runtime.Session; -using NHibernate; -using NHibernate.Linq; -using Shesha.Authorization.Users; -using Shesha.Domain; -using Shesha.MultiTenancy; -using Shesha.NHibernate.UoW; -using Shesha.Tests.Fixtures; -using System; -using System.Linq; -using System.Threading.Tasks; +using Shesha.Testing; +using Shesha.Testing.Fixtures; namespace Shesha.Tests { - public abstract class SheshaNhTestBase : ShaIntegratedTestBase where TStartupModule : AbpModule + /// + /// Convenience base class for Shesha framework integration tests. + /// Binds to so tests don't have to specify the type parameter. + /// + public abstract class SheshaNhTestBase : SheshaNhTestBase { protected SheshaNhTestBase(IDatabaseFixture fixture) : base(fixture) { - LoginAsHostAdmin(); - - EntityHelper.RefreshStore(LocalIocManager); - } - - #region UsingDbSession - - protected IDisposable UsingTenantId(int? tenantId) - { - var previousTenantId = AbpSession.TenantId; - AbpSession.TenantId = tenantId; - return new DisposeAction(() => AbpSession.TenantId = previousTenantId); - } - - protected void UsingDbSession(Action action) - { - UsingDbSession(AbpSession.TenantId, action); - } - - protected Task UsingDbSessionAsync(Func action) - { - return UsingDbSessionAsync(AbpSession.TenantId, action); - } - - protected T UsingDbSession(Func func) - { - return UsingDbSession(AbpSession.TenantId, func); - } - - protected Task UsingDbSessionAsync(Func> func) - { - return UsingDbSessionAsync(AbpSession.TenantId, func); - } - - protected void UsingDbSession(int? tenantId, Action action) - { - using (UsingTenantId(tenantId)) - { - using (var session = OpenSession()) - { - action(session); - } - } - } - - private ISession OpenSession() - { - return LocalIocManager.Resolve().OpenSession(); - } - - protected async Task UsingDbSessionAsync(int? tenantId, Func action) - { - using (UsingTenantId(tenantId)) - { - using (var session = OpenSession()) - { - await action(session); - await session.FlushAsync(); - } - } - } - - protected T UsingDbSession(int? tenantId, Func func) - { - T result; - - using (UsingTenantId(tenantId)) - { - using (var session = OpenSession()) - { - result = func(session); - session.Flush(); - } - } - - return result; - } - - protected async Task UsingDbSessionAsync(int? tenantId, Func> func) - { - T result; - - using (UsingTenantId(tenantId)) - { - using (var session = OpenSession()) - { - result = await func(session); - await session.FlushAsync(); - } - } - - return result; - } - - #endregion - - #region Login - - protected void LoginAsHostAdmin() - { - LoginAsHost(AbpUserBase.AdminUserName); - } - - protected void LoginAsDefaultTenantAdmin() - { - LoginAsTenant(AbpTenantBase.DefaultTenantName, AbpUserBase.AdminUserName); - } - - protected void LoginAsHost(string userName) - { - AbpSession.TenantId = null; - - var user = - UsingDbSession( - session => - session.Query().FirstOrDefault(u => u.TenantId == AbpSession.TenantId && u.UserName == userName)); - if (user == null) - { - throw new Exception("There is no user: " + userName + " for host."); - } - - AbpSession.UserId = user.Id; - } - - protected void LoginAsTenant(string tenancyName, string userName) - { - var tenant = UsingDbSession(session => session.Query().FirstOrDefault(t => t.TenancyName == tenancyName)); - if (tenant == null) - { - throw new Exception("There is no tenant: " + tenancyName); - } - - AbpSession.TenantId = tenant.Id; - - var user = - UsingDbSession( - session => - session.Query().FirstOrDefault(u => u.TenantId == AbpSession.TenantId && u.UserName == userName)); - if (user == null) - { - throw new Exception("There is no user: " + userName + " for tenant: " + tenancyName); - } - - AbpSession.UserId = user.Id; - } - - #endregion - - /// - /// Gets current user if is not null. - /// Throws exception if it's null. - /// - protected Task GetCurrentUserAsync() - { - var userId = AbpSession.GetUserId(); - return UsingDbSession(session => session.Query().SingleAsync(u => u.Id == userId)); - } - - /// - /// Gets current tenant if is not null. - /// Throws exception if there is no current tenant. - /// - protected Task GetCurrentTenantAsync() - { - var tenantId = AbpSession.GetTenantId(); - return UsingDbSession(session => session.Query().SingleAsync(t => t.Id == tenantId)); - } - - protected void UsingNhSession(Action action) - { - using (var uow = NewNhUnitOfWork()) - { -#pragma warning disable IDISP001 // Dispose created - var session = uow.GetSession(); -#pragma warning restore IDISP001 // Dispose created - action.Invoke(session); - uow.Complete(); - } - } - - protected NhUnitOfWork NewNhUnitOfWork() - { - var unitOfWorkManager = Resolve(); - return unitOfWorkManager.Begin() is NhUnitOfWork nhuow - ? nhuow - : throw new Exception($"Unexpected type of UnitOfWork. Expected '{nameof(NhUnitOfWork)}'"); - } - - protected virtual async Task WithUnitOfWorkAsync(Func> action, UnitOfWorkOptions? options = null) - { - using (var uowManager = LocalIocManager.ResolveAsDisposable()) - { - using (var uow = uowManager.Object.Begin(options ?? new UnitOfWorkOptions())) - { - var result = await action(); - await uow.CompleteAsync(); - return result; - } - } - } - } - - public abstract class SheshaNhTestBase : SheshaNhTestBase - { - protected SheshaNhTestBase(IDatabaseFixture fixture) : base(fixture) - { } } } diff --git a/shesha-core/test/Shesha.Tests/SheshaTestModule.cs b/shesha-core/test/Shesha.Tests/SheshaTestModule.cs index 858a5fb399..c02c074f3a 100644 --- a/shesha-core/test/Shesha.Tests/SheshaTestModule.cs +++ b/shesha-core/test/Shesha.Tests/SheshaTestModule.cs @@ -1,51 +1,36 @@ using Abp; using Abp.AspNetCore; -using Abp.AutoMapper; using Abp.Castle.Logging.Log4Net; using Abp.Configuration; -using Abp.Configuration.Startup; -using Abp.Dependency; -using Abp.Domain.Uow; using Abp.Modules; -using Abp.Net.Mail; using Abp.TestBase; -using Abp.Zero.Configuration; using Castle.Facilities.Logging; -using Castle.MicroKernel.Registration; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.Extensions.Configuration; -using Shesha.Configuration.Startup; -using Shesha.FluentMigrator; using Shesha.Modules; using Shesha.NHibernate; -using Shesha.Services; -using Shesha.Tests.DependencyInjection; -using Shesha.Tests.Fixtures; +using Shesha.Testing; using Shesha.Tests.ModuleA; using Shesha.Tests.ModuleB; using Shesha.Web.FormsDesigner; -using System; -using System.Collections.Generic; using System.Reflection; namespace Shesha.Tests { [DependsOn( - + typeof(AbpKernelModule), typeof(AbpTestBaseModule), typeof(AbpAspNetCoreModule), typeof(SheshaFormsDesignerModule), typeof(SheshaApplicationModule), - typeof(SheshaFrameworkModule), + typeof(SheshaFrameworkModule), typeof(SheshaNHibernateModule), - typeof(SheshaTestsModuleA), + typeof(SheshaTestsModuleA), typeof(SheshaTestsModuleB) )] public class SheshaTestModule : SheshaModule - { - public const string ModuleName = "Shesha.Tests"; + { + public const string ModuleName = "Shesha.Tests"; public override SheshaModuleInfo ModuleInfo => new SheshaModuleInfo(ModuleName) { FriendlyName = "Shesha Tests", @@ -61,83 +46,20 @@ public SheshaTestModule(SheshaNHibernateModule nhModule, SheshaFrameworkModule f } public override void PreInitialize() - { - IocManager.MockWebHostEnvirtonment(); - - var nhConfig = Configuration.Modules.ShaNHibernate(); - - var dbFixture = IocManager.IsRegistered() - ? IocManager.Resolve() - : null; - if (dbFixture != null) - { - nhConfig.UseDbms(c => dbFixture.DbmsType, c => dbFixture.ConnectionString); - } - else - { - var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); - nhConfig.UseDbms(c => config.GetDbmsType(), c => config.GetRequiredConnectionString("TestDB")); - } - - Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(30); - Configuration.UnitOfWork.IsTransactional = false; - - // Disable static mapper usage since it breaks unit tests (see https://github.com/aspnetboilerplate/aspnetboilerplate/issues/2052) - Configuration.Modules.AbpAutoMapper().UseStaticMapper = false; - - Configuration.BackgroundJobs.IsJobExecutionEnabled = false; - - // mock IWebHostEnvironment - //var hostingEnvironment = Mock.Of(e => e.ApplicationName == "test"); - - var inMemorySettings = new Dictionary { - /* in memory settings: - {"TopLevelKey", "TopLevelValue"}, - {"SectionName:SomeKey", "SectionValue"}, - */ - }; - - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(inMemorySettings) - .Build(); - - IocManager.IocContainer.Register( - Component.For() - .Instance(configuration) - .LifestyleSingleton() - ); - - IocManager.MockApiExplorer(); - - // Use database for language management - Configuration.Modules.Zero().LanguageManagement.EnableDbLocalization(); - - IocManager.RegisterFakeService(); - - Configuration.ReplaceService(DependencyLifeStyle.Transient); - - // replace email sender - Configuration.ReplaceService(DependencyLifeStyle.Transient); - - // replace connection string resolver - //Configuration.ReplaceService(DependencyLifeStyle.Transient); - - Configuration.ReplaceService(DependencyLifeStyle.Singleton); + { + this.ConfigureForTesting(IocManager, "appsettings.json"); + // Framework-specific: enable entity history for testing (ConfigureForTesting disables it) + Configuration.EntityHistory.IsEnabled = true; Configuration.EntityHistory.Selectors.Add("Settings", typeof(Setting)); + } - if (!IocManager.IsRegistered()) - IocManager.IocContainer.Register(Component.For().ImplementedBy()); - } - public override void Initialize() { var thisAssembly = Assembly.GetExecutingAssembly(); IocManager.RegisterAssemblyByConvention(thisAssembly); IocManager.IocContainer.AddFacility(f => f.UseAbpLog4Net().WithConfig("log4net.config")); - - ServiceCollectionRegistrar.Register(IocManager); } } -} \ No newline at end of file +} diff --git a/shesha-core/test/Shesha.Tests/Specifications/Specifications_Tests.cs b/shesha-core/test/Shesha.Tests/Specifications/Specifications_Tests.cs index 52b098e22b..145033dc27 100644 --- a/shesha-core/test/Shesha.Tests/Specifications/Specifications_Tests.cs +++ b/shesha-core/test/Shesha.Tests/Specifications/Specifications_Tests.cs @@ -6,7 +6,7 @@ using Shesha.NHibernate; using Shesha.NHibernate.Repositories; using Shesha.Specifications; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shouldly; using System; using System.Collections.Generic; diff --git a/shesha-core/test/Shesha.Tests/Users/UserAppService_Tests.cs b/shesha-core/test/Shesha.Tests/Users/UserAppService_Tests.cs index d5d392ee8a..028a21f8b7 100644 --- a/shesha-core/test/Shesha.Tests/Users/UserAppService_Tests.cs +++ b/shesha-core/test/Shesha.Tests/Users/UserAppService_Tests.cs @@ -12,7 +12,7 @@ using Shesha.Models.TokenAuth; using Shesha.Otp; using Shesha.Otp.Dto; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shesha.Users; using Shesha.Users.Dto; using Shouldly; diff --git a/shesha-core/test/Shesha.Tests/VersionedFields/VersionedField_Tests.cs b/shesha-core/test/Shesha.Tests/VersionedFields/VersionedField_Tests.cs index a67419cfe6..333ff6788e 100644 --- a/shesha-core/test/Shesha.Tests/VersionedFields/VersionedField_Tests.cs +++ b/shesha-core/test/Shesha.Tests/VersionedFields/VersionedField_Tests.cs @@ -2,7 +2,7 @@ using Abp.Domain.Uow; using Shesha.Domain; using Shesha.Services.VersionedFields; -using Shesha.Tests.Fixtures; +using Shesha.Testing.Fixtures; using Shouldly; using System; using System.Threading.Tasks;