From 2c370d0ebd11458fb72628f9d7da16a5e43cf13a Mon Sep 17 00:00:00 2001 From: Struan Judd Date: Wed, 3 Jan 2024 16:21:13 +1300 Subject: [PATCH] Improve code coverage --- .../Parse/Schema/ParseNull.cs | 4 +- .../Parse/Schema/ParseScalar.cs | 6 +- .../Parse/Schema/ParseScalarRegex.cs | 7 +- .../Ast/FieldKeyAstTests.cs | 2 +- .../Merging/MergeConstantsTests.cs | 1 - .../Parse/ClassTestBase.cs | 92 +++++++++++++++++++ .../Parse/ParseScalarClassTests.cs | 29 ++++++ .../Parse/ParseScalarDefinitionClassTests.cs | 28 ++++++ .../Result/ResultTests.cs | 27 +++++- 9 files changed, 185 insertions(+), 11 deletions(-) create mode 100644 test/GqlPlus.Verifier.ClassTests/Parse/ClassTestBase.cs create mode 100644 test/GqlPlus.Verifier.ClassTests/Parse/ParseScalarClassTests.cs create mode 100644 test/GqlPlus.Verifier.ClassTests/Parse/ParseScalarDefinitionClassTests.cs diff --git a/src/GqlPlus.Verifier/Parse/Schema/ParseNull.cs b/src/GqlPlus.Verifier/Parse/Schema/ParseNull.cs index a868d569..1dcaccb7 100644 --- a/src/GqlPlus.Verifier/Parse/Schema/ParseNull.cs +++ b/src/GqlPlus.Verifier/Parse/Schema/ParseNull.cs @@ -1,4 +1,5 @@ -using GqlPlus.Verifier.Ast; +using System.Diagnostics.CodeAnalysis; +using GqlPlus.Verifier.Ast; using GqlPlus.Verifier.Result; using GqlPlus.Verifier.Token; @@ -6,6 +7,7 @@ namespace GqlPlus.Verifier.Parse.Schema; internal class ParseNull : Parser.I { + [ExcludeFromCodeCoverage] public IResult Parse(TContext tokens, string label) where TContext : Tokenizer => 0.Empty(); diff --git a/src/GqlPlus.Verifier/Parse/Schema/ParseScalar.cs b/src/GqlPlus.Verifier/Parse/Schema/ParseScalar.cs index 62116656..4cbf5faa 100644 --- a/src/GqlPlus.Verifier/Parse/Schema/ParseScalar.cs +++ b/src/GqlPlus.Verifier/Parse/Schema/ParseScalar.cs @@ -87,16 +87,16 @@ public IResult Parse(TContext tokens, string label) return tokens.End(label, () => result); } - return scalarRegexes.AsResult(result); // not covered + return scalarRegexes.AsResult(result); case ScalarKind.Union: var scalarReferences = _references.Parse(tokens, label); if (scalarReferences.Required(references => result.References = references)) { return tokens.End(label, () => result); } - return scalarReferences.AsResult(result); // not covered + return scalarReferences.AsResult(result); default: - return tokens.Partial(label, "valid kind", () => result); // not covered + return tokens.Partial(label, "valid kind", () => result); } } } diff --git a/src/GqlPlus.Verifier/Parse/Schema/ParseScalarRegex.cs b/src/GqlPlus.Verifier/Parse/Schema/ParseScalarRegex.cs index 79c9c36e..9339e7f8 100644 --- a/src/GqlPlus.Verifier/Parse/Schema/ParseScalarRegex.cs +++ b/src/GqlPlus.Verifier/Parse/Schema/ParseScalarRegex.cs @@ -9,14 +9,17 @@ internal class ParseScalarRegex : Parser.I public IResult Parse(TContext tokens, string label) where TContext : Tokenizer { - ScalarRegexAst? result = null; var at = tokens.At; + ScalarRegexAst? result; if (tokens.Regex(out var regex)) { var excluded = tokens.Take('!'); result = new(at, regex, excluded); return result.Ok(); } - return result.Empty(); + result = new(at, regex, false); + return string.IsNullOrEmpty(regex) + ? result.Empty() + : tokens.Error(label, "Closing '/'", result); } } diff --git a/test/GqlPlus.Verifier.ClassTests/Ast/FieldKeyAstTests.cs b/test/GqlPlus.Verifier.ClassTests/Ast/FieldKeyAstTests.cs index 92596b9a..0f79ad1e 100644 --- a/test/GqlPlus.Verifier.ClassTests/Ast/FieldKeyAstTests.cs +++ b/test/GqlPlus.Verifier.ClassTests/Ast/FieldKeyAstTests.cs @@ -4,7 +4,7 @@ public class FieldKeyAstTests { [Fact] public void HashCode_WithNull() - => _checks.HashCode(() => new FieldKeyAst(AstNulls.At)); + => _checks.HashCode(() => new FieldKeyAst(AstNulls.At) with { At = AstNulls.At }); [Theory, RepeatData(Repeats)] public void HashCode_WithNumber(decimal number) diff --git a/test/GqlPlus.Verifier.ClassTests/Merging/MergeConstantsTests.cs b/test/GqlPlus.Verifier.ClassTests/Merging/MergeConstantsTests.cs index 87461526..df495a57 100644 --- a/test/GqlPlus.Verifier.ClassTests/Merging/MergeConstantsTests.cs +++ b/test/GqlPlus.Verifier.ClassTests/Merging/MergeConstantsTests.cs @@ -1,5 +1,4 @@ using GqlPlus.Verifier.Ast; -using Newtonsoft.Json.Linq; namespace GqlPlus.Verifier.Merging; diff --git a/test/GqlPlus.Verifier.ClassTests/Parse/ClassTestBase.cs b/test/GqlPlus.Verifier.ClassTests/Parse/ClassTestBase.cs new file mode 100644 index 00000000..a2f1f920 --- /dev/null +++ b/test/GqlPlus.Verifier.ClassTests/Parse/ClassTestBase.cs @@ -0,0 +1,92 @@ +using GqlPlus.Verifier.Parse.Schema; +using GqlPlus.Verifier.Result; +using GqlPlus.Verifier.Token; +using NSubstitute; + +namespace GqlPlus.Verifier.Parse; + +public class ClassTestBase +{ + protected static Tokenizer Tokens(string input) + { + var tokens = new Tokenizer(input); + tokens.Read(); + return tokens; + } + + internal static T NameFor(string name) + where T : class, INameParser + { + var nameParser = Substitute.For(); + nameParser.ParseName(default!, out var _, out var _) + .ReturnsForAnyArgs(c => { + c[1] = name; + return true; + }); + return nameParser; + } + + protected static Parser.D ParserFor() + => ParserFor(out var _); + + protected static Parser.D ParserFor(out Parser.I parser) + { + parser = Substitute.For.I>(); + parser.Parse(default!, default!) + .ReturnsForAnyArgs(0.Empty()); + + var result = Substitute.For.D>(); + result().Returns(parser); + + return result; + } + + protected static Parser, T>.D OptionParserFor() + where T : struct + => OptionParserFor(out var _); + + protected static Parser, T>.D OptionParserFor(out Parser.I parser) + where T : struct + { + parser = Substitute.For>(); + parser.Parse(default!, default!) + .ReturnsForAnyArgs(0.Empty()); + + var result = Substitute.For, T>.D>(); + result().Returns(parser); + + return result; + } + + protected static Parser, T>.D EnumParserFor() + where T : struct + => EnumParserFor(out var _); + + protected static Parser, T>.D EnumParserFor(out Parser.I parser) + where T : struct + { + parser = Substitute.For>(); + parser.Parse(default!, default!) + .ReturnsForAnyArgs(0.Empty()); + + var result = Substitute.For, T>.D>(); + result().Returns(parser); + + return result; + } + + protected static Parser.DA ArrayParserFor() + => ArrayParserFor(out var _); + + protected static Parser.DA ArrayParserFor(out Parser.IA parser) + { + parser = Substitute.For.IA>(); + parser.Parse(default!, default!) + .ReturnsForAnyArgs(0.EmptyArray()); + + var result = Substitute.For.DA>(); + result().Returns(parser); + + return result; + } +} diff --git a/test/GqlPlus.Verifier.ClassTests/Parse/ParseScalarClassTests.cs b/test/GqlPlus.Verifier.ClassTests/Parse/ParseScalarClassTests.cs new file mode 100644 index 00000000..8f1ac968 --- /dev/null +++ b/test/GqlPlus.Verifier.ClassTests/Parse/ParseScalarClassTests.cs @@ -0,0 +1,29 @@ +using GqlPlus.Verifier.Ast; +using GqlPlus.Verifier.Ast.Schema; +using GqlPlus.Verifier.Result; +using NSubstitute; + +namespace GqlPlus.Verifier.Parse.Schema; + +public class ParseScalarClassTests : ClassTestBase +{ + [Theory, RepeatData(Repeats)] + public void Parse_UnknownKind_ReturnsExpected(string name) + { + var tokens = Tokens("{ "); + + var simpleName = NameFor(name); + var param = ArrayParserFor(); + var aliases = ArrayParserFor(); + var option = OptionParserFor(); + var definition = ParserFor(out var definitionParser); + definitionParser.Parse(tokens, default!) + .ReturnsForAnyArgs(new ScalarDefinition() { Kind = (ScalarKind)99 }.Ok()); + + var scalar = new ParseScalar(simpleName, param, aliases, option, definition); + + var result = scalar.Parse(tokens, "test"); + + result.Should().BeAssignableTo>(); + } +} diff --git a/test/GqlPlus.Verifier.ClassTests/Parse/ParseScalarDefinitionClassTests.cs b/test/GqlPlus.Verifier.ClassTests/Parse/ParseScalarDefinitionClassTests.cs new file mode 100644 index 00000000..50ec9ae3 --- /dev/null +++ b/test/GqlPlus.Verifier.ClassTests/Parse/ParseScalarDefinitionClassTests.cs @@ -0,0 +1,28 @@ +using GqlPlus.Verifier.Ast.Schema; +using GqlPlus.Verifier.Result; +using NSubstitute; + +namespace GqlPlus.Verifier.Parse.Schema; + +public class ParseScalarDefinitionClassTests : ClassTestBase +{ + [Fact] + public void Parse_UnknownKind_ReturnsExpected() + { + var tokens = Tokens("{ "); + + var kind = EnumParserFor(out var kindParser); + kindParser.Parse(tokens, default!) + .ReturnsForAnyArgs(((ScalarKind)99).Ok()); + + var ranges = ArrayParserFor(); + var references = ArrayParserFor(); + var regexes = ArrayParserFor(); + + var scalar = new ParseScalarDefinition(kind, ranges, references, regexes); + + var result = scalar.Parse(tokens, "test"); + + result.Should().BeAssignableTo>(); + } +} diff --git a/test/GqlPlus.Verifier.ClassTests/Result/ResultTests.cs b/test/GqlPlus.Verifier.ClassTests/Result/ResultTests.cs index d1aec9db..11087119 100644 --- a/test/GqlPlus.Verifier.ClassTests/Result/ResultTests.cs +++ b/test/GqlPlus.Verifier.ClassTests/Result/ResultTests.cs @@ -15,11 +15,32 @@ public void Optional_ThrowsInvalidOperation() .Which.Message.Should().Contain(nameof(String)); } + [Fact] + public void OptionalArray_ThrowsInvalidOperation() + { + var input = new TestResultArray(); + + Action action = () => input.Optional(); + + action.Should().Throw() + .Which.Message.Should().Contain(nameof(String)); + } + [ExcludeFromCodeCoverage] public class TestResult : IResult { - public IResult AsPartial(R result, Action? withValue = null, Action? action = null) => throw new InvalidOperationException(); - public IResult AsResult(R? _ = default) => throw new InvalidOperationException(); - public IResult Map(SelectResult onValue, OnResult? otherwise = null) => throw new InvalidOperationException(); + public IResult AsPartial(R result, Action? withValue = null, Action? action = null) => throw new NotImplementedException(); + public IResult AsResult(R? _ = default) => throw new NotImplementedException(); + public IResult Map(SelectResult onValue, OnResult? otherwise = null) => throw new NotImplementedException(); + } + + [ExcludeFromCodeCoverage] + public class TestResultArray : IResultArray + { + public IResult AsPartial(R result, Action? withValue = null, Action? action = null) => throw new NotImplementedException(); + public IResultArray AsPartialArray(IEnumerable result, Action? withValue = null) => throw new NotImplementedException(); + public IResult AsResult(R? _ = default) => throw new NotImplementedException(); + public IResultArray AsResultArray(R[]? _ = null) => throw new NotImplementedException(); + public IResult Map(SelectResult onValue, OnResult? otherwise = null) => throw new NotImplementedException(); } }