Commit graph

15 commits

Author SHA1 Message Date
Sipke Schoorstra 74123110d5
feat(auth)!: structured authorization model, phases 1-6 (#7980)
* feat(auth): add the permission model and evaluator (Phase 1)

Additive only. Nothing changes behavior: no endpoint declares against this
yet, and no existing enforcement path routes through it.

A permission is {resource}:{verb}, both axes open and string-keyed. A
trailing wildcard on the resource axis matches the named node and every
descendant at any depth, so workflows/definitions/* covers
workflows/definitions itself; * on the verb axis matches any verb.
Wildcards are the only construct with forward reach.

A bare * parses to *:* at parse time rather than being special-cased in
the evaluator, so superuser stays an ordinary grant and a stored or seeded
* keeps authorizing across the vocabulary migration without a lock-out
window.

Adds:
- Permission, with parsing that rejects a value containing a comma, since
  the persistence converter joins collections with one
- CoreVerbs, the recommended set modules should reuse; a convention rather
  than a closed vocabulary
- PermissionMatcher, one matching rule shape on both axes
- IPermissionEvaluator, the single place permission decisions are made,
  skipping malformed claims so one bad stored grant cannot deny a principal
- PermissionRequirement and PermissionAuthorizationHandler
- The descriptor catalog in core: PermissionDescriptor now carries the
  verbs a resource supports and marks non-core ones, and the registry can
  report what a wildcard covers today

External Authentication keeps its own descriptor types for now; it moves to
the core catalog with the other modules in Phase 2, which keeps this change
purely additive.

55 unit tests cover the matcher table, wildcard forward reach, the
counterpart that concrete grants stay frozen, absence-is-denial, and the
seeded * case.

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(auth): contribute the permission catalog from every module (Phase 2)

Still additive. Existing endpoints keep their legacy declarations; nothing
changes behavior for them.

Every module exposing protected endpoints now declares its resources and
the verbs each accepts, following the pattern already proven in External
Authentication -- constants and descriptors colocated -- refined to one
constant per resource, with the verb supplied separately. 47 resources
across 15 modules, matching the settled vocabulary.

Descriptors are discovered from the same assemblies as a module's
endpoints, in AddFastEndpointsFromModule. Registering them per module
would let the catalog and the endpoints drift, which is the failure this
model exists to remove; tying them to one registration makes the catalog
necessarily describe the endpoints that exist.

Adds:
- GET /identity/permissions, the catalog a role editor renders from, so
  no client hard-codes permission strings
- GET /identity/permissions/reach, reporting what a wildcard covers today.
  This is the mitigation for forward reach on the resource axis: a
  wildcard is useful precisely because it covers things that do not exist
  yet, so an author needs to see what it reaches now
- GET /identity/me/permissions, resolving wildcards to concrete verbs so a
  client needs no matching logic, and listing denied resources with an
  empty verb list so "denied" is distinguishable from "unknown"
- IPermissionGrantValidator, wired into Roles/Create and Roles/Update,
  which previously persisted request.Permissions after only the
  caller-subset check. Concrete segments validate against the catalog;
  wildcards validate structurally and are accepted even when they match
  nothing today, since installing a module later is what gives such a
  grant meaning
- RequirePermission(resource, verb) and RequireAuthenticatedOnly() on the
  endpoint base classes, with the six copy-pasted ConfigurePermissions
  bodies collapsed into one implementation

New endpoints require new-format grants, so during the transition they
authorize only for holders of *, which parses to *:*. Phase 3 migrates the
rest and closes that gap.

70 unit tests, including the wildcard-accepting validator cases.

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(auth)!: cut every endpoint over to the permission model (Phase 3)

BREAKING: legacy permission strings no longer authorize. A permanent alias
layer would keep two vocabularies valid forever, so the break is
deliberate and reported rather than absorbed. `*` survives unchanged --
it parses to `*:*` -- so an administrator cannot be locked out while
roles are re-authored.

All 168 declaration call sites across 151 files now use
RequirePermission(resource, verb) with the constants their module
declares, so a typo is a compile error rather than an unreachable
endpoint.

Enforcement consolidated onto IPermissionEvaluator:
- RoleAuthorizationService evaluates containment through the evaluator
  rather than by set membership. This matters: a caller holding
  workflows/*:view can now delegate workflows/definitions:view, which set
  membership got wrong and which would otherwise force administrators to
  hold every concrete grant they wish to delegate.
- The two Broker/Logout.cs endpoints declare explicitly. Logout is
  authenticated-only; ContinueLogout is anonymous, matching every other
  broker callback -- the route handle carries the authority and a
  top-level browser navigation sends no Authorization header.

Removes the C#/Python expression permissions (#7975). They conflated an
incoherent execution-side gate -- a workflow runs under the server's
authority, not the caller's, so the check never constrained what a script
could do -- with a meaningful authoring-side one. The host switch
(AllowHostCodeExecution) becomes the single control. This is a deliberate
reduction in control: where host code is enabled, any author who may write
definitions may use C# and Python.

Adds the fail-closed gate. Omitting a declaration previously inherited the
FastEndpoints default with no Elsa-level fallback, so an endpoint could
ship ungated unnoticed. EndpointCoverage asserts every endpoint declares
exactly one of RequirePermission, RequireAuthenticatedOnly or
AllowAnonymous, with no exemption list. Its canary assertion earned its
keep immediately by catching that the gate was scanning an assembly
containing no endpoints.

EndpointPermissionRegistry records what each endpoint declares. The
requirement is attached as an inline policy and is not readable back from
the definition, so this keeps the declaration introspectable -- and lets
tests assert a specific requirement rather than merely that one exists.

Two behavior notes worth calling out:
- The runtime status endpoint previously accepted either the read or the
  manage permission. It now requires workflows/runtime:view alone, which
  is least privilege; a role holding only control must also be granted
  view to read status.
- BPMN interchange repeats the workflow-definitions path locally rather
  than taking a dependency on Elsa.Workflows.Api for one constant. It
  contributes no descriptor: the resource is owned and described by
  Workflows.Api, and the registry keeps one entry per resource.

Also adds a startup validator that logs every stored role permission that
no longer resolves, identified by role, so an upgrade is loud.

188 unit tests pass across Api.Common, Workflows.Api and Identity.

Refs #7974, #7975, #7976

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(auth): revocation bound and role audit notifications (Phase 4)

Default access-token lifetime drops from 1 hour to 15 minutes. This is
the revocation bound: permission claims are issued at sign-in and refresh
re-reads the user's roles, so removing a role takes effect at most one
access-token lifetime later. Refresh already rotates both tokens, so no
client change is required and the refresh lifetime is unchanged.

Adds an optional permission stamp for deployments needing a tighter
bound. The stamp is derived from the user's roles and their permissions
rather than stored as a counter on the user. That avoids changing the
Identity schema, which would have required migrations across all five EF
providers and made this milestone depend on the tenancy work. It also
means every node computes the same value from the same store with no
cross-node cache invalidation, which matters because Elsa has none.

The stamp is issued unconditionally and only validated when enabled, so
turning it on does not invalidate tokens already in flight; an absent
stamp is not treated as a mismatch for the same reason. It changes when a
role is added to or removed from the user and when a held role's
permissions change, but not when an unrelated role changes.

Role create and update now publish typed security notifications per ADR
0007, carrying the resulting grants so a reviewer can reconstruct what a
role conferred at a point in time without replaying every prior event.
This module owns no audit store: a future audit module subscribes and
sets its own retention.

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(auth): tenancy hardening for identity (Phase 5)

Closes the gaps that made "roles are configurable per tenant" untrue
however the rest of the stack behaved.

Uniqueness becomes per tenant. User.Name, Role.Name, Application.Name and
Application.ClientId carried globally unique indexes, so two tenants could
not both hold a role named Admin. Migrations for all five EF providers
drop the global indexes and create composite ones on (TenantId, Name).

The in-memory user and role stores now scope to the ambient tenant.
Isolation previously existed only on the Entity Framework path, and only
when multitenancy was enabled, so a deployment running the default stores
had none at all. The tenant-agnostic sentinel is honored, matching the EF
query filter, so a shared platform role stays visible from every tenant.

RoleFilter gains TenantId, matching UserFilter, and the role and user list
endpoints pass it explicitly rather than relying on an ambient filter that
only exists on one persistence path.

UserManager.CreateUserAsync sets TenantId explicitly instead of relying on
the EF saving handler, which does not run in memory and left users
unassigned there.

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs: migration guide, ADR, and security wiki for the authorization model

Adds docs/migrations/authorization-model.md, following the shape of the
external-authentication persistence guide. It leads with the three things
that are not a simple rename, because each silently produces a wrong
result if treated as one:

- The migration expands where new sub-resources are finer-grained than
  what they replace, so a one-for-one substitution narrows roles.
- read:* and exec:* become materially more powerful. They are literal
  claim values today, authorizing twelve of roughly forty read endpoints;
  their replacements work as the names always implied. Any role holding
  them needs review by hand, not an automated rewrite.
- The C#/Python expression permissions are removed rather than
  translated, which is a deliberate reduction in control where host code
  is enabled.

It also states plainly that `*` keeps working, and says to do that first,
since it is what stops an instance locking itself out mid-migration.

ADR 0012 records the model and, more usefully, why a closed verb
enumeration was drafted and rejected: it was justified on implication, but
aggregates were already excluded and no verb implies another, so the
bitwise check was expressing set containment all along.

The security wiki's API Authorization section replaces its Secrets-only
route table with the catalog endpoint as the authoritative source, and
states why read-only mode is a separate axis rather than a permission.

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(auth): restore the suites after the tenancy and evaluator changes

The whole solution builds with zero errors and every affected suite
passes: 70 Api.Common, 23 Workflows.Api, 95 Identity, 154 External
Authentication unit, 133 External Authentication integration.

Most breakage was test call sites constructing the tenant-aware stores
and the evaluator-backed RoleAuthorizationService directly. Adds
TestTenantAccessor to Elsa.Testing.Shared rather than giving the
production constructors an optional accessor, which would have let a
missing registration silently disable isolation.

Several External Authentication tests created fixtures in tenant-a while
running under the default tenant, so the newly isolating store correctly
stopped finding them. They are now scoped to the tenant their own
fixtures use; JustInTimeProvisioningTests, which genuinely spans two
tenants, is scoped per case.

One production fix came out of it: IdentityFeature now ensures an
ITenantAccessor with TryAdd. The identity stores are tenant-scoped, so a
host that never enables multitenancy would otherwise fail to construct
them -- which is what the DI registration tests were reporting. TryAdd
leaves MultitenancyFeature's own registration untouched.

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(auth): register the identity services on the classic feature path

Found by running Elsa.Server.Web, not by the test suites: the app failed
at startup with "Unable to resolve service for type RoleSecurityNotifier
while attempting to activate Roles.Update".

RoleSecurityNotifier, the permission stamp services, the memory cache and
the stored-permission validator were registered only in the CShells shell
feature. Elsa.Server.Web uses the classic UseIdentity() path, whose
IdentityFeature registered none of them, so every host on that path
crashed while mapping endpoints. Unit tests did not catch it because they
construct services directly rather than through either feature.

Verified end to end against the running server:

- The seeded admin role stores "*". It parsed to *:* and resolved to
  concrete verbs across all 27 registered resources, which is the
  bare-wildcard parse rule working on real data rather than in a test.
- GET /identity/permissions returns the catalog for the modules this app
  installs -- 27 resources, 0 unverified, categories Dashboard, Identity,
  Resilience and Workflows -- rather than all 47, which is correct: the
  catalog describes what is installed.
- GET /identity/permissions/reach?resource=workflows/* reports 19 covered
  resources.
- A role holding only dashboard:view gets 200 on /dashboard/overview and
  403 on /identity/roles, /identity/users, /workflow-definitions and
  /identity/permissions, while /identity/me/permissions returns 200
  because it declares RequireAuthenticatedOnly -- confirming FR-019's
  third declaration state behaves as designed.
- That same principal's /me/permissions lists all 27 resources with 26
  carrying an empty verb list, so "denied" stays distinguishable from
  "unknown to this server".
- The startup validator logged no unresolvable permissions, as expected
  for a seed holding only "*".

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(auth): discover permission descriptors on the shell host path

Found by running Elsa.ModularServer.Web. The shell host started cleanly
and authorized correctly, but GET /identity/permissions returned zero
resources and /identity/me/permissions returned no grants.

Descriptor discovery was wired into AddFastEndpointsFromModule, which only
the classic module path calls. CShells discovers endpoints from features
implementing its own marker interface, so on a shell host no provider was
ever registered. Authorization still worked, because the evaluator reads
claims and needs no descriptors -- which is exactly why nothing failed
loudly. What silently broke was everything built on the catalog: role
authoring would have rejected every concrete grant as an unknown
resource, introspection returned nothing for clients to render, and the
stored-permission validator would have reported every concrete stored
permission as unresolvable.

ElsaFastEndpointsFeature now contributes descriptors from the loaded Elsa
assemblies, bounded to those and run once per shell.

Verified on the modular host, which installs far more modules than
Elsa.Server.Web:

- 47 resources registered, 0 unverified, across all 12 categories, with
  all 17 module-specific verbs present. That is the entire published
  vocabulary confirmed against a running server rather than a document.
- Reach reports workflows/* covering 20, external-authentication/*
  covering 8, and * covering 47.
- Creating a role with dashboard:view and workflows/*:view succeeds,
  confirming a wildcard grant survives authoring validation.
- Creating one with invented/resource:view and secrets:publish is
  rejected with 400.

Also makes those rejections actionable. The permission was reported
without the reason, so an operator learned which entry was wrong but not
why; both parts are now in the message, including the supported verbs for
the resource.

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* wip: bpmn test vocabulary

* fix(auth): make enforcement DI-independent and finish the hub cutover

CI on #7980 was red. Running the full suite locally rather than the
subset I had been checking surfaced 24 failures across four projects,
in three distinct classes.

Enforcement no longer depends on a DI registration. RequirePermission
attached a PermissionRequirement evaluated by a registered handler, so a
host that had not called AddElsaAuthorization got 403 on every endpoint
with nothing to indicate why. Several test hosts wire FastEndpoints
directly and did exactly that. The requirement is now evaluated inline
against a shared stateless evaluator, with a host-registered
IPermissionEvaluator still taking precedence. Registration remains
worthwhile for the catalog and the validator; authorization can no longer
silently fail closed because of a missing one.

Registration also moved from AddFastEndpointsFromModule to
AddFastEndpointsAssembly. Registering an endpoint assembly is what should
guarantee its permissions work, and a host may never call the former.

Finishes T039. The four SignalR hubs still matched hard-coded legacy
permission strings, which no longer exist, so every hub denied access.
They now route through the evaluator like every other enforcement path.

Test fixtures granting legacy strings were updated to the new vocabulary.
Two categories were deliberately left alone: naming tests asserting the
legacy constants still hold their old values, which is true and worth
keeping, and the workflow script authorization tests, which asserted a
MissingPermission outcome that D21 removed -- those now assert the host
switch is the only control.

One test previously pinned that the hub honors a FastEndpoints-configured
permissions claim type. It now asserts the opposite, and says why: Elsa is
the only authority that expands roles into permission claims (ADR 0009),
and this model no longer uses the FastEndpoints permission mechanism, so
its separately configurable claim type is not consulted. That property is
also unreadable outside reflection.

Whole solution builds with 0 errors and every test project passes.

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(auth): scope the permission-stamp cache to the tenant

Greptile found and reproduced a cross-tenant authorization bug, and it was
mine: Phase 5 made user names unique per tenant rather than globally, but
PermissionStampValidator kept caching by user name alone. Tenant A's
lookup could therefore populate the cache with its own stamp and satisfy a
revoked token belonging to a same-named user in tenant B, without ever
resolving tenant B's user.

Both the cache key and the user lookup are now tenant-scoped. Added
PermissionStampValidatorTests, including the cross-tenant case; verified it
fails without the fix and passes with it.

Also from review:
- Removed the legacy permission constants left unused in the three hubs
  after they moved to the evaluator, so no stale vocabulary lingers.
- Narrowed two generic catch clauses. The IL scanner now catches only the
  exceptions an unresolvable metadata token actually throws, and the
  startup validator rethrows cancellation while still refusing to stop the
  host for anything else -- an unreachable or half-migrated store is
  exactly when an operator most needs the host up.

Whole solution builds with 0 errors and every test project passes.

Refs #7974

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(docs): correct path in log message for authorization model migration link

Aligns the log message path to the correct documentation directory, changing `docs` to `doc` to avoid confusion and incorrect linking during log output.

* fix(auth): update permissions method to use new syntax

* docs: consolidate docs/ into doc/

The repository had two documentation roots. Merge docs/ into doc/ and
remove the empty docs/ tree.

The two adr/ folders both numbered from 0001, so the identity and
authorization series is renumbered to continue the core series rather
than collide with it:

  docs/adr/0001-0012 -> doc/adr/0014-0025

Every reference is updated to match: the Status cross-links between the
renumbered ADRs, the ADR and path links in specs/012-external-authentication
and specs/013-rbac-authorization-model, and doc/wiki/identity-tenancy-security.md.

doc/adr/toc.md gains entries 14-25. doc/adr/graph.dot is regenerated out
to 25; it had been stale since ADR 10 and now also carries the partial
supersession edges declared by the ADRs themselves.

docs/codebase/ and docs/migrations/ move across unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(auth): simplify syntax in PermissionEvaluator and related classes

Streamlined syntax for method definitions by using expression-bodied members and simplified object instantiations across the Authorization module. This includes adjustments in `PermissionEvaluator`, `LocalHostRequirement`, and `WebApplicationExtensions` for better readability and maintainability.

* ci(bounty): point the footer step at the file's real path

The bounty workflow read docs/bounty-footer.md, the path the file had
when the workflow was added in b421b00e1. The file later moved to
doc/bounty/bounty-footer.md and the workflow was never updated, so the
read step has been resolving nothing and the appended comment was empty.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 23:44:55 +02:00
lukhipolito-nexxbiz fcd0b5ae9a
Feat/unit test coverage sendhttprequest (#6961)
* Unit test coverage for SendHttpRequest + useful extensions

* Update src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update test/unit/Elsa.Activities.UnitTests/HTTP/SendHttpRequestTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update test/unit/Elsa.Activities.UnitTests/HTTP/SendHttpRequestTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Improvements on unit test and documentation for http

* Update doc/qa/test-guidelines.md

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* Improvements on maintainability of sendhttprequest unit tests

* Improving tests and scheduled activity evaluation for activity context

* Refactor and splitting unnecessary grouped tests

* Improvements on SendHttp Unit tests

* Improvements on tests

* Update test/unit/Elsa.Activities.UnitTests/HTTP/SendHttpRequestTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Small suggestions from copilot

* Update test/unit/Elsa.Activities.UnitTests/HTTP/SendHttpRequestTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* small copilot suggestion

* Introduce Scheduler Strategy Interfaces and Implementations for Workflow and Activity Execution Contexts (#6984)

* Introduce Scheduler Strategy Interfaces and Implementations for Workflow and Activity Execution Contexts

- Added `IWorkflowExecutionContextSchedulerStrategy` and `IActivityExecutionContextSchedulerStrategy` interfaces.
- Implemented `WorkflowExecutionContextSchedulerStrategy` and `ActivityExecutionContextSchedulerStrategy` for scheduling activities in workflows.
- Refactored scheduling logic to utilize the new scheduler strategies.
- Updated unit tests and test helpers to reflect refactoring, introducing fake implementations for testing purposes.
- Adjusted background execution scheduling and improved extensibility for custom scheduler strategies.

* Refactor `SendHttpRequestTests`: simplify scheduling assertions, use shared extensions, and standardize method naming. Streamline helper methods and remove unused test logic.

* Refactor: Replace `ActivityTestHelper` with `ActivityTestFixture` in unit tests for streamlined activity testing

- Introduced `ActivityTestFixture` with a fluent API for better test setup and execution of activities.
- Added extension methods `ActivityTestFixtureExtensions` and `ActivityTestFixtureHttpExtensions` for configuring attributes and HTTP services.
- Updated test guidelines and unit tests to use the new fixture and extensions.
- Removed `ActivityTestHelper`.

* Refactor: Move `ActivityTestFixture` and related extensions to shared project for reuse across test suites

- Consolidated `ActivityTestFixture`, `ActivityTestFixtureExtensions`, and `ActivityTestFixtureHttpExtensions` into `Elsa.Testing.Shared`.
- Updated namespaces and imports across unit tests to reflect new structure.
- Enhanced `AssertActivityAttributes` and added fluent configuration APIs.
- Adjusted `Directory.Packages.props` with new dependencies, including `NSubstitute` and `xunit.assert`.

* Refactor `SetVariableTests`: inline `ActivityTestFixture` initialization to simplify test setup.

* Refactor `ActivityTestFixture`: eliminate redundant field `_services`, add `UsedImplicitly` attributes, and improve service collection management

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add XML documentation for `ActivityExecutionContextExtensions`, detailing methods and parameters.

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add XML documentation for scheduler strategies and their methods

- Updated `IActivityExecutionContextSchedulerStrategy` and `IWorkflowExecutionContextSchedulerStrategy` interfaces with XML summaries for methods.
- Added XML documentation to implementations (`ActivityExecutionContextSchedulerStrategy`, `WorkflowExecutionContextSchedulerStrategy`) and test fakes for clarity.

* Refactor `WriteLineTests`: consolidate duplicate test logic, simplify setup with shared helper method, and enhance readability in assertions.

* Refactor `WriteLineTests` and `SetVariableTests`: replace `WriteLineAsync` assertions with synchronous `WriteLine`, streamline exception recording in `SetVariableTests`, and remove unused imports.

* Add `ParallelTests` for unit and integration testing with various scenarios (#6988)

- Added unit tests for `Parallel` activity to ensure proper scheduling of child activities, including empty and mixed activity cases.
- Added integration tests to validate execution flow and edge cases for `Parallel` activities (e.g., nested parallelism, fault handling).
- Enhanced `ScheduleChildrenAsync` in `Parallel` to handle no activity scenario by completing immediately.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add new unit and integration tests for activity input and expression evaluation (#6990)

* Introduce Scheduler Strategy Interfaces and Implementations for Workflow and Activity Execution Contexts

- Added `IWorkflowExecutionContextSchedulerStrategy` and `IActivityExecutionContextSchedulerStrategy` interfaces.
- Implemented `WorkflowExecutionContextSchedulerStrategy` and `ActivityExecutionContextSchedulerStrategy` for scheduling activities in workflows.
- Refactored scheduling logic to utilize the new scheduler strategies.
- Updated unit tests and test helpers to reflect refactoring, introducing fake implementations for testing purposes.
- Adjusted background execution scheduling and improved extensibility for custom scheduler strategies.

* Refactor `SendHttpRequestTests`: simplify scheduling assertions, use shared extensions, and standardize method naming. Streamline helper methods and remove unused test logic.

* Refactor: Replace `ActivityTestHelper` with `ActivityTestFixture` in unit tests for streamlined activity testing

- Introduced `ActivityTestFixture` with a fluent API for better test setup and execution of activities.
- Added extension methods `ActivityTestFixtureExtensions` and `ActivityTestFixtureHttpExtensions` for configuring attributes and HTTP services.
- Updated test guidelines and unit tests to use the new fixture and extensions.
- Removed `ActivityTestHelper`.

* Refactor: Move `ActivityTestFixture` and related extensions to shared project for reuse across test suites

- Consolidated `ActivityTestFixture`, `ActivityTestFixtureExtensions`, and `ActivityTestFixtureHttpExtensions` into `Elsa.Testing.Shared`.
- Updated namespaces and imports across unit tests to reflect new structure.
- Enhanced `AssertActivityAttributes` and added fluent configuration APIs.
- Adjusted `Directory.Packages.props` with new dependencies, including `NSubstitute` and `xunit.assert`.

* Refactor `SetVariableTests`: inline `ActivityTestFixture` initialization to simplify test setup.

* Refactor `ActivityTestFixture`: eliminate redundant field `_services`, add `UsedImplicitly` attributes, and improve service collection management

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add XML documentation for `ActivityExecutionContextExtensions`, detailing methods and parameters.

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add XML documentation for scheduler strategies and their methods

- Updated `IActivityExecutionContextSchedulerStrategy` and `IWorkflowExecutionContextSchedulerStrategy` interfaces with XML summaries for methods.
- Added XML documentation to implementations (`ActivityExecutionContextSchedulerStrategy`, `WorkflowExecutionContextSchedulerStrategy`) and test fakes for clarity.

* Refactor `WriteLineTests`: consolidate duplicate test logic, simplify setup with shared helper method, and enhance readability in assertions.

* Refactor `WriteLineTests` and `SetVariableTests`: replace `WriteLineAsync` assertions with synchronous `WriteLine`, streamline exception recording in `SetVariableTests`, and remove unused imports.

* Add new unit and integration tests for activity input and expression evaluation

- Introduced comprehensive test suites covering activity input evaluation, expression handling, and fault scenarios.
- Added unit tests for `ExpressionDescriptorRegistry`, `ExpressionEvaluator`, and activity execution context extensions.
- Added integration tests: `CustomInputEvaluatorTests`, `InputEvaluationErrorTests`, and `InputPropertyEvaluationTests`.
- Enhanced code coverage for edge cases and async evaluation logic.

* Remove unused `RunWorkflowAsync` extension method from `RunActivityExtensions`.

* Rename `CustomInputEvaluatorTests` to `InputEvaluationTests` for consistency with naming conventions.

* Remove unused `Elsa.Workflows.Activities` import from `RunActivityExtensions`.

* Remove redundant comments from `InputEvaluationErrorTests` for clarity.

* Refactor unit tests to streamline activity and expression evaluations

- Refactored `ExecuteActivityAsync` and `ExecuteWriteLineAsync` into shared helpers for consistency and reuse across tests.
- Replaced redundant mock setups with helper methods in `ExpressionDescriptorRegistryTests`.
- Simplified test setup for expression and activity evaluation by removing unused imports and consolidating configuration logic.
- Enhanced readability by reducing duplicate code and leveraging shared utility methods.

* Refactor activity input evaluation tests

- Extracted `CreateContextAsync` helper into `EvaluationTestHelpers` for reuse across evaluation test suites.
- Replaced inline activity context setup with shared helper in `InputPropertyEvaluationTests`, `WrappedInputEvaluationTests`, and related test suites.
- Simplified test method names for clarity and consistency.
- Updated test annotations to enhance readability and align with naming conventions.

* Remove redundant test cases and unused imports

- Deleted duplicated and non-essential test cases across evaluation test suites.
- Removed unused imports to improve code cleanliness and readability.
- Streamlined variable initializations and method calls within test setups.

* Remove redundant test case from `InputEvaluationErrorTests`

- Deleted the `ContinuesEvaluationForMultipleInputs` test, as it overlaps with existing tests and does not provide additional coverage.

* Remove redundant assertion from `InputPropertyEvaluationTests`

- Deleted `Assert.True(context.GetHasEvaluatedProperties())`, as it is unnecessary for verifying test outcomes.

* Remove redundant test cases from `WrappedInputEvaluationTests`

- Deleted `UsesDefaultValueWhenInputIsNull` and `EvaluatesExpression` tests as they are either duplicated or unnecessary for current test coverage.

* Add unit test projects for `Elsa.Workflows.Management` and `Elsa.Expressions`

- Introduced new test projects to separate and organize unit tests for `Elsa.Workflows.Management` and `Elsa.Expressions`.
- Updated `Elsa.sln` to include references to the newly added test projects.
- Adjusted namespaces in affected test classes for consistency with the updated project structure.

* Refactor `ExpressionEvaluatorTests` for clarity and consistency

- Simplified test method names and annotations for improved readability.
- Replaced duplicate mock setups with helper functions (`CreateContextAsync`, `CreateContextWithMockHandlerAsync`, and related methods).
- Streamlined test setups by removing redundant code and consolidating context creation logic.
- Updated test annotations to include descriptive `DisplayName` attributes.

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Remove redundant comment from `ExpressionEvaluatorTests` for clarity

* Remove redundant blank lines from unit test classes

- Eliminated unnecessary blank lines across `ActivityExecutionContextExtensions` test suites to improve code readability and consistency.

* Update src/modules/Elsa.Expressions/Services/ExpressionEvaluator.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Expand `test-guidelines.md` with testing best practices, helper references, and example snippets

- Added detailed guidance on test project organization, updated helper documentation, and streamlined example code for activity unit testing.
- Introduced scheduler strategy information and integration test patterns for deterministic tests.
- Clarified usage of shared infrastructure like `ActivityTestFixture` and `AsyncWorkflowRunner`.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: lucas.hipolito <lukhipolito@yahoo.com.br>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
2025-10-20 20:11:46 +02:00
Sipke Schoorstra 1f2353cad8
Remove obsolete modules and associated code
Eliminated the `Elsa.Labels`, `Elsa.Environments`, and `Elsa.OpenTelemetry` modules along with their handlers, contracts, models, and related functionality. This cleanup improves maintainability and aligns the codebase with recent architectural changes.
2025-06-02 20:05:10 +02:00
Matt 10348c5ea3 Update all dependencies to new projects. 2025-05-21 00:20:13 +01:00
Sipke Schoorstra 2cfa8f935c Update project reference path for Elsa module
Corrected the project reference path from 'bundles' to 'modules' to ensure proper linkage and compilation. This change addresses a path configuration error in the project file.
2024-07-24 14:11:29 +02:00
Marko Lahma bc3d6d9bbc Convert to use Central Package Management
* Add GitHubActionsTestLogger
2024-02-04 13:49:54 +02:00
Marko Lahma 68beb5c61f replace configureawait.props and frameworks.props with Directory.Build.props 2024-01-26 19:04:39 +02:00
Marko Lahma 2f6ecb0b5d Use Directory.Build.props 2024-01-26 18:43:50 +02:00
Sipke Schoorstra bec8cbc322
Target .NET 8 (#4660) 2023-11-27 11:26:16 +01:00
Sipke Schoorstra e4d6ad22c4
Decouple Expression Types (#4622)
* Refactor Expressions model

* Enable expression type filtering

* Implement dynamic enablement of satelite dependencies
2023-11-14 20:44:42 +01:00
Sipke Schoorstra cd33d29768 Add support for direct-output binding
This change enables activity output to be directly bound to workflow output.
2023-11-09 13:35:02 +01:00
Sipke Schoorstra 7f0b3cb8fc Fix cascaded handling of SetOutcome 2023-09-14 16:30:18 +02:00
Sipke Schoorstra 3eb304e1ec
Fix flowchart completion bug (#3928)
* Ensure Flowchart completes when no more work

* Fix workflow instance JSON serializer

* Add tests that cover flowchart completion
2023-04-18 21:28:23 +02:00
Sipke Schoorstra 2dcd9bcd73 Add .NET 7 as a target 2022-12-22 15:40:31 +01:00
Sipke Schoorstra 3dfede96d3 Move shared test utility to common folder + various incremental changes 2022-12-20 12:13:53 +01:00
Renamed from test/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj (Browse further)