diff --git a/Octokit.GraphQL.Core.UnitTests/ExpressionRewriterAssertions.cs b/Octokit.GraphQL.Core.UnitTests/ExpressionRewriterAssertions.cs index 9ccdfe16..1b121747 100644 --- a/Octokit.GraphQL.Core.UnitTests/ExpressionRewriterAssertions.cs +++ b/Octokit.GraphQL.Core.UnitTests/ExpressionRewriterAssertions.cs @@ -52,7 +52,7 @@ public static string ReplaceSubqueryPlaceholders(string expectedString, params s { foreach (var subqueryPlaceholderReplacement in subqueryPlaceholderReplacements) { - var regex = new Regex("PagingTests.subqueryPlaceholder"); + var regex = new Regex("SubqueryPlaceholder.placeholder"); expectedString = regex.Replace(expectedString, subqueryPlaceholderReplacement, 1); } diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests.cs deleted file mode 100644 index a9662c87..00000000 --- a/Octokit.GraphQL.Core.UnitTests/PagingTests.cs +++ /dev/null @@ -1,1602 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using AgileObjects.ReadableExpressions; -using Newtonsoft.Json.Linq; -using Octokit.GraphQL.Core.Builders; -using Octokit.GraphQL.Core.Syntax; -using Octokit.GraphQL.Core.UnitTests.Models; -using Xunit; - -namespace Octokit.GraphQL.Core.UnitTests -{ - public class PagingTests - { - static readonly ISubquery subqueryPlaceholder; - - public class Repository_Issues_AllPages - { - static Repository_Issues_AllPages() - { - ExpressionCompiler.IsUnitTesting = true; - } - - private ICompiledQuery> TestQuery(int? allPages = null) => - allPages.HasValue ? - new Query() - .Repository("foo", "bar") - .Issues() - .AllPages(allPages.Value) - .Select(issue => issue.Number) - .Compile() : - new Query() - .Repository("foo", "bar") - .Issues() - .AllPages() - .Select(issue => issue.Number) - .Compile(); - - private SimpleQuery> TestMasterQuery(int? allPages = null) => TestQuery(allPages).GetMasterQuery(); - - private IReadOnlyList TestQuerySubqueries(int? allPages = null) => TestQuery(allPages).GetSubqueries(); - - private SimpleSubquery> TestQueryFirstSubquery(int? allPages = null) => (SimpleSubquery>)TestQuerySubqueries(allPages).First(); - - [Fact] - public void Creates_MasterQuery() - { - var expected = @"query { - repository(owner: ""foo"", name: ""bar"") { - id - issues(first: 100) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - } -}"; - - Assert.Equal(expected, TestMasterQuery().ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_MasterQuery_CustomPageSize() - { - var expected = @"query { - repository(owner: ""foo"", name: ""bar"") { - id - issues(first: 10) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - } -}"; - - Assert.Equal(expected, TestMasterQuery(10).ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_MasterQuery_Expression() - { - Expression>> expected = data => - (IEnumerable)Rewritten.List.ToSubqueryList( - Rewritten.List.Select( - data["data"]["repository"]["issues"]["nodes"], - issue => issue["number"].ToObject()), - data.Annotation(), subqueryPlaceholder); - - ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, TestMasterQuery(10), "SimpleSubquery>"); - } - - [Fact] - public void Creates_Subquery() - { - var expected = @"query($__id: ID!, $__after: String) { - node(id: $__id) { - __typename - ... on Repository { - issues(first: 100, after: $__after) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - } - } -}"; - - Assert.Single(TestQuerySubqueries()); - Assert.Equal(expected, TestQueryFirstSubquery().ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_Subquery_CustomPageSize() - { - var expected = @"query($__id: ID!, $__after: String) { - node(id: $__id) { - __typename - ... on Repository { - issues(first: 10, after: $__after) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - } - } -}"; - - Assert.Single(TestQuerySubqueries(10)); - Assert.Equal(expected, TestQueryFirstSubquery(10).ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_Subquery_Expression() - { - Expression>> expected = - data => (IEnumerable)Rewritten.List.Select( - Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], - issue => issue["number"].ToObject()).ToList(); - var expectedString = expected.ToReadableString(); - - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery().ResultBuilder); - var actualString = actual.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_Expression_PageInfo() - { - Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); - var expectedString = expected.ToReadableString(); - - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery().PageInfo); - var actualString = actual.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_Expression_ParentId() - { - Expression>> expected = data => data.SelectTokens("$.data.repository.id"); - var expectedString = expected.ToReadableString(); - - var actualExpression = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery().ParentIds); - var actualString = actualExpression.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_Expression_ParentPage() - { - Expression>> expected = data => data.SelectTokens("$.data.repository.issues.pageInfo"); - var expectedString = expected.ToReadableString(); - - var actualExpression = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery().ParentPageInfo); - var actualString = actualExpression.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public async Task Reads_All_Pages() - { - int page = 0; - - string Execute(string _, IDictionary variables) - { - switch (page++) - { - case 0: - Assert.Null(variables); - return @"{ - data: { - ""repository"": { - ""id"": ""repoid"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": true, - ""endCursor"": ""end0"" - }, - ""nodes"": [ - { ""number"": 0 }, - { ""number"": 1 }, - { ""number"": 2 }, - ] - } - } - } -}"; - case 1: - Assert.NotNull(variables); - Assert.Equal(variables["__id"], "repoid"); - Assert.Equal(variables["__after"], "end0"); - return @"{ - data: { - ""node"": { - ""__typename"": ""Repository"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": true, - ""endCursor"": ""end1"" - }, - ""nodes"": [ - { ""number"": 3 }, - { ""number"": 4 }, - ] - } - } - } -}"; - case 2: - Assert.NotNull(variables); - Assert.Equal(variables["__after"], "end1"); - return @"{ - data: { - ""node"": { - ""__typename"": ""Repository"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": false, - ""endCursor"": ""end2"" - }, - ""nodes"": [ - { ""number"": 5 }, - ] - } - } - } -}"; - default: - throw new NotSupportedException("Should not get here"); - } - } - - var connection = new MockConnection(Execute); - var result = (await connection.Run(TestQuery())).ToList(); - - Assert.Equal(Enumerable.Range(0, 6).ToList(), result); - } - } - - public class Repository_Name_Issues_AllPages - { - static Repository_Name_Issues_AllPages() - { - ExpressionCompiler.IsUnitTesting = true; - } - - private ICompiledQuery TestQuery => new Query() - .Repository("foo", "bar") - .Select(repository => new RepositoryModel - { - Name = repository.Name, - Issues = repository.Issues(null, null, null, null, new[] { "bug" }).AllPages().Select(issue => new IssueModel - { - Number = issue.Number, - }).ToList() - }).Compile(); - - private SimpleQuery TestMasterQuery => TestQuery.GetMasterQuery(); - - private IReadOnlyList TestQuerySubqueries => TestQuery.GetSubqueries(); - - private SimpleSubquery> TestQueryFirstSubquery => (SimpleSubquery>)TestQuerySubqueries.First(); - - [Fact] - public void Creates_MasterQuery() - { - var expected = @"query { - repository(owner: ""foo"", name: ""bar"") { - id - name - issues(labels: [""bug""], first: 100) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - } -}"; - - Assert.Equal(expected, TestMasterQuery.ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_MasterQuery_Expression() - { - Expression> expected = data => - Rewritten.Value.Select( - data["data"]["repository"], - repository => new RepositoryModel - { - Name = repository["name"].ToObject(), - Issues = Rewritten.List.ToSubqueryList( - Rewritten.List.Select( - repository["issues"]["nodes"], - issue => new IssueModel - { - Number = issue["number"].ToObject(), - }), - data.Annotation(), - subqueryPlaceholder), - }); - - ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, TestMasterQuery, - "SimpleSubquery>"); - } - - [Fact] - public void Creates_Subquery() - { - var expected = @"query($__id: ID!, $__after: String) { - node(id: $__id) { - __typename - ... on Repository { - issues(first: 100, after: $__after, labels: [""bug""]) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - } - } -}"; - - Assert.Single(TestQuerySubqueries); - Assert.Equal(expected, TestQueryFirstSubquery.ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_Subquery_Expression() - { - Expression>> expected = - data => (IEnumerable)Rewritten.List.Select( - Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], - issue => new IssueModel - { - Number = issue["number"].ToObject() - }).ToList(); - var expectedString = expected.ToReadableString(); - - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ResultBuilder); - var actualString = actual.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_Expression_PageInfo() - { - Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); - var expectedString = expected.ToReadableString(); - - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.PageInfo); - var actualString = actual.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_Expression_ParentId() - { - Expression>> expected = data => data.SelectTokens("$.data.repository.id"); - var expectedString = expected.ToReadableString(); - - var actualExpression = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ParentIds); - var actualString = actualExpression.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_Expression_ParentPage() - { - Expression>> expected = data => data.SelectTokens("$.data.repository.issues.pageInfo"); - var expectedString = expected.ToReadableString(); - - var actualExpression = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ParentPageInfo); - var actualString = actualExpression.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public async Task Reads_All_Pages() - { - int page = 0; - - string Execute(string _, IDictionary variables) - { - switch (page++) - { - case 0: - Assert.Null(variables); - return @"{ - data: { - ""repository"": { - ""id"": ""repoid"", - ""name"": ""foo"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": true, - ""endCursor"": ""end0"" - }, - ""nodes"": [ - { ""number"": 0 }, - { ""number"": 1 }, - { ""number"": 2 }, - ] - } - } - } -}"; - case 1: - Assert.NotNull(variables); - Assert.Equal(variables["__id"], "repoid"); - Assert.Equal(variables["__after"], "end0"); - return @"{ - data: { - ""node"": { - ""__typename"": ""Repository"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": false, - ""endCursor"": ""end1"" - }, - ""nodes"": [ - { ""number"": 3 }, - { ""number"": 4 }, - ] - } - } - } -}"; - default: - throw new NotSupportedException("Should not get here"); - } - } - - var connection = new MockConnection(Execute); - var result = await connection.Run(TestQuery); - - Assert.Equal( - Enumerable.Range(0, 5).ToList(), - result.Issues.Select(x => x.Number).ToList()); - } - } - - public class Repository_Name_Issues_AllPages_To_Dictionary - { - static Repository_Name_Issues_AllPages_To_Dictionary() - { - ExpressionCompiler.IsUnitTesting = true; - } - - private ICompiledQuery TestQuery => new Query() - .Repository("foo", "bar") - .Select(repository => new RepositoryModelWithDictionary - { - Name = repository.Name, - Issues = repository.Issues(null, null, null, null, new[] { "bug" }).AllPages().Select(issue => new IssueModel - { - Number = issue.Number, - }).ToDictionary(x => x.Number, x => x) - }).Compile(); - - private SimpleQuery TestMasterQuery => TestQuery.GetMasterQuery(); - - private IReadOnlyList TestQuerySubqueries => TestQuery.GetSubqueries(); - - private SimpleSubquery> TestQueryFirstSubquery => (SimpleSubquery>)TestQuerySubqueries.First(); - - [Fact] - public void Creates_MasterQuery() - { - var expected = @"query { - repository(owner: ""foo"", name: ""bar"") { - id - name - issues(labels: [""bug""], first: 100) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - } -}"; - - Assert.Equal(expected, TestMasterQuery.ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_MasterQuery_Expression() - { - Expression> expected = data => - Rewritten.Value.Select( - data["data"]["repository"], - repository => new RepositoryModelWithDictionary - { - Name = repository["name"].ToObject(), - Issues = (IDictionary)Rewritten.List.ToSubqueryDictionary( - Rewritten.List.Select( - repository["issues"]["nodes"], - issue => new PagingTests.IssueModel - { - Number = issue["number"].ToObject() - }), - data.Annotation(), - subqueryPlaceholder, - x => x.Number, - x => x) - }); - - ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, TestMasterQuery, - "SimpleSubquery>"); - } - - [Fact] - public void Creates_Subquery() - { - var expected = @"query($__id: ID!, $__after: String) { - node(id: $__id) { - __typename - ... on Repository { - issues(first: 100, after: $__after, labels: [""bug""]) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - } - } -}"; - - Assert.Single(TestQuerySubqueries); - Assert.Equal(expected, TestQueryFirstSubquery.ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_Subquery_Expression() - { - Expression>> expected = - data => (IEnumerable)Rewritten.List.Select( - Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], - issue => new IssueModel - { - Number = issue["number"].ToObject() - }).ToList(); - var expectedString = expected.ToReadableString(); - - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ResultBuilder); - var actualString = actual.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_Expression_PageInfo() - { - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.PageInfo); - var actualString = actual.ToReadableString(); - - Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_Expression_ParentId() - { - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ParentIds); - var actualString = actual.ToReadableString(); - - Expression>> expected = data => data.SelectTokens("$.data.repository.id"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_Expression_ParentPageInfo() - { - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ParentPageInfo); - var actualString = actual.ToReadableString(); - - Expression>> expected = data => data.SelectTokens("$.data.repository.issues.pageInfo"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public async Task Reads_All_Pages() - { - int page = 0; - - string Execute(string _, IDictionary variables) - { - switch (page++) - { - case 0: - Assert.Null(variables); - return @"{ - data: { - ""repository"": { - ""id"": ""repoid"", - ""name"": ""foo"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": true, - ""endCursor"": ""end0"" - }, - ""nodes"": [ - { ""number"": 0 }, - { ""number"": 1 }, - { ""number"": 2 }, - ] - } - } - } -}"; - case 1: - Assert.NotNull(variables); - Assert.Equal(variables["__id"], "repoid"); - Assert.Equal(variables["__after"], "end0"); - return @"{ - data: { - ""node"": { - ""__typename"": ""Repository"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": false, - ""endCursor"": ""end1"" - }, - ""nodes"": [ - { ""number"": 3 }, - { ""number"": 4 }, - ] - } - } - } -}"; - default: - throw new NotSupportedException("Should not get here"); - } - } - - var connection = new MockConnection(Execute); - var result = await connection.Run(TestQuery); - - Assert.Equal( - Enumerable.Range(0, 5).ToList(), - result.Issues.Select(x => x.Key).ToList()); - } - } - - public class Repository_Issues_Comments_AllPages - { - static Repository_Issues_Comments_AllPages() - { - ExpressionCompiler.IsUnitTesting = true; - } - - private ICompiledQuery> TestQuery { get; } = new Query() - .Repository("foo", "bar") - .Issues() - .AllPages() - .Select(issue => new IssueModel - { - Number = issue.Number, - Comments = issue.Comments(null, null, null, null).AllPages().Select(comment => new CommentModel - { - Id = comment.Id.ToString(), - Body = comment.Body, - }).ToList(), - }) - .Compile(); - - private SimpleQuery> TestMasterQuery => TestQuery.GetMasterQuery(); - - private IReadOnlyList TestQuerySubqueries => TestQuery.GetSubqueries(); - - private PagedSubquery> TestQueryFirstSubquery => (PagedSubquery>)TestQuerySubqueries.First(); - - private SimpleQuery> TestQueryFirstSubqueryMasterQuery => TestQueryFirstSubquery.GetMasterQuery(); - - private IReadOnlyList TestQueryFirstSubquerySubqueries => TestQueryFirstSubquery.GetSubqueries(); - - private SimpleSubquery> TestQueryFirstSubqueryFirstSubquery => (SimpleSubquery>)TestQueryFirstSubquerySubqueries[0]; - - private SimpleSubquery> TestQuerySecondSubquery => (SimpleSubquery>)TestQuerySubqueries.Skip(1).First(); - - [Fact] - public void Creates_MasterQuery() - { - var expected = @"query { - repository(owner: ""foo"", name: ""bar"") { - id - issues(first: 100) { - pageInfo { - hasNextPage - endCursor - } - nodes { - id - number - comments(first: 100) { - pageInfo { - hasNextPage - endCursor - } - nodes { - id - body - } - } - } - } - } -}"; - - Assert.Equal(expected, TestMasterQuery.ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_MasterQuery_Expression() - { - Expression>> expected = data => - (IEnumerable)Rewritten.List.ToSubqueryList( - Rewritten.List.Select( - data["data"]["repository"]["issues"]["nodes"], - issue => new IssueModel - { - Number = issue["number"].ToObject(), - Comments = Rewritten.List.ToSubqueryList( - Rewritten.List.Select( - issue["comments"]["nodes"], - comment => new CommentModel - { - Id = comment["id"].ToString(), - Body = comment["body"].ToObject(), - }), - data.Annotation(), - subqueryPlaceholder) - }), - data.Annotation(), - subqueryPlaceholder); - - ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, TestMasterQuery, - "SimpleSubquery>", - "PagedSubquery>"); - } - - [Fact] - public void Creates_PagedSubquery_1_MasterQuery() - { - Assert.Equal(2, TestQuerySubqueries.Count); - - var expected = @"query($__id: ID!, $__after: String) { - node(id: $__id) { - __typename - ... on Repository { - issues(first: 100, after: $__after) { - pageInfo { - hasNextPage - endCursor - } - nodes { - id - number - comments(first: 100) { - pageInfo { - hasNextPage - endCursor - } - nodes { - id - body - } - } - } - } - } - } -}"; - - Assert.Equal(expected, TestQueryFirstSubqueryMasterQuery.ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_PagedSubquery_1_MasterQuery_Expression() - { - Expression>> expected = - data => (IEnumerable)Rewritten.List.Select( - Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], - issue => new IssueModel - { - Number = issue["number"].ToObject(), - Comments = Rewritten.List.ToSubqueryList( - Rewritten.List.Select( - issue["comments"]["nodes"], - comment => new PagingTests.CommentModel - { - Id = comment["id"].ToString(), - Body = comment["body"].ToObject() - }), - data.Annotation(), - subqueryPlaceholder) - }).ToList(); - var expectedString = ExpressionRewriterAssertions.ReplaceSubqueryPlaceholders(expected.ToReadableString(), "SimpleSubquery>"); - - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryMasterQuery.ResultBuilder); - var actualString = actual.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_PagedSubquery_1_SubQuery_Expression() - { - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryFirstSubquery.ResultBuilder); - var actualString = actual.ToReadableString(); - - Expression>> expected = - data => (IEnumerable)Rewritten.List.Select( - Rewritten.Interface.Cast(data["data"]["node"], "Issue")["comments"]["nodes"], - comment => new CommentModel - { - Id = comment["id"].ToString(), - Body = comment["body"].ToObject() - }).ToList(); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_PagedSubquery_1_SubQuery_Expression_PageInfo() - { - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryFirstSubquery.PageInfo); - var actualString = actual.ToReadableString(); - - Expression> expected = data => data.SelectToken("data.node.comments.pageInfo"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_PagedSubquery_1_SubQuery_Expression_ParentIds() - { - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryFirstSubquery.ParentIds); - var actualString = actual.ToReadableString(); - - Expression>> expected = data => data.SelectTokens("$.data.node.issues.nodes.[*].id"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_PagedSubquery_1_SubQuery_Expression_ParentPageInfo() - { - var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryFirstSubquery.ParentPageInfo); - var actualString = actual.ToReadableString(); - - Expression>> expected = data => data.SelectTokens("$.data.node.issues.nodes.[*].comments.pageInfo"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_2_MasterQuery() - { - var expected = @"query($__id: ID!, $__after: String) { - node(id: $__id) { - __typename - ... on Issue { - comments(first: 100, after: $__after) { - pageInfo { - hasNextPage - endCursor - } - nodes { - id - body - } - } - } - } -}"; - - Assert.Equal(2, TestQuerySubqueries.Count); - Assert.Equal(expected, TestQuerySecondSubquery.ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_Subquery_2_MasterQuery_Expression() - { - Expression>> expected = - data => (IEnumerable)Rewritten.List.Select( - Rewritten.Interface.Cast(data["data"]["node"], "Issue")["comments"]["nodes"], - comment => new CommentModel - { - Id = comment["id"].ToString(), - Body = comment["body"].ToObject() - }).ToList(); - var expectedString = expected.ToReadableString(); - - var actual = ExpressionCompiler.GetSourceExpression(TestQuerySecondSubquery.ResultBuilder); - var actualString = actual.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public async Task Reads_All_Pages() - { - int page = 0; - - string CreateComment(int index) - { - return @" - { - id: ""comment" + index + @""", - body: ""comment " + index + @""" - }"; - } - - string CreateIssue(int number, int commentCount, bool hasNextPage) - { - return @" - { - id: ""issue" + number + @""", - number: " + number + @", - comments: { - ""pageInfo"": { - ""hasNextPage"": " + hasNextPage.ToString().ToLower() + @", - ""endCursor"": """ + "comment_end" + number + @""" - }, - ""nodes"": [" + - string.Join(",", Enumerable.Range(number * 10, commentCount).Select(x => CreateComment(x))) + @" - ] - } - }"; - } - - string Execute(string _, IDictionary variables) - { - switch (page++) - { - case 0: - Assert.Null(variables); - return @"{ - data: { - ""repository"": { - ""id"": ""repoid"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": true, - ""endCursor"": ""issue_end0"" - }, - ""nodes"": [" + - CreateIssue(1, 1, false) + "," + - CreateIssue(2, 3, true) + "," + - CreateIssue(3, 0, false) + @" - ] - } - } - } -}"; - case 1: - Assert.NotNull(variables); - Assert.Equal("issue2", variables["__id"]); - Assert.Equal("comment_end2", variables["__after"]); - return @"{ - data: { - ""node"": { - ""__typename"": ""Issue"", - ""comments"": { - ""pageInfo"": { - ""hasNextPage"": false, - ""endCursor"": ""comment_end"" - }, - ""nodes"": [" + - CreateComment(23) + "," + - CreateComment(24) + @" - ] - } - } - } -}"; - - case 2: - Assert.NotNull(variables); - Assert.Equal("repoid", variables["__id"]); - Assert.Equal("issue_end0", variables["__after"]); - return @"{ - data: { - ""node"": { - ""__typename"": ""Repository"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": false, - ""endCursor"": ""issue_end1"" - }, - ""nodes"": [" + - CreateIssue(4, 0, false) + "," + - CreateIssue(5, 0, false) + @" - ] - } - } - } -}"; - - default: - throw new NotSupportedException("Should not get here"); - } - } - - var connection = new MockConnection(Execute); - var result = (await connection.Run(TestQuery)).ToList(); - - Assert.Equal(Enumerable.Range(1, 5).ToList(), result.Select(x => x.Number).ToList()); - Assert.Single(result[0].Comments); - Assert.Equal(5, result[1].Comments.Count); - Assert.Empty(result[2].Comments); - Assert.Empty(result[3].Comments); - Assert.Empty(result[4].Comments); - } - - [Fact] - public async Task Handles_Empty_Nodes() - { - int page = 0; - - string Execute(string query, IDictionary variables) - { - switch (page++) - { - case 0: - Assert.Null(variables); - return @"{ - data: { - ""repository"": { - ""id"": ""repoid"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": false, - ""endCursor"": ""issue_end0"" - }, - ""nodes"": [] - } - } - } -}"; - default: - throw new NotSupportedException("Should not get here"); - } - } - - var connection = new MockConnection(Execute); - var result = (await connection.Run(TestQuery)).ToList(); - - Assert.Empty(result); - } - } - - public class Repository_Issues_ComplexQuery_AllPages - { - static Repository_Issues_ComplexQuery_AllPages() - { - ExpressionCompiler.IsUnitTesting = true; - } - - [Fact] - public void Creates_MasterQuery() - { - var expected = @"query { - repository(owner: ""foo"", name: ""bar"") { - id - intList1: issues(first: 100) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - intList2: issues(first: 100) { - pageInfo { - hasNextPage - endCursor - } - nodes { - number - } - } - } -}"; - - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var masterQuery = query.GetMasterQuery(); - - Assert.Equal(expected, masterQuery.ToString(), ignoreLineEndingDifferences: true); - } - - [Fact] - public void Creates_MasterQuery_Expression() - { - Expression> expected = data => - Rewritten.Value.Select( - data["data"]["repository"], - repository => new { - IntList1 = Rewritten.List.ToSubqueryList( - Rewritten.List.ToList(Rewritten.List.Select(repository["intList1"]["nodes"], issue => issue["number"])), - data.Annotation(), - subqueryPlaceholder), - IntList2 = Rewritten.List.ToSubqueryList( - Rewritten.List.ToList(Rewritten.List.Select(repository["intList2"]["nodes"], issue => issue["number"])), - data.Annotation(), - subqueryPlaceholder) - }); - - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var masterQuery = query.GetMasterQuery(); - - ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, masterQuery, - "SimpleSubquery>", - "SimpleSubquery>"); - } - - [Fact] - public void Creates_Subquery_1_Expression() - { - Expression>> expected = - data => (IEnumerable)Rewritten.List.Select( - Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], - issue => issue["number"].ToObject()).ToList(); - var expectedString = expected.ToReadableString(); - - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var subqueries = query.GetSubqueries(); - var queryFirstSubquery = (SimpleSubquery>) subqueries[0]; - - var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ResultBuilder); - var actualString = actual.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_1_Expression_PageInfo() - { - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var subqueries = query.GetSubqueries(); - var queryFirstSubquery = (SimpleSubquery>)subqueries[0]; - - var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.PageInfo); - var actualString = actual.ToReadableString(); - - Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_1_Expression_ParentId() - { - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var subqueries = query.GetSubqueries(); - var queryFirstSubquery = (SimpleSubquery>)subqueries[0]; - - var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ParentIds); - var actualString = actual.ToReadableString(); - - Expression>> expected = data => data.SelectTokens("$.data.repository.id"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_1_Expression_ParentPageInfo() - { - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var subqueries = query.GetSubqueries(); - var queryFirstSubquery = (SimpleSubquery>)subqueries[0]; - - var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ParentPageInfo); - var actualString = actual.ToReadableString(); - - Expression>> expected = data => data.SelectTokens("$.data.repository.intList1.pageInfo"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_2_Expression() - { - Expression>> expected = - data => (IEnumerable)Rewritten.List.Select( - Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], - issue => issue["number"].ToObject()).ToList(); - var expectedString = expected.ToReadableString(); - - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var subqueries = query.GetSubqueries(); - var queryFirstSubquery = (SimpleSubquery>) subqueries[1]; - - var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ResultBuilder); - var actualString = actual.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_2_Expression_PageInfo() - { - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var subqueries = query.GetSubqueries(); - var queryFirstSubquery = (SimpleSubquery>)subqueries[1]; - - var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.PageInfo); - var actualString = actual.ToReadableString(); - - Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_2_Expression_ParentId() - { - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var subqueries = query.GetSubqueries(); - var queryFirstSubquery = (SimpleSubquery>)subqueries[1]; - - var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ParentIds); - var actualString = actual.ToReadableString(); - - Expression>> expected = data => data.SelectTokens("$.data.repository.id"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public void Creates_Subquery_2_Expression_ParentPageInfo() - { - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - var subqueries = query.GetSubqueries(); - var queryFirstSubquery = (SimpleSubquery>)subqueries[1]; - - var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ParentPageInfo); - var actualString = actual.ToReadableString(); - - Expression>> expected = data => data.SelectTokens("$.data.repository.intList2.pageInfo"); - var expectedString = expected.ToReadableString(); - - Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); - } - - [Fact] - public async Task Reads_All_Pages() - { - int page = 0; - - string Execute(string _, IDictionary variables) - { - switch (page++) - { - case 0: - Assert.Null(variables); - return @"{ - ""data"": { - ""repository"": { - ""id"": ""repoId"", - ""intList1"": { - ""pageInfo"": { - ""hasNextPage"": true, - ""endCursor"": ""endCursor1"" - }, - ""nodes"": [{ - ""number"": 1 - }, { - ""number"": 2 - }, { - ""number"": 3 - } - ] - }, - ""intList2"": { - ""pageInfo"": { - ""hasNextPage"": true, - ""endCursor"": ""endCursor2"" - }, - ""nodes"": [{ - ""number"": 4 - }, { - ""number"": 5 - }, { - ""number"": 6 - } - ] - } - } - } -}"; - case 1: - Assert.NotNull(variables); - Assert.Equal("repoId", variables["__id"]); - Assert.Equal("endCursor2", variables["__after"]); - - return @"{ - ""data"": { - ""node"": { - ""__typename"": ""Repository"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": false, - ""endCursor"": ""endCursor2.1"" - }, - ""nodes"": [{ - ""number"": 4 - }, { - ""number"": 5 - } - ] - } - } - } -}"; - case 2: - Assert.NotNull(variables); - Assert.Equal("repoId", variables["__id"]); - Assert.Equal("endCursor1", variables["__after"]); - - return @"{ - ""data"": { - ""node"": { - ""__typename"": ""Repository"", - ""issues"": { - ""pageInfo"": { - ""hasNextPage"": false, - ""endCursor"": ""endCursor1.1"" - }, - ""nodes"": [{ - ""number"": 4 - }, { - ""number"": 5 - }, { - ""number"": 6 - } - ] - } - } - } -}"; - - default: - throw new NotSupportedException("Should not get here"); - } - } - - var query = new Query() - .Repository("foo", "bar") - .Select(repository => new - { - IntList1 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList(), - IntList2 = repository.Issues(null, null, null, null, null) - .AllPages() - .Select(issue => issue.Number) - .ToList() - }).Compile(); - - - var connection = new MockConnection(Execute); - var result = (await connection.Run(query)); - - Assert.Equal(6, result.IntList1.Count); - Assert.Equal(5, result.IntList2.Count); - } - - } - - class RepositoryModel - { - public string Name { get; set; } - public IList Issues { get; set; } - } - - class RepositoryModelWithDictionary - { - public string Name { get; set; } - public IDictionary Issues { get; set; } - } - - class IssueModel - { - public int Number { get; set; } - public IList Comments { get; set; } - } - - class CommentModel - { - public string Id { get; set; } - public string Body { get; set; } - } - } -} diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/CommentModel.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/CommentModel.cs new file mode 100644 index 00000000..21517839 --- /dev/null +++ b/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/CommentModel.cs @@ -0,0 +1,8 @@ +namespace Octokit.GraphQL.Core.UnitTests.PagingTests.Models +{ + class CommentModel + { + public string Id { get; set; } + public string Body { get; set; } + } +} diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/IssueModel.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/IssueModel.cs new file mode 100644 index 00000000..e0034752 --- /dev/null +++ b/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/IssueModel.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Octokit.GraphQL.Core.UnitTests.PagingTests.Models +{ + class IssueModel + { + public int Number { get; set; } + public IList Comments { get; set; } + } +} diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/RepositoryModel.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/RepositoryModel.cs new file mode 100644 index 00000000..98a5fe4f --- /dev/null +++ b/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/RepositoryModel.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Octokit.GraphQL.Core.UnitTests.PagingTests.Models +{ + class RepositoryModel + { + public string Name { get; set; } + public IList Issues { get; set; } + } +} diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/RepositoryModelWithDictionary.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/RepositoryModelWithDictionary.cs new file mode 100644 index 00000000..4de9c6cc --- /dev/null +++ b/Octokit.GraphQL.Core.UnitTests/PagingTests/Models/RepositoryModelWithDictionary.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Octokit.GraphQL.Core.UnitTests.PagingTests.Models +{ + class RepositoryModelWithDictionary + { + public string Name { get; set; } + public IDictionary Issues { get; set; } + } +} diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryIssuesAllPagesTests.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryIssuesAllPagesTests.cs new file mode 100644 index 00000000..d1ab9bda --- /dev/null +++ b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryIssuesAllPagesTests.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using AgileObjects.ReadableExpressions; +using Newtonsoft.Json.Linq; +using Octokit.GraphQL.Core.Builders; +using Octokit.GraphQL.Core.UnitTests.Models; +using Xunit; + +namespace Octokit.GraphQL.Core.UnitTests +{ + internal class SubqueryPlaceholder + { + public static readonly ISubquery placeholder; + } + + public class Repository_Issues_AllPages + { + static Repository_Issues_AllPages() + { + ExpressionCompiler.IsUnitTesting = true; + } + + private ICompiledQuery> TestQuery(int? allPages = null) => + allPages.HasValue ? + new Query() + .Repository("foo", "bar") + .Issues() + .AllPages(allPages.Value) + .Select(issue => issue.Number) + .Compile() : + new Query() + .Repository("foo", "bar") + .Issues() + .AllPages() + .Select(issue => issue.Number) + .Compile(); + + private SimpleQuery> TestMasterQuery(int? allPages = null) => TestQuery(allPages).GetMasterQuery(); + + private IReadOnlyList TestQuerySubqueries(int? allPages = null) => TestQuery(allPages).GetSubqueries(); + + private SimpleSubquery> TestQueryFirstSubquery(int? allPages = null) => (SimpleSubquery>)TestQuerySubqueries(allPages).First(); + + [Fact] + public void Creates_MasterQuery() + { + var expected = @"query { + repository(owner: ""foo"", name: ""bar"") { + id + issues(first: 100) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + } +}"; + + Assert.Equal(expected, TestMasterQuery().ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_MasterQuery_CustomPageSize() + { + var expected = @"query { + repository(owner: ""foo"", name: ""bar"") { + id + issues(first: 10) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + } +}"; + + Assert.Equal(expected, TestMasterQuery(10).ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_MasterQuery_Expression() + { + Expression>> expected = data => + (IEnumerable)Rewritten.List.ToSubqueryList( + Rewritten.List.Select( + data["data"]["repository"]["issues"]["nodes"], + issue => issue["number"].ToObject()), + data.Annotation(), SubqueryPlaceholder.placeholder); + + ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, TestMasterQuery(10), "SimpleSubquery>"); + } + + [Fact] + public void Creates_Subquery() + { + var expected = @"query($__id: ID!, $__after: String) { + node(id: $__id) { + __typename + ... on Repository { + issues(first: 100, after: $__after) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + } + } +}"; + + Assert.Single(TestQuerySubqueries()); + Assert.Equal(expected, TestQueryFirstSubquery().ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_Subquery_CustomPageSize() + { + var expected = @"query($__id: ID!, $__after: String) { + node(id: $__id) { + __typename + ... on Repository { + issues(first: 10, after: $__after) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + } + } +}"; + + Assert.Single(TestQuerySubqueries(10)); + Assert.Equal(expected, TestQueryFirstSubquery(10).ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_Subquery_Expression() + { + Expression>> expected = + data => (IEnumerable)Rewritten.List.Select( + Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], + issue => issue["number"].ToObject()).ToList(); + var expectedString = expected.ToReadableString(); + + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery().ResultBuilder); + var actualString = actual.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_Expression_PageInfo() + { + Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); + var expectedString = expected.ToReadableString(); + + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery().PageInfo); + var actualString = actual.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_Expression_ParentId() + { + Expression>> expected = data => data.SelectTokens("$.data.repository.id"); + var expectedString = expected.ToReadableString(); + + var actualExpression = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery().ParentIds); + var actualString = actualExpression.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_Expression_ParentPage() + { + Expression>> expected = data => data.SelectTokens("$.data.repository.issues.pageInfo"); + var expectedString = expected.ToReadableString(); + + var actualExpression = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery().ParentPageInfo); + var actualString = actualExpression.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public async Task Reads_All_Pages() + { + int page = 0; + + string Execute(string _, IDictionary variables) + { + switch (page++) + { + case 0: + Assert.Null(variables); + return @"{ + data: { + ""repository"": { + ""id"": ""repoid"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": true, + ""endCursor"": ""end0"" + }, + ""nodes"": [ + { ""number"": 0 }, + { ""number"": 1 }, + { ""number"": 2 }, + ] + } + } + } +}"; + case 1: + Assert.NotNull(variables); + Assert.Equal(variables["__id"], "repoid"); + Assert.Equal(variables["__after"], "end0"); + return @"{ + data: { + ""node"": { + ""__typename"": ""Repository"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": true, + ""endCursor"": ""end1"" + }, + ""nodes"": [ + { ""number"": 3 }, + { ""number"": 4 }, + ] + } + } + } +}"; + case 2: + Assert.NotNull(variables); + Assert.Equal(variables["__after"], "end1"); + return @"{ + data: { + ""node"": { + ""__typename"": ""Repository"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": false, + ""endCursor"": ""end2"" + }, + ""nodes"": [ + { ""number"": 5 }, + ] + } + } + } +}"; + default: + throw new NotSupportedException("Should not get here"); + } + } + + var connection = new MockConnection(Execute); + var result = (await connection.Run(TestQuery())).ToList(); + + Assert.Equal(Enumerable.Range(0, 6).ToList(), result); + } + } +} \ No newline at end of file diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryIssuesCommentsAllPagesTests.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryIssuesCommentsAllPagesTests.cs new file mode 100644 index 00000000..bae93482 --- /dev/null +++ b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryIssuesCommentsAllPagesTests.cs @@ -0,0 +1,427 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using AgileObjects.ReadableExpressions; +using Newtonsoft.Json.Linq; +using Octokit.GraphQL.Core.Builders; +using Octokit.GraphQL.Core.UnitTests.Models; +using Octokit.GraphQL.Core.UnitTests.PagingTests.Models; +using Xunit; + +namespace Octokit.GraphQL.Core.UnitTests +{ + public class Repository_Issues_Comments_AllPages + { + static Repository_Issues_Comments_AllPages() + { + ExpressionCompiler.IsUnitTesting = true; + } + + private ICompiledQuery> TestQuery { get; } = new Query() + .Repository("foo", "bar") + .Issues() + .AllPages() + .Select(issue => new IssueModel + { + Number = issue.Number, + Comments = issue.Comments(null, null, null, null).AllPages().Select(comment => new CommentModel + { + Id = comment.Id.ToString(), + Body = comment.Body, + }).ToList(), + }) + .Compile(); + + private SimpleQuery> TestMasterQuery => TestQuery.GetMasterQuery(); + + private IReadOnlyList TestQuerySubqueries => TestQuery.GetSubqueries(); + + private PagedSubquery> TestQueryFirstSubquery => (PagedSubquery>)TestQuerySubqueries.First(); + + private SimpleQuery> TestQueryFirstSubqueryMasterQuery => TestQueryFirstSubquery.GetMasterQuery(); + + private IReadOnlyList TestQueryFirstSubquerySubqueries => TestQueryFirstSubquery.GetSubqueries(); + + private SimpleSubquery> TestQueryFirstSubqueryFirstSubquery => (SimpleSubquery>)TestQueryFirstSubquerySubqueries[0]; + + private SimpleSubquery> TestQuerySecondSubquery => (SimpleSubquery>)TestQuerySubqueries.Skip(1).First(); + + [Fact] + public void Creates_MasterQuery() + { + var expected = @"query { + repository(owner: ""foo"", name: ""bar"") { + id + issues(first: 100) { + pageInfo { + hasNextPage + endCursor + } + nodes { + id + number + comments(first: 100) { + pageInfo { + hasNextPage + endCursor + } + nodes { + id + body + } + } + } + } + } +}"; + + Assert.Equal(expected, TestMasterQuery.ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_MasterQuery_Expression() + { + Expression>> expected = data => + (IEnumerable)Rewritten.List.ToSubqueryList( + Rewritten.List.Select( + data["data"]["repository"]["issues"]["nodes"], + issue => new IssueModel + { + Number = issue["number"].ToObject(), + Comments = Rewritten.List.ToSubqueryList( + Rewritten.List.Select( + issue["comments"]["nodes"], + comment => new CommentModel + { + Id = comment["id"].ToString(), + Body = comment["body"].ToObject(), + }), + data.Annotation(), + SubqueryPlaceholder.placeholder) + }), + data.Annotation(), + SubqueryPlaceholder.placeholder); + + ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, TestMasterQuery, + "SimpleSubquery>", + "PagedSubquery>"); + } + + [Fact] + public void Creates_PagedSubquery_1_MasterQuery() + { + Assert.Equal(2, TestQuerySubqueries.Count); + + var expected = @"query($__id: ID!, $__after: String) { + node(id: $__id) { + __typename + ... on Repository { + issues(first: 100, after: $__after) { + pageInfo { + hasNextPage + endCursor + } + nodes { + id + number + comments(first: 100) { + pageInfo { + hasNextPage + endCursor + } + nodes { + id + body + } + } + } + } + } + } +}"; + + Assert.Equal(expected, TestQueryFirstSubqueryMasterQuery.ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_PagedSubquery_1_MasterQuery_Expression() + { + Expression>> expected = + data => (IEnumerable)Rewritten.List.Select( + Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], + issue => new IssueModel + { + Number = issue["number"].ToObject(), + Comments = Rewritten.List.ToSubqueryList( + Rewritten.List.Select( + issue["comments"]["nodes"], + comment => new CommentModel + { + Id = comment["id"].ToString(), + Body = comment["body"].ToObject() + }), + data.Annotation(), + SubqueryPlaceholder.placeholder) + }).ToList(); + var expectedString = ExpressionRewriterAssertions.ReplaceSubqueryPlaceholders(expected.ToReadableString(), "SimpleSubquery>"); + + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryMasterQuery.ResultBuilder); + var actualString = actual.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_PagedSubquery_1_SubQuery_Expression() + { + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryFirstSubquery.ResultBuilder); + var actualString = actual.ToReadableString(); + + Expression>> expected = + data => (IEnumerable)Rewritten.List.Select( + Rewritten.Interface.Cast(data["data"]["node"], "Issue")["comments"]["nodes"], + comment => new CommentModel + { + Id = comment["id"].ToString(), + Body = comment["body"].ToObject() + }).ToList(); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_PagedSubquery_1_SubQuery_Expression_PageInfo() + { + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryFirstSubquery.PageInfo); + var actualString = actual.ToReadableString(); + + Expression> expected = data => data.SelectToken("data.node.comments.pageInfo"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_PagedSubquery_1_SubQuery_Expression_ParentIds() + { + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryFirstSubquery.ParentIds); + var actualString = actual.ToReadableString(); + + Expression>> expected = data => data.SelectTokens("$.data.node.issues.nodes.[*].id"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_PagedSubquery_1_SubQuery_Expression_ParentPageInfo() + { + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubqueryFirstSubquery.ParentPageInfo); + var actualString = actual.ToReadableString(); + + Expression>> expected = data => data.SelectTokens("$.data.node.issues.nodes.[*].comments.pageInfo"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_2_MasterQuery() + { + var expected = @"query($__id: ID!, $__after: String) { + node(id: $__id) { + __typename + ... on Issue { + comments(first: 100, after: $__after) { + pageInfo { + hasNextPage + endCursor + } + nodes { + id + body + } + } + } + } +}"; + + Assert.Equal(2, TestQuerySubqueries.Count); + Assert.Equal(expected, TestQuerySecondSubquery.ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_Subquery_2_MasterQuery_Expression() + { + Expression>> expected = + data => (IEnumerable)Rewritten.List.Select( + Rewritten.Interface.Cast(data["data"]["node"], "Issue")["comments"]["nodes"], + comment => new CommentModel + { + Id = comment["id"].ToString(), + Body = comment["body"].ToObject() + }).ToList(); + var expectedString = expected.ToReadableString(); + + var actual = ExpressionCompiler.GetSourceExpression(TestQuerySecondSubquery.ResultBuilder); + var actualString = actual.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public async Task Reads_All_Pages() + { + int page = 0; + + string CreateComment(int index) + { + return @" + { + id: ""comment" + index + @""", + body: ""comment " + index + @""" + }"; + } + + string CreateIssue(int number, int commentCount, bool hasNextPage) + { + return @" + { + id: ""issue" + number + @""", + number: " + number + @", + comments: { + ""pageInfo"": { + ""hasNextPage"": " + hasNextPage.ToString().ToLower() + @", + ""endCursor"": """ + "comment_end" + number + @""" + }, + ""nodes"": [" + + String.Join(",", Enumerable.Range(number * 10, commentCount).Select(x => CreateComment(x))) + @" + ] + } + }"; + } + + string Execute(string _, IDictionary variables) + { + switch (page++) + { + case 0: + Assert.Null(variables); + return @"{ + data: { + ""repository"": { + ""id"": ""repoid"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": true, + ""endCursor"": ""issue_end0"" + }, + ""nodes"": [" + + CreateIssue(1, 1, false) + "," + + CreateIssue(2, 3, true) + "," + + CreateIssue(3, 0, false) + @" + ] + } + } + } +}"; + case 1: + Assert.NotNull(variables); + Assert.Equal("issue2", variables["__id"]); + Assert.Equal("comment_end2", variables["__after"]); + return @"{ + data: { + ""node"": { + ""__typename"": ""Issue"", + ""comments"": { + ""pageInfo"": { + ""hasNextPage"": false, + ""endCursor"": ""comment_end"" + }, + ""nodes"": [" + + CreateComment(23) + "," + + CreateComment(24) + @" + ] + } + } + } +}"; + + case 2: + Assert.NotNull(variables); + Assert.Equal("repoid", variables["__id"]); + Assert.Equal("issue_end0", variables["__after"]); + return @"{ + data: { + ""node"": { + ""__typename"": ""Repository"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": false, + ""endCursor"": ""issue_end1"" + }, + ""nodes"": [" + + CreateIssue(4, 0, false) + "," + + CreateIssue(5, 0, false) + @" + ] + } + } + } +}"; + + default: + throw new NotSupportedException("Should not get here"); + } + } + + var connection = new MockConnection(Execute); + var result = (await connection.Run(TestQuery)).ToList(); + + Assert.Equal(Enumerable.Range(1, 5).ToList(), result.Select(x => x.Number).ToList()); + Assert.Single(result[0].Comments); + Assert.Equal(5, result[1].Comments.Count); + Assert.Empty(result[2].Comments); + Assert.Empty(result[3].Comments); + Assert.Empty(result[4].Comments); + } + + [Fact] + public async Task Handles_Empty_Nodes() + { + int page = 0; + + string Execute(string query, IDictionary variables) + { + switch (page++) + { + case 0: + Assert.Null(variables); + return @"{ + data: { + ""repository"": { + ""id"": ""repoid"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": false, + ""endCursor"": ""issue_end0"" + }, + ""nodes"": [] + } + } + } +}"; + default: + throw new NotSupportedException("Should not get here"); + } + } + + var connection = new MockConnection(Execute); + var result = (await connection.Run(TestQuery)).ToList(); + + Assert.Empty(result); + } + } +} \ No newline at end of file diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryIssuesComplexQueryAllPagesTests.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryIssuesComplexQueryAllPagesTests.cs new file mode 100644 index 00000000..7430866d --- /dev/null +++ b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryIssuesComplexQueryAllPagesTests.cs @@ -0,0 +1,467 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using AgileObjects.ReadableExpressions; +using Newtonsoft.Json.Linq; +using Octokit.GraphQL.Core.Builders; +using Octokit.GraphQL.Core.UnitTests.Models; +using Xunit; + +namespace Octokit.GraphQL.Core.UnitTests +{ + public class Repository_Issues_ComplexQuery_AllPages + { + static Repository_Issues_ComplexQuery_AllPages() + { + ExpressionCompiler.IsUnitTesting = true; + } + + [Fact] + public void Creates_MasterQuery() + { + var expected = @"query { + repository(owner: ""foo"", name: ""bar"") { + id + intList1: issues(first: 100) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + intList2: issues(first: 100) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + } +}"; + + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var masterQuery = query.GetMasterQuery(); + + Assert.Equal(expected, masterQuery.ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_MasterQuery_Expression() + { + Expression> expected = data => + Rewritten.Value.Select( + data["data"]["repository"], + repository => new { + IntList1 = Rewritten.List.ToSubqueryList( + Rewritten.List.ToList(Rewritten.List.Select(repository["intList1"]["nodes"], issue => issue["number"])), + data.Annotation(), + SubqueryPlaceholder.placeholder), + IntList2 = Rewritten.List.ToSubqueryList( + Rewritten.List.ToList(Rewritten.List.Select(repository["intList2"]["nodes"], issue => issue["number"])), + data.Annotation(), + SubqueryPlaceholder.placeholder) + }); + + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var masterQuery = query.GetMasterQuery(); + + ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, masterQuery, + "SimpleSubquery>", + "SimpleSubquery>"); + } + + [Fact] + public void Creates_Subquery_1_Expression() + { + Expression>> expected = + data => (IEnumerable)Rewritten.List.Select( + Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], + issue => issue["number"].ToObject()).ToList(); + var expectedString = expected.ToReadableString(); + + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var subqueries = query.GetSubqueries(); + var queryFirstSubquery = (SimpleSubquery>) subqueries[0]; + + var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ResultBuilder); + var actualString = actual.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_1_Expression_PageInfo() + { + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var subqueries = query.GetSubqueries(); + var queryFirstSubquery = (SimpleSubquery>)subqueries[0]; + + var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.PageInfo); + var actualString = actual.ToReadableString(); + + Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_1_Expression_ParentId() + { + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var subqueries = query.GetSubqueries(); + var queryFirstSubquery = (SimpleSubquery>)subqueries[0]; + + var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ParentIds); + var actualString = actual.ToReadableString(); + + Expression>> expected = data => data.SelectTokens("$.data.repository.id"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_1_Expression_ParentPageInfo() + { + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var subqueries = query.GetSubqueries(); + var queryFirstSubquery = (SimpleSubquery>)subqueries[0]; + + var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ParentPageInfo); + var actualString = actual.ToReadableString(); + + Expression>> expected = data => data.SelectTokens("$.data.repository.intList1.pageInfo"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_2_Expression() + { + Expression>> expected = + data => (IEnumerable)Rewritten.List.Select( + Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], + issue => issue["number"].ToObject()).ToList(); + var expectedString = expected.ToReadableString(); + + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var subqueries = query.GetSubqueries(); + var queryFirstSubquery = (SimpleSubquery>) subqueries[1]; + + var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ResultBuilder); + var actualString = actual.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_2_Expression_PageInfo() + { + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var subqueries = query.GetSubqueries(); + var queryFirstSubquery = (SimpleSubquery>)subqueries[1]; + + var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.PageInfo); + var actualString = actual.ToReadableString(); + + Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_2_Expression_ParentId() + { + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var subqueries = query.GetSubqueries(); + var queryFirstSubquery = (SimpleSubquery>)subqueries[1]; + + var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ParentIds); + var actualString = actual.ToReadableString(); + + Expression>> expected = data => data.SelectTokens("$.data.repository.id"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_2_Expression_ParentPageInfo() + { + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + var subqueries = query.GetSubqueries(); + var queryFirstSubquery = (SimpleSubquery>)subqueries[1]; + + var actual = ExpressionCompiler.GetSourceExpression(queryFirstSubquery.ParentPageInfo); + var actualString = actual.ToReadableString(); + + Expression>> expected = data => data.SelectTokens("$.data.repository.intList2.pageInfo"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public async Task Reads_All_Pages() + { + int page = 0; + + string Execute(string _, IDictionary variables) + { + switch (page++) + { + case 0: + Assert.Null(variables); + return @"{ + ""data"": { + ""repository"": { + ""id"": ""repoId"", + ""intList1"": { + ""pageInfo"": { + ""hasNextPage"": true, + ""endCursor"": ""endCursor1"" + }, + ""nodes"": [{ + ""number"": 1 + }, { + ""number"": 2 + }, { + ""number"": 3 + } + ] + }, + ""intList2"": { + ""pageInfo"": { + ""hasNextPage"": true, + ""endCursor"": ""endCursor2"" + }, + ""nodes"": [{ + ""number"": 4 + }, { + ""number"": 5 + }, { + ""number"": 6 + } + ] + } + } + } +}"; + case 1: + Assert.NotNull(variables); + Assert.Equal("repoId", variables["__id"]); + Assert.Equal("endCursor2", variables["__after"]); + + return @"{ + ""data"": { + ""node"": { + ""__typename"": ""Repository"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": false, + ""endCursor"": ""endCursor2.1"" + }, + ""nodes"": [{ + ""number"": 4 + }, { + ""number"": 5 + } + ] + } + } + } +}"; + case 2: + Assert.NotNull(variables); + Assert.Equal("repoId", variables["__id"]); + Assert.Equal("endCursor1", variables["__after"]); + + return @"{ + ""data"": { + ""node"": { + ""__typename"": ""Repository"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": false, + ""endCursor"": ""endCursor1.1"" + }, + ""nodes"": [{ + ""number"": 4 + }, { + ""number"": 5 + }, { + ""number"": 6 + } + ] + } + } + } +}"; + + default: + throw new NotSupportedException("Should not get here"); + } + } + + var query = new Query() + .Repository("foo", "bar") + .Select(repository => new + { + IntList1 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList(), + IntList2 = repository.Issues(null, null, null, null, null) + .AllPages() + .Select(issue => issue.Number) + .ToList() + }).Compile(); + + + var connection = new MockConnection(Execute); + var result = (await connection.Run(query)); + + Assert.Equal(6, result.IntList1.Count); + Assert.Equal(5, result.IntList2.Count); + } + } +} \ No newline at end of file diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryNameIssuesAllPagesTests.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryNameIssuesAllPagesTests.cs new file mode 100644 index 00000000..db2a07be --- /dev/null +++ b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryNameIssuesAllPagesTests.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using AgileObjects.ReadableExpressions; +using Newtonsoft.Json.Linq; +using Octokit.GraphQL.Core.Builders; +using Octokit.GraphQL.Core.UnitTests.Models; +using Octokit.GraphQL.Core.UnitTests.PagingTests.Models; +using Xunit; + +namespace Octokit.GraphQL.Core.UnitTests +{ + public class Repository_Name_Issues_AllPages + { + static Repository_Name_Issues_AllPages() + { + ExpressionCompiler.IsUnitTesting = true; + } + + private ICompiledQuery TestQuery => new Query() + .Repository("foo", "bar") + .Select(repository => new RepositoryModel + { + Name = repository.Name, + Issues = repository.Issues(null, null, null, null, new[] { "bug" }).AllPages().Select(issue => new IssueModel + { + Number = issue.Number, + }).ToList() + }).Compile(); + + private SimpleQuery TestMasterQuery => TestQuery.GetMasterQuery(); + + private IReadOnlyList TestQuerySubqueries => TestQuery.GetSubqueries(); + + private SimpleSubquery> TestQueryFirstSubquery => (SimpleSubquery>)TestQuerySubqueries.First(); + + [Fact] + public void Creates_MasterQuery() + { + var expected = @"query { + repository(owner: ""foo"", name: ""bar"") { + id + name + issues(labels: [""bug""], first: 100) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + } +}"; + + Assert.Equal(expected, TestMasterQuery.ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_MasterQuery_Expression() + { + Expression> expected = data => + Rewritten.Value.Select( + data["data"]["repository"], + repository => new RepositoryModel + { + Name = repository["name"].ToObject(), + Issues = Rewritten.List.ToSubqueryList( + Rewritten.List.Select( + repository["issues"]["nodes"], + issue => new IssueModel + { + Number = issue["number"].ToObject(), + }), + data.Annotation(), + SubqueryPlaceholder.placeholder), + }); + + ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, TestMasterQuery, + "SimpleSubquery>"); + } + + [Fact] + public void Creates_Subquery() + { + var expected = @"query($__id: ID!, $__after: String) { + node(id: $__id) { + __typename + ... on Repository { + issues(first: 100, after: $__after, labels: [""bug""]) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + } + } +}"; + + Assert.Single(TestQuerySubqueries); + Assert.Equal(expected, TestQueryFirstSubquery.ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_Subquery_Expression() + { + Expression>> expected = + data => (IEnumerable)Rewritten.List.Select( + Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], + issue => new IssueModel + { + Number = issue["number"].ToObject() + }).ToList(); + var expectedString = expected.ToReadableString(); + + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ResultBuilder); + var actualString = actual.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_Expression_PageInfo() + { + Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); + var expectedString = expected.ToReadableString(); + + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.PageInfo); + var actualString = actual.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_Expression_ParentId() + { + Expression>> expected = data => data.SelectTokens("$.data.repository.id"); + var expectedString = expected.ToReadableString(); + + var actualExpression = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ParentIds); + var actualString = actualExpression.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_Expression_ParentPage() + { + Expression>> expected = data => data.SelectTokens("$.data.repository.issues.pageInfo"); + var expectedString = expected.ToReadableString(); + + var actualExpression = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ParentPageInfo); + var actualString = actualExpression.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public async Task Reads_All_Pages() + { + int page = 0; + + string Execute(string _, IDictionary variables) + { + switch (page++) + { + case 0: + Assert.Null(variables); + return @"{ + data: { + ""repository"": { + ""id"": ""repoid"", + ""name"": ""foo"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": true, + ""endCursor"": ""end0"" + }, + ""nodes"": [ + { ""number"": 0 }, + { ""number"": 1 }, + { ""number"": 2 }, + ] + } + } + } +}"; + case 1: + Assert.NotNull(variables); + Assert.Equal(variables["__id"], "repoid"); + Assert.Equal(variables["__after"], "end0"); + return @"{ + data: { + ""node"": { + ""__typename"": ""Repository"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": false, + ""endCursor"": ""end1"" + }, + ""nodes"": [ + { ""number"": 3 }, + { ""number"": 4 }, + ] + } + } + } +}"; + default: + throw new NotSupportedException("Should not get here"); + } + } + + var connection = new MockConnection(Execute); + var result = await connection.Run(TestQuery); + + Assert.Equal( + Enumerable.Range(0, 5).ToList(), + result.Issues.Select(x => x.Number).ToList()); + } + } +} \ No newline at end of file diff --git a/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryNameIssuesAllPagesToDictionaryTests.cs b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryNameIssuesAllPagesToDictionaryTests.cs new file mode 100644 index 00000000..e745e77a --- /dev/null +++ b/Octokit.GraphQL.Core.UnitTests/PagingTests/RepositoryNameIssuesAllPagesToDictionaryTests.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using AgileObjects.ReadableExpressions; +using Newtonsoft.Json.Linq; +using Octokit.GraphQL.Core.Builders; +using Octokit.GraphQL.Core.UnitTests.Models; +using Octokit.GraphQL.Core.UnitTests.PagingTests.Models; +using Xunit; + +namespace Octokit.GraphQL.Core.UnitTests +{ + public class Repository_Name_Issues_AllPages_To_Dictionary + { + static Repository_Name_Issues_AllPages_To_Dictionary() + { + ExpressionCompiler.IsUnitTesting = true; + } + + private ICompiledQuery TestQuery => new Query() + .Repository("foo", "bar") + .Select(repository => new RepositoryModelWithDictionary + { + Name = repository.Name, + Issues = repository.Issues(null, null, null, null, new[] { "bug" }).AllPages().Select(issue => new IssueModel + { + Number = issue.Number, + }).ToDictionary(x => x.Number, x => x) + }).Compile(); + + private SimpleQuery TestMasterQuery => TestQuery.GetMasterQuery(); + + private IReadOnlyList TestQuerySubqueries => TestQuery.GetSubqueries(); + + private SimpleSubquery> TestQueryFirstSubquery => (SimpleSubquery>)TestQuerySubqueries.First(); + + [Fact] + public void Creates_MasterQuery() + { + var expected = @"query { + repository(owner: ""foo"", name: ""bar"") { + id + name + issues(labels: [""bug""], first: 100) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + } +}"; + + Assert.Equal(expected, TestMasterQuery.ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_MasterQuery_Expression() + { + Expression> expected = data => + Rewritten.Value.Select( + data["data"]["repository"], + repository => new RepositoryModelWithDictionary + { + Name = repository["name"].ToObject(), + Issues = (IDictionary)Rewritten.List.ToSubqueryDictionary( + Rewritten.List.Select( + repository["issues"]["nodes"], + issue => new IssueModel + { + Number = issue["number"].ToObject() + }), + data.Annotation(), + SubqueryPlaceholder.placeholder, + x => x.Number, + x => x) + }); + + ExpressionRewriterAssertions.AssertCompiledQueryExpressionEqual(expected, TestMasterQuery, + "SimpleSubquery>"); + } + + [Fact] + public void Creates_Subquery() + { + var expected = @"query($__id: ID!, $__after: String) { + node(id: $__id) { + __typename + ... on Repository { + issues(first: 100, after: $__after, labels: [""bug""]) { + pageInfo { + hasNextPage + endCursor + } + nodes { + number + } + } + } + } +}"; + + Assert.Single(TestQuerySubqueries); + Assert.Equal(expected, TestQueryFirstSubquery.ToString(), ignoreLineEndingDifferences: true); + } + + [Fact] + public void Creates_Subquery_Expression() + { + Expression>> expected = + data => (IEnumerable)Rewritten.List.Select( + Rewritten.Interface.Cast(data["data"]["node"], "Repository")["issues"]["nodes"], + issue => new IssueModel + { + Number = issue["number"].ToObject() + }).ToList(); + var expectedString = expected.ToReadableString(); + + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ResultBuilder); + var actualString = actual.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_Expression_PageInfo() + { + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.PageInfo); + var actualString = actual.ToReadableString(); + + Expression> expected = data => data.SelectToken("data.node.issues.pageInfo"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_Expression_ParentId() + { + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ParentIds); + var actualString = actual.ToReadableString(); + + Expression>> expected = data => data.SelectTokens("$.data.repository.id"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public void Creates_Subquery_Expression_ParentPageInfo() + { + var actual = ExpressionCompiler.GetSourceExpression(TestQueryFirstSubquery.ParentPageInfo); + var actualString = actual.ToReadableString(); + + Expression>> expected = data => data.SelectTokens("$.data.repository.issues.pageInfo"); + var expectedString = expected.ToReadableString(); + + Assert.Equal(ExpressionRewriterAssertions.StripWhitespace(expectedString), ExpressionRewriterAssertions.StripWhitespace(actualString)); + } + + [Fact] + public async Task Reads_All_Pages() + { + int page = 0; + + string Execute(string _, IDictionary variables) + { + switch (page++) + { + case 0: + Assert.Null(variables); + return @"{ + data: { + ""repository"": { + ""id"": ""repoid"", + ""name"": ""foo"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": true, + ""endCursor"": ""end0"" + }, + ""nodes"": [ + { ""number"": 0 }, + { ""number"": 1 }, + { ""number"": 2 }, + ] + } + } + } +}"; + case 1: + Assert.NotNull(variables); + Assert.Equal(variables["__id"], "repoid"); + Assert.Equal(variables["__after"], "end0"); + return @"{ + data: { + ""node"": { + ""__typename"": ""Repository"", + ""issues"": { + ""pageInfo"": { + ""hasNextPage"": false, + ""endCursor"": ""end1"" + }, + ""nodes"": [ + { ""number"": 3 }, + { ""number"": 4 }, + ] + } + } + } +}"; + default: + throw new NotSupportedException("Should not get here"); + } + } + + var connection = new MockConnection(Execute); + var result = await connection.Run(TestQuery); + + Assert.Equal( + Enumerable.Range(0, 5).ToList(), + result.Issues.Select(x => x.Key).ToList()); + } + } +} \ No newline at end of file diff --git a/Octokit.GraphQL.IntegrationTests/Octokit.GraphQL.IntegrationTests.v3.ncrunchproject b/Octokit.GraphQL.IntegrationTests/Octokit.GraphQL.IntegrationTests.v3.ncrunchproject index 704afcb2..16c321bc 100644 --- a/Octokit.GraphQL.IntegrationTests/Octokit.GraphQL.IntegrationTests.v3.ncrunchproject +++ b/Octokit.GraphQL.IntegrationTests/Octokit.GraphQL.IntegrationTests.v3.ncrunchproject @@ -1,6 +1,9 @@  15000 + + + True \ No newline at end of file