Skip to content

Add comprehensive API controller test coverage and serializer model tests#2250

Merged
niemyjski merged 21 commits into
mainfrom
feature/api-controller-test-coverage
May 23, 2026
Merged

Add comprehensive API controller test coverage and serializer model tests#2250
niemyjski merged 21 commits into
mainfrom
feature/api-controller-test-coverage

Conversation

@niemyjski
Copy link
Copy Markdown
Member

@niemyjski niemyjski commented May 22, 2026

Summary

Adds broad API controller coverage, ports serializer model contract tests from feature/system-text-json-v2, and includes the small production seams/fixes needed to make those API and billing paths testable.

Controller Tests Added

Controller Tests Added Coverage
UserController 22 new tests All 12 endpoints covered
UtilityController 5 new tests ValidateAsync endpoint fully covered
StripeController 3 new tests Webhook error paths
StackController ~20 new tests ChangeStatus, Delete, GetAll, GetAsync, GetByOrganization, GetByProject, MarkCritical, MarkNotCritical, RemoveLink, Snooze
StatusController ~9 new tests GetAbout, GetQueueStats, SystemNotification CRUD
OrganizationController Billing plan-change and Stripe failure-path coverage
TokenController Token creation, ownership, validation, default-token, update, and delete coverage

Serializer Model Tests Ported

Ported from feature/system-text-json-v2 with roundtrip, snake_case deserialization, and explicit wire-name assertions:

  • TokenSerializerTests
  • OrganizationSerializerTests
  • LocationSerializerTests
  • MethodSerializerTests
  • InnerErrorSerializerTests
  • ManualStackingInfoSerializerTests
  • SubmissionClientSerializerTests
  • UserDescriptionSerializerTests
  • SavedViewSerializerTests

Production Changes

  • Restores NewToken to the project-owned contract: ProjectId is required and NewToken implements IOwnedByOrganizationAndProject directly.
  • Updates the OpenAPI baseline so NewToken.project_id is required and ViewToken.project_id remains required.
  • Adds IStripeBillingClient / StripeBillingClient so controller and service billing flows can be tested without hitting Stripe.
  • Reuses a single lazy Stripe client inside StripeBillingClient instead of recreating the client for each operation.
  • Seeds SubscribeDate for existing-customer paid-plan changes when it was previously null or DateTime.MinValue.
  • Fixes collection equality null-comparison semantics in dictionary/enumerable helpers.

Conventions

  • Tests use 3-part naming: MethodUnderTest_Scenario_ExpectedBehavior.
  • Arrange/Act/Assert pattern throughout.
  • Request assertions use the shared status-code helpers when available.

niemyjski and others added 2 commits May 22, 2026 09:58
Tests cover all 12 UserController endpoints:
- GetCurrentUser (me), GetById, GetByOrganization
- Patch/Put user updates
- DeleteCurrentUser, Delete (admin)
- UpdateEmailAddress, VerifyEmail, ResendVerification
- UnverifyEmailAddress (admin)
- AddAdminRole, DeleteAdminRole (admin)

Each endpoint tested for happy path, unauthorized access,
and forbidden access (non-admin on admin endpoints).

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

Controller tests:
- UserControllerTests: 22 tests covering all 12 endpoints
- UtilityControllerTests: 5 tests for search/validate endpoint
- StripeControllerTests: 3 tests for webhook error cases
- StackControllerTests: ~20 new tests (ChangeStatus, Delete, GetAll,
  GetAsync, GetByOrganization, GetByProject, MarkCritical, MarkNotCritical,
  RemoveLink, Snooze)
- StatusControllerTests: ~9 new tests (GetAbout, QueueStats,
  SystemNotification CRUD)

Serializer model tests (ported from feature/system-text-json-v2):
- TokenSerializerTests: roundtrip and snake_case deserialization
- OrganizationSerializerTests: core properties, invites, suspension
- LocationSerializerTests: full, partial, unicode roundtrip
- MethodSerializerTests: all properties, minimal, snake_case
- InnerErrorSerializerTests: nested errors, stack traces
- ManualStackingInfoSerializerTests: signature data, special chars
- SubmissionClientSerializerTests: IPv4, IPv6, minimal
- UserDescriptionSerializerTests: data dictionary, minimal
- SavedViewSerializerTests: columns, filters, minimal

All tests use 3-part naming (Method_Scenario_Expected) with
Arrange/Act/Assert pattern, inserted alphabetically.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread tests/Exceptionless.Tests/Controllers/StripeControllerTests.cs Fixed
Comment thread tests/Exceptionless.Tests/Controllers/StripeControllerTests.cs Fixed
Comment thread tests/Exceptionless.Tests/Controllers/StripeControllerTests.cs Fixed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds new integration tests for multiple API controllers and ports several serializer roundtrip / snake_case deserialization tests from feature/system-text-json-v2 to increase regression coverage ahead of future controller refactors (e.g., minimal API migration).

Changes:

  • Added serializer model tests validating snake_case deserialization + roundtrip serialization for several core models.
  • Added new integration test suites for UserController, UtilityController, and StripeController.
  • Expanded existing controller test coverage for StatusController and StackController with additional endpoint scenarios.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/Exceptionless.Tests/Serializer/Models/UserDescriptionSerializerTests.cs Adds snake_case + roundtrip tests for UserDescription.
tests/Exceptionless.Tests/Serializer/Models/TokenSerializerTests.cs Adds snake_case + roundtrip tests for Token variations and flags.
tests/Exceptionless.Tests/Serializer/Models/SubmissionClientSerializerTests.cs Adds snake_case + roundtrip tests for SubmissionClient (IPv4/IPv6/minimal).
tests/Exceptionless.Tests/Serializer/Models/SavedViewSerializerTests.cs Adds snake_case + roundtrip tests for SavedView including columns/filter data.
tests/Exceptionless.Tests/Serializer/Models/OrganizationSerializerTests.cs Adds snake_case + roundtrip tests for Organization core fields/invites/suspension.
tests/Exceptionless.Tests/Serializer/Models/MethodSerializerTests.cs Adds snake_case + roundtrip tests for Method including params/generics.
tests/Exceptionless.Tests/Serializer/Models/ManualStackingInfoSerializerTests.cs Adds snake_case + roundtrip tests for ManualStackingInfo signature data.
tests/Exceptionless.Tests/Serializer/Models/LocationSerializerTests.cs Adds snake_case + roundtrip tests for Location including unicode.
tests/Exceptionless.Tests/Serializer/Models/InnerErrorSerializerTests.cs Adds snake_case + roundtrip tests for InnerError including nested inner errors.
tests/Exceptionless.Tests/Controllers/UtilityControllerTests.cs Adds integration coverage for search/validate scenarios (auth/valid/invalid/premium).
tests/Exceptionless.Tests/Controllers/UserControllerTests.cs Adds integration coverage for UserController endpoints (authz + basic success paths).
tests/Exceptionless.Tests/Controllers/StripeControllerTests.cs Adds integration coverage for Stripe webhook error paths.
tests/Exceptionless.Tests/Controllers/StatusControllerTests.cs Extends integration coverage for /about, /queue-stats, and system notification CRUD.
tests/Exceptionless.Tests/Controllers/StackControllerTests.cs Extends integration coverage for stack status changes, delete/get, critical flags, links, and snoozing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/Exceptionless.Tests/Controllers/UtilityControllerTests.cs
Comment thread tests/Exceptionless.Tests/Controllers/StackControllerTests.cs
Comment thread tests/Exceptionless.Tests/Controllers/StripeControllerTests.cs Outdated
niemyjski and others added 3 commits May 22, 2026 11:07
- Add PatchAsync tests to OrganizationControllerTests: UpdateName, NonExistent,
  Anonymous, EmptyName validation
- Add SemanticVersionParserTests: standard semver, pre-release, wildcard, cache
- Add EnumerableExtensionsTests: CollectionEquals order/equality, GetCollectionHashCode
- Add DictionaryExtensionsTests: CollectionEquals, AddRange, ContainsKeyWithValue
- Fix inverted logic in EnumerableExtensions.CollectionEquals (returned false for
  equal elements)
- Fix inverted logic in DictionaryExtensions.CollectionEquals (returned false for
  equal values, early-returned true on first null pair)

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

ProjectController - new tests:
- DeleteAsync: existing project removal, non-existent returns 404
- DeleteDataAsync: removes data key, invalid key returns 400, not found
- GetAllAsync: returns user projects, respects limit parameter
- GetByOrganizationAsync: returns org projects, invalid org returns 404
- GetConfigAsync: by project ID returns config
- GetV2ConfigAsync: client auth returns config
- GetNotificationSettingsAsync: admin gets all, user gets own
- IsNameAvailableAsync: taken name returns 204, new name returns 201, org-scoped
- PatchAsync: name change persists and preserves other fields, not found, extra props
- PostDataAsync: valid persists, empty/dash key returns 400, not found
- ResetDataAsync: clears stacks and events
- SetNotificationSettingsAsync: persists settings, null removes settings

AdminController - new tests:
- GetSettings: returns app options, forbidden for non-admin, unauthorized
- EchoRequest: returns headers/IP, forbidden, unauthorized
- GetAssemblies: returns assembly list, forbidden, unauthorized
- ChangePlanAsync: valid plan changes org, invalid plan fails, forbidden
- SetBonusAsync: applies bonus, with expiration, invalid org returns 422, forbidden

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add 14 new TokenController tests (delete, get by org/project, patch, auth)
- Add 10 new WebHookController tests (CRUD, unsubscribe, auth)
- Add StringExtensions unit tests (28 tests)
- Add HashExtensions unit tests (11 tests)
- Add JsonExtensions unit tests (11 tests)
- Fix double blank lines in serializer test files
- Clean up whitespace in Status/Stripe/User controller tests
- Add content-unchanged assertions on error responses

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread tests/Exceptionless.Tests/Controllers/StripeControllerTests.cs Fixed
Comment thread tests/Exceptionless.Tests/Controllers/StripeControllerTests.cs Fixed
Comment thread tests/Exceptionless.Tests/Utility/SemanticVersionParserTests.cs Fixed
Comment thread tests/Exceptionless.Tests/Controllers/ProjectControllerTests.cs Fixed
Comment thread tests/Exceptionless.Tests/Controllers/ProjectControllerTests.cs Fixed
Comment thread tests/Exceptionless.Tests/Controllers/AdminControllerTests.cs
Comment thread tests/Exceptionless.Tests/Controllers/AdminControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Controllers/AdminControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Controllers/ProjectControllerTests.cs
Comment thread tests/Exceptionless.Tests/Controllers/StackControllerTests.cs
Comment thread tests/Exceptionless.Tests/Controllers/StackControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Controllers/StatusControllerTests.cs
Comment thread tests/Exceptionless.Tests/Controllers/StripeControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Controllers/UtilityControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Controllers/WebHookControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Utility/DictionaryExtensionsTests.cs Outdated
niemyjski and others added 2 commits May 22, 2026 12:32
…, and IDisposable

- AdminControllerTests: remove System.Text.Json import, add private DTOs
  (AppSettingsDto, EchoResult, AssemblyInfo, ChangePlanResult), fix
  DateTime.UtcNow -> TimeProvider.GetUtcNow() in SetBonusAsync_WithExpiration
- ProjectControllerTests: use TryGetValue instead of ContainsKey+indexer
- StackControllerTests: rename now->utcNow/snoozeUntil->snoozeUntilUtc,
  add content-unchanged assertion after bad request
- StatusControllerTests: restore System.Net import for HttpStatusCode
- StripeControllerTests: using var StringContent, String.Empty, remove import
- UtilityControllerTests: fix premium query to use tags:error (free field fix)
- WebHookControllerTests: remove spurious RefreshDataAsync before GetByIdAsync
- DictionaryExtensionsTests: fix 2-part names to 3-part
- SemanticVersionParserTests: implement IDisposable for LoggerFactory

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

- Fix IsNameAvailable tests: 204=available, 201=taken (not reversed)
- Remove Assert.Single(Workers) from DeleteAsync test (project delete has no work items)
- Fix PatchAsync_EmptyName: expects 400 BadRequest (not 422 UnprocessableEntity)
- Fix GetCurrentUserAsync: remove IsEmailAddressVerified assertion (not set in sample data)
- Fix GetByOrganizationAsync invalid org: GlobalAdmin gets 200 empty list (not 404)
- Fix WebHook GetByProject: add RefreshDataAsync() before search-based GET
- Fix WebHook Unsubscribe: add RefreshDataAsync() before unsubscribe (search-dependent)
- Fix GetAssemblies: parse JSON directly (AssemblyDetail has private setters, STJ can't deserialize)
- Add StatusCodeShouldBeNoContent() extension method

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread tests/Exceptionless.Tests/Controllers/AdminControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Controllers/AdminControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Controllers/AdminControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Controllers/StatusControllerTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Services/OrganizationServiceTests.cs Fixed
Comment thread tests/Exceptionless.Tests/Services/OrganizationServiceTests.cs Fixed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 49 out of 49 changed files in this pull request and generated no new comments.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 49 out of 49 changed files in this pull request and generated no new comments.

Comment thread src/Exceptionless.Web/Models/Token/NewToken.cs Outdated
Comment thread src/Exceptionless.Web/Models/Token/ViewToken.cs Outdated
Comment thread src/Exceptionless.Core/Billing/StripeBillingClient.cs
Comment thread tests/Exceptionless.Tests/Utility/Results/OkPaginatedResultTests.cs Outdated
Comment thread tests/Exceptionless.Tests/Utility/Results/OkPaginatedResultTests.cs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 51 out of 51 changed files in this pull request and generated 2 comments.

Comment thread src/Exceptionless.Web/Models/Token/NewToken.cs Outdated
Comment thread tests/Exceptionless.Tests/Controllers/StackControllerTests.cs Outdated
niemyjski added 3 commits May 22, 2026 22:57
The NewToken model's ProjectId property is now non-nullable, and the API schema is updated to reflect `project_id` as a required field. This change enforces explicit project association for all new tokens.

When both `ProjectId` and `DefaultProjectId` are provided during token creation, `DefaultProjectId` will now be cleared. Attempts to create a token without a `ProjectId` will result in a validation error.
Refactor `ProjectController.PatchAsync` tests to send raw JSON payloads using `snake_case` property names.

New assertions verify:
- Read-only fields are ignored during patch updates.
- Updatable fields are correctly applied while preserving others.
- The API response consistently uses `snake_case` property naming, preventing potential serialization drift back to `PascalCase`.
@github-actions
Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
Exceptionless.Web 72% 61% 3733
Exceptionless.AppHost 18% 9% 82
Exceptionless.Insulation 25% 23% 203
Exceptionless.Core 68% 62% 7738
Summary 67% (13088 / 19439) 61% (6875 / 11304) 11756

@niemyjski niemyjski merged commit 1528263 into main May 23, 2026
9 checks passed
@niemyjski niemyjski deleted the feature/api-controller-test-coverage branch May 23, 2026 14:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants