Skip to content

fix(ci): bound test sessions with a per-session timeout - #547

Merged
JerrettDavis merged 26 commits into
mainfrom
fix/ci-test-timeout
Nov 6, 2026
Merged

JerrettDavis merged 26 commits into
mainfrom
fix/ci-test-timeout

Conversation

@JerrettDavis

Copy link
Copy Markdown
Owner

Recovers the CI hardening that was parked in the stash on docs/fix-toc-coverage and adapts it to the current per-project test layout.

What changed

Adds -- RunConfiguration.TestSessionTimeout=300000 (5 minutes) to all eight dotnet test invocations across the pr-checks and release jobs.

Why

A single hung test could previously consume an entire test step's budget. This caps one test session so the step timeout becomes a reliable backstop rather than the first line of defence. Main has no per-session guard in any form today.

What was deliberately not applied

Two hunks from the original stash are already superseded upstream and are not reapplied:

Stashed value Current main Reason
timeout-minutes: 10 (test step) 45 Tuned for the old single dotnet test PatternKit.slnx invocation. Main now runs four test projects separately, so tightening back to 10 minutes would break CI.
timeout-minutes: 30 (release job) 90 Same reason, at job level.

The stash also carried Exclude="[*Tests]*" coverage filters. Those are not portable either — main now invokes each test project directly, so that filter would exclude the very assemblies being measured.

Provenance

The stashed change was written against the older single-dotnet test layout and mirrored the implementation on fix/ci-test-timeout. Only the portable per-session guard is carried forward; the superseded timeout values are left in the stash entry, which is retained.

🤖 Generated with Claude Code

Copilot AI and others added 26 commits February 19, 2026 04:17
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
…ribute

Complete rewrite of ObserverGenerator.cs to extract the payload type from the
[Observer(typeof(TPayload))] attribute constructor argument and generate type-safe
Subscribe/Publish methods.

Key features:
- Extract TPayload from attribute constructor (typeof(TPayload))
- Generate Subscribe(Action<TPayload>) and Subscribe(Func<TPayload, ValueTask>)
- Generate Publish(TPayload) and PublishAsync(TPayload, CancellationToken)
- Implement proper snapshot semantics for thread-safe iteration
- Support three threading policies:
  * SingleThreadedFast: No locking, just a List
  * Locking: Use lock() for thread safety (default)
  * Concurrent: Use Immutable collections for lock-free operation
- Support three exception policies:
  * Continue: Invoke all handlers, call optional OnSubscriberError hook
  * Stop: Rethrow first exception
  * Aggregate: Collect all exceptions and throw AggregateException
- Support RegistrationOrder (FIFO) and Undefined order policies
- Support all target type kinds (class, struct, record class, record struct)
- Handle structs without field initializers (C# 11+ compatibility)
- Nested private Subscription class implementing IDisposable
- Idempotent, thread-safe disposal
- Clean, deterministic code generation with proper nullability annotations

The generator now follows the same pattern as StrategyGenerator for extracting
constructor arguments and generating clean, focused implementation code.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ty issues

- Add ObserverAttribute(Type payloadType) constructor to accept payload type
- Add PayloadType property to ObserverAttribute
- Update documentation example to show [Observer(typeof(Temperature))]
- Fix critical thread safety bugs in lock usage:
  * Use lock field assignment to ensure same lock object is used
  * Change 'lock (_lock ?? new object())' to proper initialization
- Add defensive null check for Attributes array access
- Ensure lock object is initialized before use in Publish and Unsubscribe

These fixes address code review feedback and ensure proper thread-safe operation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
- Created comprehensive documentation at docs/generators/observer.md
  - Overview and motivation
  - Basic usage examples with sync and async handlers
  - Configuration options (threading, exceptions, ordering)
  - Complete API reference (Subscribe, Publish, PublishAsync)
  - Best practices and performance considerations
  - Common patterns (observable properties, event aggregator)
  - Diagnostics reference (PKOBS001-003)
  - Troubleshooting guide

- Added real-world examples in src/PatternKit.Examples/ObserverGeneratorDemo/
  - TemperatureMonitor.cs: Basic Observer usage with temperature sensors
    - Demonstrates sync handlers, multiple subscribers
    - Exception handling with OnSubscriberError
    - Subscription lifecycle management
  - NotificationSystem.cs: Advanced async patterns
    - Multi-channel notifications (Email, SMS, Push)
    - Async handlers with PublishAsync
    - Exception policies (Continue vs Aggregate)
    - Cancellation token support
  - README.md: Comprehensive example documentation
    - Quick start guide
    - Configuration examples
    - Common patterns
    - Running instructions

- Updated docs/generators/toc.yml to include observer.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use positional parameters instead of named parameters with incorrect casing
in Notification record instantiation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ion, fire-and-forget async, disable struct support

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
…ixed handler test, verify struct diagnostics

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
…ets.Struct, improve async error handling, remove unused using

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
…y Subscription.Dispose

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
… validate enum ranges, fix ForceAsync config, assert EmitResult, document dependencies

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
…n, fix ConcurrentBag race, update nested type examples, clarify hub attributes not implemented, merge main

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
…nd-forget docs, add AttributeTargets.Struct for PKOBS003

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
…andling, add discard for Task.Run to avoid CS4014

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
…berError usage, add System.Linq using for ImmutableList.ToArray()

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
…ncurrentBag compaction on disposal, update docs

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…o copilot/create-observer-pattern-generator

# Conflicts:
#	src/PatternKit.Examples/ObserverGeneratorDemo/NotificationSystem.cs
#	src/PatternKit.Examples/ObserverGeneratorDemo/TemperatureMonitor.cs
#	src/PatternKit.Generators/AnalyzerReleases.Unshipped.md
#	src/PatternKit.Generators/Observer/ObserverGenerator.cs
#	test/PatternKit.Generators.Tests/ObserverGeneratorTests.cs
#	test/PatternKit.Generators.Tests/PatternKit.Generators.Tests.csproj
#	test/PatternKit.Generators.Tests/packages.lock.json
Recovers the CI hardening that was parked in the stash on
docs/fix-toc-coverage and adapts it to the current per-project test
layout.

Two of the stashed hunks are already superseded upstream and are not
reapplied: the 10-minute test-step budget and the 30-minute release-job
budget. Main now uses 45/60-minute step budgets and 75/90-minute job
budgets, which are tuned for the split per-project test runs that
replaced the old single `dotnet test PatternKit.slnx` invocation.
Tightening them back down would break CI.

The portable piece is the per-test-session guard, which main does not
have in any form. `-- RunConfiguration.TestSessionTimeout=300000`
(5 minutes) caps a single test session so one hung test cannot consume
an entire step budget. It is applied to all eight test invocations
across the pr-checks and release jobs, matching the implementation on
fix/ci-test-timeout.
Copilot AI balanced review requested due to automatic review settings November 6, 2026 18:33
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

An extra -- separator in eight CI invocations causes VSTest to reject the commands before running tests, blocking both pr-checks and release.

1 open issue
What changed in this PR

This PR aims to limit hung test sessions to five minutes within the existing per-project CI test layout.

Changes:

  • Adds RunConfiguration.TestSessionTimeout=300000 to all eight dotnet test invocations across pr-checks and release.
  • Preserves existing step/job timeouts and coverage settings.
File Description
.github/​workflows/​ci.yml Adds per-session timeout arguments to four test-project commands in each job.

Review effort: Balanced

Comment thread .github/workflows/ci.yml
--logger "trx;LogFilePrefix=core-test-results" \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura \
-- RunConfiguration.TestSessionTimeout=300000
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Code Coverage

Summary
  Generated on: 11/06/2026 - 18:41:30
  Coverage date: 11/06/2026 - 18:39:27 - 11/06/2026 - 18:41:18
  Parser: MultiReport (8x Cobertura)
  Assemblies: 5
  Classes: 1817
  Files: 611
  Line coverage: 97.4%
  Covered lines: 48650
  Uncovered lines: 1276
  Coverable lines: 49926
  Total lines: 105800
  Branch coverage: 84.3% (15262 of 18095)
  Covered branches: 15262
  Total branches: 18095
  Method coverage: 97.3% (9510 of 9772)
  Full method coverage: 91.7% (8961 of 9772)
  Covered methods: 9510
  Fully covered methods: 8961
  Total methods: 9772

PatternKit.Core                                                                                                     96.2%
  PatternKit.Application.ActivityTracking.ActivityGateState                                                          100%
  PatternKit.Application.ActivityTracking.ActivityLease                                                              100%
  PatternKit.Application.ActivityTracking.ActivityRecord                                                             100%
  PatternKit.Application.ActivityTracking.ActivityTracker                                                            100%
  PatternKit.Application.Aggregates.AggregateCommandHandler<T1, T2, T3>                                              100%
  PatternKit.Application.Aggregates.AggregateCommandResult<T>                                                        100%
  PatternKit.Application.Aggregates.AggregateRoot<T1, T2>                                                            100%
  PatternKit.Application.AntiCorruption.AntiCorruptionLayer<T1, T2>                                                 90.4%
  PatternKit.Application.AntiCorruption.AntiCorruptionResult<T>                                                      100%
  PatternKit.Application.AuditLog.AuditLogAppendResult<T>                                                           85.7%
  PatternKit.Application.AuditLog.InMemoryAuditLog<T1, T2>                                                          95.4%
  PatternKit.Application.BoundedContexts.BoundedContextAdapter                                                       100%
  PatternKit.Application.BoundedContexts.BoundedContextCapability                                                   83.3%
  PatternKit.Application.BoundedContexts.BoundedContextDescriptor                                                   95.4%
  PatternKit.Application.CompensatingTransactions.CompensatingTransaction<T>                                        96.9%
  PatternKit.Application.CompensatingTransactions.CompensatingTransactionExecution<T>                                100%
  PatternKit.Application.CompensatingTransactions.CompensatingTransactionRecord                                      100%
  PatternKit.Application.CompensatingTransactions.CompensatingTransactionStep<T>                                     100%
  PatternKit.Application.CompensatingTransactions.CompensatingTransactionStepBuilder<T>                              100%
  PatternKit.Application.ContextMaps.ContextMapDescriptor                                                           96.8%
  PatternKit.Application.ContextMaps.ContextMapRelationship                                                          100%
  PatternKit.Application.DataMapping.DataMapper<T1, T2>                                                             94.6%
  PatternKit.Application.DataMapping.DataMapperError                                                                  90%
  PatternKit.Application.DataMapping.DataMapperResult<T>                                                            84.6%
  PatternKit.Application.DomainEvents.DomainEventDispatcher<T>                                                      95.4%
  PatternKit.Application.DomainEvents.DomainEventDispatchResult                                                      100%
  PatternKit.Application.DomainServices.DomainServiceOperation<T1, T2>                                               100%
  PatternKit.Application.DomainServices.DomainServiceRegistry<T1, T2>                                                100%
  PatternKit.Application.EventSourcing.EventStoreAppendResult                                                        100%
  PatternKit.Application.EventSourcing.InMemoryEventStore<T1, T2>                                                   97.9%
  PatternKit.Application.EventSourcing.StoredEvent<T1, T2>                                                            80%
  PatternKit.Application.EventualConsistency.EventualConsistencyEvaluation<T>                                       92.3%
  PatternKit.Application.EventualConsistency.EventualConsistencyMonitor<T>                                          97.2%
  PatternKit.Application.EventualConsistency.EventualConsistencyMonitorState<T>                                      100%
  PatternKit.Application.EventualConsistency.EventualConsistencyWatermarks<T>                                       96.7%
  PatternKit.Application.FeatureToggles.FeatureToggleDecision                                                       87.5%
  PatternKit.Application.FeatureToggles.FeatureToggleRule<T>                                                         100%
  PatternKit.Application.FeatureToggles.FeatureToggleSet<T>                                                         96.9%

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Test Results

    12 files      12 suites   12m 35s ⏱️
 4 197 tests  4 197 ✅ 0 💤 0 ❌
13 043 runs  13 043 ✅ 0 💤 0 ❌

Results for commit 1bd22dd.

@codecov

codecov Bot commented Nov 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.44%. Comparing base (c0d109c) to head (1bd22dd).

Additional details and impacted files
@@            Coverage Diff            @@
##             main     #547     +/-   ##
=========================================
  Coverage   97.44%   97.44%             
=========================================
  Files         611      611             
  Lines       49926    49926             
  Branches     3114       34   -3080     
=========================================
+ Hits        48650    48651      +1     
+ Misses       1276     1275      -1     
Flag Coverage Δ
unittests 97.44% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Validation Results

Version: ``

✅ Validation Steps

  • Build solution
  • Run tests
  • Build documentation
  • Dry-run NuGet packaging

📊 Artifacts

Dry-run artifacts have been uploaded and will be available for 7 days.


This comment was automatically generated by the PR validation workflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants