@@ -451,4 +451,225 @@ public void GitHubRepoConnector_DetermineBaselineVersion_NoReleases_ReturnsNull(
451451 Assert . IsNull ( fromVersion ) ;
452452 Assert . IsNull ( fromHash ) ;
453453 }
454+
455+ /// <summary>
456+ /// Test that GetBuildInformationAsync works with mocked data.
457+ /// </summary>
458+ [ TestMethod ]
459+ public async Task GitHubRepoConnector_GetBuildInformationAsync_WithMockedData_ReturnsValidBuildInformation ( )
460+ {
461+ // Arrange - Create mock responses using helper methods
462+ using var mockHandler = new MockGitHubGraphQLHttpMessageHandler ( )
463+ . AddCommitsResponse ( new [ ] { "abc123def456" } )
464+ . AddReleasesResponse ( new [ ] { new MockRelease ( "v1.0.0" , "2024-01-01T00:00:00Z" ) } )
465+ . AddPullRequestsResponse ( Array . Empty < MockPullRequest > ( ) )
466+ . AddIssuesResponse ( Array . Empty < MockIssue > ( ) )
467+ . AddTagsResponse ( new [ ] { new MockTag ( "v1.0.0" , "abc123def456" ) } ) ;
468+
469+ using var mockHttpClient = new HttpClient ( mockHandler ) ;
470+ var connector = new MockableGitHubRepoConnector ( mockHttpClient ) ;
471+
472+ // Set up mock command responses
473+ connector . SetCommandResponse ( "git remote get-url origin" , "https://github.com/test/repo.git" ) ;
474+ connector . SetCommandResponse ( "git rev-parse --abbrev-ref HEAD" , "main" ) ;
475+ connector . SetCommandResponse ( "git rev-parse HEAD" , "abc123def456" ) ;
476+ connector . SetCommandResponse ( "gh auth token" , "test-token" ) ;
477+
478+ // Act
479+ var buildInfo = await connector . GetBuildInformationAsync ( Version . Create ( "v1.0.0" ) ) ;
480+
481+ // Assert
482+ Assert . IsNotNull ( buildInfo ) ;
483+ Assert . AreEqual ( "1.0.0" , buildInfo . CurrentVersionTag . VersionInfo . FullVersion ) ;
484+ Assert . AreEqual ( "abc123def456" , buildInfo . CurrentVersionTag . CommitHash ) ;
485+ Assert . IsNotNull ( buildInfo . Changes ) ;
486+ Assert . IsNotNull ( buildInfo . Bugs ) ;
487+ Assert . IsNotNull ( buildInfo . KnownIssues ) ;
488+ }
489+
490+ /// <summary>
491+ /// Test that GetBuildInformationAsync correctly selects previous version and generates changelog link.
492+ /// </summary>
493+ [ TestMethod ]
494+ public async Task GitHubRepoConnector_GetBuildInformationAsync_WithMultipleVersions_SelectsCorrectPreviousVersionAndGeneratesChangelogLink ( )
495+ {
496+ // Arrange - Create mock responses with multiple versions
497+ using var mockHandler = new MockGitHubGraphQLHttpMessageHandler ( )
498+ . AddCommitsResponse ( new [ ] { "commit3" , "commit2" , "commit1" } )
499+ . AddReleasesResponse ( new [ ]
500+ {
501+ new MockRelease ( "v2.0.0" , "2024-03-01T00:00:00Z" ) ,
502+ new MockRelease ( "v1.1.0" , "2024-02-01T00:00:00Z" ) ,
503+ new MockRelease ( "v1.0.0" , "2024-01-01T00:00:00Z" )
504+ } )
505+ . AddPullRequestsResponse ( Array . Empty < MockPullRequest > ( ) )
506+ . AddIssuesResponse ( Array . Empty < MockIssue > ( ) )
507+ . AddTagsResponse ( new [ ]
508+ {
509+ new MockTag ( "v2.0.0" , "commit3" ) ,
510+ new MockTag ( "v1.1.0" , "commit2" ) ,
511+ new MockTag ( "v1.0.0" , "commit1" )
512+ } ) ;
513+
514+ using var mockHttpClient = new HttpClient ( mockHandler ) ;
515+ var connector = new MockableGitHubRepoConnector ( mockHttpClient ) ;
516+
517+ // Set up mock command responses
518+ connector . SetCommandResponse ( "git remote get-url origin" , "https://github.com/test/repo.git" ) ;
519+ connector . SetCommandResponse ( "git rev-parse --abbrev-ref HEAD" , "main" ) ;
520+ connector . SetCommandResponse ( "git rev-parse HEAD" , "commit3" ) ;
521+ connector . SetCommandResponse ( "gh auth token" , "test-token" ) ;
522+
523+ // Act
524+ var buildInfo = await connector . GetBuildInformationAsync ( Version . Create ( "v2.0.0" ) ) ;
525+
526+ // Assert
527+ Assert . IsNotNull ( buildInfo ) ;
528+ Assert . AreEqual ( "2.0.0" , buildInfo . CurrentVersionTag . VersionInfo . FullVersion ) ;
529+ Assert . AreEqual ( "commit3" , buildInfo . CurrentVersionTag . CommitHash ) ;
530+
531+ // Should have selected v1.1.0 as baseline (previous non-prerelease)
532+ Assert . IsNotNull ( buildInfo . BaselineVersionTag ) ;
533+ Assert . AreEqual ( "1.1.0" , buildInfo . BaselineVersionTag . VersionInfo . FullVersion ) ;
534+ Assert . AreEqual ( "commit2" , buildInfo . BaselineVersionTag . CommitHash ) ;
535+
536+ // Should have changelog link
537+ Assert . IsNotNull ( buildInfo . CompleteChangelogLink ) ;
538+ Assert . IsTrue ( buildInfo . CompleteChangelogLink . TargetUrl . Contains ( "v1.1.0...v2.0.0" ) ) ;
539+ }
540+
541+ /// <summary>
542+ /// Test that GetBuildInformationAsync correctly gathers changes from PRs with labels.
543+ /// </summary>
544+ [ TestMethod ]
545+ public async Task GitHubRepoConnector_GetBuildInformationAsync_WithPullRequests_GathersChangesCorrectly ( )
546+ {
547+ // Arrange - Create mock responses with PRs containing different label types
548+ // We need commits in range between v1.0.0 and v1.1.0
549+ using var mockHandler = new MockGitHubGraphQLHttpMessageHandler ( )
550+ . AddCommitsResponse ( new [ ] { "commit3" , "commit2" , "commit1" } )
551+ . AddReleasesResponse ( new [ ]
552+ {
553+ new MockRelease ( "v1.1.0" , "2024-02-01T00:00:00Z" ) ,
554+ new MockRelease ( "v1.0.0" , "2024-01-01T00:00:00Z" )
555+ } )
556+ . AddPullRequestsResponse ( new [ ]
557+ {
558+ new MockPullRequest (
559+ Number : 101 ,
560+ Title : "Add new feature" ,
561+ Url : "https://github.com/test/repo/pull/101" ,
562+ Merged : true ,
563+ MergeCommitSha : "commit3" ,
564+ HeadRefOid : "feature-branch" ,
565+ Labels : new List < string > { "feature" , "enhancement" } ) ,
566+ new MockPullRequest (
567+ Number : 100 ,
568+ Title : "Fix critical bug" ,
569+ Url : "https://github.com/test/repo/pull/100" ,
570+ Merged : true ,
571+ MergeCommitSha : "commit2" ,
572+ HeadRefOid : "bugfix-branch" ,
573+ Labels : new List < string > { "bug" } )
574+ } )
575+ . AddIssuesResponse ( Array . Empty < MockIssue > ( ) )
576+ . AddTagsResponse ( new [ ]
577+ {
578+ new MockTag ( "v1.1.0" , "commit3" ) ,
579+ new MockTag ( "v1.0.0" , "commit1" )
580+ } )
581+ // Mock the linked issues query to return empty (PRs are treated as standalone changes)
582+ . AddResponse ( "closingIssuesReferences" , @"{""data"":{""repository"":{""pullRequest"":{""closingIssuesReferences"":{""nodes"":[],""pageInfo"":{""hasNextPage"":false,""endCursor"":null}}}}}}" ) ;
583+
584+ using var mockHttpClient = new HttpClient ( mockHandler ) ;
585+ var connector = new MockableGitHubRepoConnector ( mockHttpClient ) ;
586+
587+ // Set up mock command responses
588+ connector . SetCommandResponse ( "git remote get-url origin" , "https://github.com/test/repo.git" ) ;
589+ connector . SetCommandResponse ( "git rev-parse --abbrev-ref HEAD" , "main" ) ;
590+ connector . SetCommandResponse ( "git rev-parse HEAD" , "commit3" ) ;
591+ connector . SetCommandResponse ( "gh auth token" , "test-token" ) ;
592+
593+ // Act
594+ var buildInfo = await connector . GetBuildInformationAsync ( Version . Create ( "v1.1.0" ) ) ;
595+
596+ // Assert
597+ Assert . IsNotNull ( buildInfo ) ;
598+ Assert . AreEqual ( "1.1.0" , buildInfo . CurrentVersionTag . VersionInfo . FullVersion ) ;
599+
600+ // PRs without linked issues are treated based on their labels
601+ // PR 100 with "bug" label should be in bugs
602+ Assert . IsNotNull ( buildInfo . Bugs ) ;
603+ Assert . IsTrue ( buildInfo . Bugs . Count >= 1 , $ "Expected at least 1 bug, got { buildInfo . Bugs . Count } ") ;
604+ var bugPR = buildInfo . Bugs . FirstOrDefault ( b => b . Index == 100 ) ;
605+ Assert . IsNotNull ( bugPR , "PR 100 should be categorized as a bug" ) ;
606+ Assert . AreEqual ( "Fix critical bug" , bugPR . Title ) ;
607+
608+ // PR 101 with "feature" label should be in changes
609+ Assert . IsNotNull ( buildInfo . Changes ) ;
610+ Assert . IsTrue ( buildInfo . Changes . Count >= 1 , $ "Expected at least 1 change, got { buildInfo . Changes . Count } ") ;
611+ var featurePR = buildInfo . Changes . FirstOrDefault ( c => c . Index == 101 ) ;
612+ Assert . IsNotNull ( featurePR , "PR 101 should be categorized as a change" ) ;
613+ Assert . AreEqual ( "Add new feature" , featurePR . Title ) ;
614+ }
615+
616+ /// <summary>
617+ /// Test that GetBuildInformationAsync correctly identifies known issues.
618+ /// </summary>
619+ [ TestMethod ]
620+ public async Task GitHubRepoConnector_GetBuildInformationAsync_WithOpenIssues_IdentifiesKnownIssues ( )
621+ {
622+ // Arrange - Create mock responses with open and closed issues
623+ using var mockHandler = new MockGitHubGraphQLHttpMessageHandler ( )
624+ . AddCommitsResponse ( new [ ] { "commit1" } )
625+ . AddReleasesResponse ( new [ ] { new MockRelease ( "v1.0.0" , "2024-01-01T00:00:00Z" ) } )
626+ . AddPullRequestsResponse ( Array . Empty < MockPullRequest > ( ) )
627+ . AddIssuesResponse ( new [ ]
628+ {
629+ new MockIssue (
630+ Number : 201 ,
631+ Title : "Known bug in feature X" ,
632+ Url : "https://github.com/test/repo/issues/201" ,
633+ State : "OPEN" ,
634+ Labels : new List < string > { "bug" } ) ,
635+ new MockIssue (
636+ Number : 202 ,
637+ Title : "Feature request for Y" ,
638+ Url : "https://github.com/test/repo/issues/202" ,
639+ State : "OPEN" ,
640+ Labels : new List < string > { "feature" } ) ,
641+ new MockIssue (
642+ Number : 203 ,
643+ Title : "Fixed bug" ,
644+ Url : "https://github.com/test/repo/issues/203" ,
645+ State : "CLOSED" ,
646+ Labels : new List < string > { "bug" } )
647+ } )
648+ . AddTagsResponse ( new [ ] { new MockTag ( "v1.0.0" , "commit1" ) } ) ;
649+
650+ using var mockHttpClient = new HttpClient ( mockHandler ) ;
651+ var connector = new MockableGitHubRepoConnector ( mockHttpClient ) ;
652+
653+ // Set up mock command responses
654+ connector . SetCommandResponse ( "git remote get-url origin" , "https://github.com/test/repo.git" ) ;
655+ connector . SetCommandResponse ( "git rev-parse --abbrev-ref HEAD" , "main" ) ;
656+ connector . SetCommandResponse ( "git rev-parse HEAD" , "commit1" ) ;
657+ connector . SetCommandResponse ( "gh auth token" , "test-token" ) ;
658+
659+ // Act
660+ var buildInfo = await connector . GetBuildInformationAsync ( Version . Create ( "v1.0.0" ) ) ;
661+
662+ // Assert
663+ Assert . IsNotNull ( buildInfo ) ;
664+
665+ // Known issues are open issues that aren't linked to any changes in this release
666+ Assert . IsNotNull ( buildInfo . KnownIssues ) ;
667+ // Since we have no PRs, all open issues should be known issues
668+ Assert . IsTrue ( buildInfo . KnownIssues . Count >= 1 , $ "Expected at least 1 known issue, got { buildInfo . KnownIssues . Count } ") ;
669+
670+ // Verify at least one known issue is present
671+ var knownIssueTitles = buildInfo . KnownIssues . Select ( i => i . Title ) . ToList ( ) ;
672+ Assert . IsTrue ( knownIssueTitles . Any ( t => t . Contains ( "Known bug" ) || t . Contains ( "Feature request" ) ) ,
673+ "Should have at least one of the open issues as a known issue" ) ;
674+ }
454675}
0 commit comments