Skip to content

test(discord): Add null-guard branch coverage for BotService constructor#377

Open
github-actions[bot] wants to merge 5 commits into
mainfrom
feature/356-discord-botservice-null-coverage
Open

test(discord): Add null-guard branch coverage for BotService constructor#377
github-actions[bot] wants to merge 5 commits into
mainfrom
feature/356-discord-botservice-null-coverage

Conversation

@github-actions

@github-actions github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

Summary

  • Adds a reflection-based test to cover the bot == null guard in BotService constructor, bringing BotService to 100% line and branch coverage.
  • The NX0002 analyser rule prevents using null! directly, so the constructor is invoked via ConstructorInfo.Invoke with a null argument instead.

Coverage status after this PR

Assembly Line Branch
BuildBot.ServiceModel 100% 100%
BuildBot.Discord ~93% line (infrastructure gaps tracked in #374) see #374
BuildBot.Health 100% 100%
BuildBot.Json 100% 100%
BuildBot.Watchtower 100% 100%
BuildBot.CloudFormation 100% 100%
BuildBot.GitHub 100% 100%
BuildBot 100% 100%

Remaining gaps in BuildBot.Discord (DiscordChannelAdapter and the live-infrastructure methods of DiscordSocketClientAdapter) are infrastructure-dependent and tracked in issue #374.

Closes #356

Test plan

  • All 233 tests pass
  • BotService reaches 100% line and branch coverage
  • dotnet buildcheck passes with no errors

@github-actions github-actions Bot added the auto-pr Pull request created automatically label Jun 22, 2026
@dnyw4l3n13 dnyw4l3n13 added C# C# Source Files Tech Debt Technical debt AI-Work Work for an AI Agent labels Jun 22, 2026
credfeto
credfeto previously approved these changes Jun 22, 2026
TargetInvocationException ex = Assert.Throws<TargetInvocationException>(() =>
ctor.Invoke([null, messageChannel, releaseChannel])
);
Assert.IsType<ArgumentNullException>(ex.InnerException);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Code Review] The test asserts only IsType<ArgumentNullException> without checking ParamName. If the guard in BotService is accidentally changed to throw new ArgumentNullException(nameof(botMessageChannel)) (wrong parameter name), the test still passes and the regression goes undetected.

Suggestion: also assert the parameter name:

ArgumentNullException ane = Assert.IsType<ArgumentNullException>(ex.InnerException);
Assert.Equal("bot", ane.ParamName);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fixed in 0e24809 — asserted ParamName == "bot" on the caught ArgumentNullException so a mis-targeted guard would be detected.

MessageChannel<BotMessage> messageChannel = new();
MessageChannel<BotReleaseMessage> releaseChannel = new();

ConstructorInfo ctor = Assert.Single(typeof(BotService).GetConstructors());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Code Review] Assert.Single(typeof(BotService).GetConstructors()) is a guard against a second constructor being added, but if one is ever added the failure message will be a count-related xunit error with no mention of null guards — making the root cause non-obvious to the next developer.

Consider selecting the specific constructor by its parameter types instead, which both locates the right overload and provides a clearer failure if the signature changes:

ConstructorInfo? ctor = typeof(BotService).GetConstructor([
    typeof(IDiscordBot),
    typeof(IMessageChannel<BotMessage>),
    typeof(IMessageChannel<BotReleaseMessage>)
]);
Assert.NotNull(ctor);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fixed in 0e24809 — replaced Assert.Single(GetConstructors()) with GetConstructor([...]) targeting the constructor by explicit parameter types, giving a clearer failure if the signature changes.

[Fact]
public void Constructor_ThrowsArgumentNullException_WhenBotIsNull()
{
MessageChannel<BotMessage> messageChannel = new();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Code Review] messageChannel and releaseChannel are created but never reached: the BotService constructor throws ArgumentNullException on the first line (bot ?? throw ...) before it touches either channel parameter. Passing null for those positions (or any placeholder) would produce the same result and make it clearer that only bot is the subject under test.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fixed in 0e24809 — removed the unnecessary messageChannel and releaseChannel allocations; all three invoke arguments are now null since the constructor throws before reaching the channel parameters.

@dnyw4l3n13

Copy link
Copy Markdown
Collaborator

[Code Review — Pre-existing gap] BotService.cs lines 27-28 assign botMessageChannel and botReleaseMessageChannel without null guards, but both are immediately dereferenced inside the Rx chain (lines 31 and 41). If either is passed as null, a NullReferenceException fires deep in Rx internals with no hint that it was a missing null guard. This is pre-existing and outside this PR's diff, but it is the subject matter this PR is extending (null-guard coverage), so flagging it here for tracking. Consider adding ?? throw new ArgumentNullException(nameof(...)) guards for the two channel parameters in BotService.cs.

dnyw4l3n13 added a commit that referenced this pull request Jun 23, 2026
- Use GetConstructor with explicit parameter types instead of Assert.Single
  on GetConstructors(), avoiding confusing failure messages if a second
  constructor is ever added
- Remove unnecessary MessageChannel allocations — the constructor throws
  before touching the channel parameters, so null suffices for those args
- Assert ParamName == "bot" on the caught ArgumentNullException so a
  mis-targeted guard is detected rather than silently passing

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
dnyw4l3n13 added a commit that referenced this pull request Jun 25, 2026
- Use GetConstructor with explicit parameter types instead of Assert.Single
  on GetConstructors(), avoiding confusing failure messages if a second
  constructor is ever added
- Remove unnecessary MessageChannel allocations — the constructor throws
  before touching the channel parameters, so null suffices for those args
- Assert ParamName == "bot" on the caught ArgumentNullException so a
  mis-targeted guard is detected rather than silently passing

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
@dnyw4l3n13 dnyw4l3n13 force-pushed the feature/356-discord-botservice-null-coverage branch from b04c00f to c2aa353 Compare June 25, 2026 10:03
dnyw4l3n13 added a commit that referenced this pull request Jun 26, 2026
- Use GetConstructor with explicit parameter types instead of Assert.Single
  on GetConstructors(), avoiding confusing failure messages if a second
  constructor is ever added
- Remove unnecessary MessageChannel allocations — the constructor throws
  before touching the channel parameters, so null suffices for those args
- Assert ParamName == "bot" on the caught ArgumentNullException so a
  mis-targeted guard is detected rather than silently passing

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
@dnyw4l3n13 dnyw4l3n13 force-pushed the feature/356-discord-botservice-null-coverage branch from c2aa353 to 0e24809 Compare June 26, 2026 03:04
dnyw4l3n13 added a commit that referenced this pull request Jun 27, 2026
…nstructor

Add `?? throw ArgumentNullException` guards for botMessageChannel and
botReleaseMessageChannel — they were assigned without guards and would
cause a NullReferenceException deep in Rx internals if passed null.
Add corresponding constructor tests to cover both new guards.

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
dnyw4l3n13 added a commit that referenced this pull request Jun 27, 2026
…structor

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
@dnyw4l3n13

Copy link
Copy Markdown
Collaborator

Fixed in ee1e59b — added ?? throw new ArgumentNullException(nameof(...)) guards for both botMessageChannel and botReleaseMessageChannel in BotService.cs, and added corresponding constructor tests (Constructor_ThrowsArgumentNullException_WhenBotMessageChannelIsNull and Constructor_ThrowsArgumentNullException_WhenBotReleaseMessageChannelIsNull) to cover both new guards.

}

[Fact]
public void Constructor_ThrowsArgumentNullException_WhenBotIsNull()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Code Review] The three Constructor_ThrowsArgumentNullException_When* methods are structurally identical — each performs the same GetConstructor lookup and the same assert pattern, varying only in which argument is null and the expected ParamName. Project rules require: 'Prefer parameterised tests over duplicated test methods — each behavioural variant is a data point, not a separate method. Use the idiomatic mechanism for the framework (xUnit [Theory]/[InlineData]).' A single [Theory] with [MemberData] covers all three cases, and also eliminates the GetConstructor call duplicated verbatim three times.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fixed in bf6ec38 — collapsed the three identical [Fact] methods into a single [Theory] with [MemberData], using an int index to select which parameter is null and setting it after pre-populating all args with non-null substitutes. The GetConstructor call now appears once.

dnyw4l3n13 added a commit that referenced this pull request Jun 27, 2026
Replace three structurally identical Constructor_ThrowsArgumentNullException_When*
[Fact] methods with a single [Theory] + [MemberData] test, using an index to
select which parameter is null. Removes the GetConstructor call duplicated
verbatim three times and satisfies the project rule requiring parameterised
tests over duplicated methods.

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
@dnyw4l3n13 dnyw4l3n13 marked this pull request as ready for review June 27, 2026 06:29
@dnyw4l3n13 dnyw4l3n13 requested a review from a team as a code owner June 27, 2026 06:29
@dnyw4l3n13 dnyw4l3n13 enabled auto-merge June 27, 2026 06:29
@credfeto

credfeto commented Jun 27, 2026

Copy link
Copy Markdown
Member

Super-linter summary

Language Validation result

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@dnyw4l3n13

Copy link
Copy Markdown
Collaborator

All work complete. Summary of changes on this PR:

  • 0e24809 — Strengthened the existing bot null-guard test: use GetConstructor with explicit parameter types, pass [null, null, null] to avoid unnecessary allocations, assert ParamName == "bot"
  • ee1e59b — Added null guards for botMessageChannel and botReleaseMessageChannel in BotService.cs, with corresponding tests
  • bf6ec38 — Refactored three identical [Fact] null-guard tests into a single [Theory] with [MemberData] per project rules

Code review and security review both passed clean. Auto-merge enabled.

Adds a reflection-based test to cover the null-bot argument guard in
BotService, bringing BotService to 100% line and branch coverage.
The null! operator cannot be used directly due to the NX0002 analyser
rule, so the constructor is invoked via reflection.

Prompt: Work on issue #356 in funfair-tech/BuildBot.
Closes #356
- Use GetConstructor with explicit parameter types instead of Assert.Single
  on GetConstructors(), avoiding confusing failure messages if a second
  constructor is ever added
- Remove unnecessary MessageChannel allocations — the constructor throws
  before touching the channel parameters, so null suffices for those args
- Assert ParamName == "bot" on the caught ArgumentNullException so a
  mis-targeted guard is detected rather than silently passing

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
…nstructor

Add `?? throw ArgumentNullException` guards for botMessageChannel and
botReleaseMessageChannel — they were assigned without guards and would
cause a NullReferenceException deep in Rx internals if passed null.
Add corresponding constructor tests to cover both new guards.

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
…structor

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
Replace three structurally identical Constructor_ThrowsArgumentNullException_When*
[Fact] methods with a single [Theory] + [MemberData] test, using an index to
select which parameter is null. Removes the GetConstructor call duplicated
verbatim three times and satisfies the project rule requiring parameterised
tests over duplicated methods.

Prompt: Work on pull request #377 in funfair-tech/BuildBot.
@dnyw4l3n13 dnyw4l3n13 force-pushed the feature/356-discord-botservice-null-coverage branch from bf6ec38 to 3f41659 Compare June 27, 2026 17:42
@dnyw4l3n13

Copy link
Copy Markdown
Collaborator

Rebased the branch onto origin/main to pick up the Meziantou.Analyzer 3.0.115 dependency update (commit cc5c24cb). Force-pushed successfully; CI is now running on the rebased tip. Auto-merge remains enabled — will merge automatically once checks pass.

@dnyw4l3n13

Copy link
Copy Markdown
Collaborator

All CI checks pass and auto-merge is enabled. The PR is currently blocked on REVIEW_REQUIRED — the previous approval from @credfeto was dismissed when the branch was rebased and force-pushed (2026-06-27T17:43). No further code changes are needed; the PR is ready and waiting for a human approval to satisfy the branch-protection rule before auto-merge can complete.

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

Labels

AI-Work Work for an AI Agent auto-pr Pull request created automatically C# C# Source Files Tech Debt Technical debt

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Coverage] Increase code coverage to 100% across all projects

2 participants