< Summary

Line coverage
100%
Covered lines: 123
Uncovered lines: 0
Coverable lines: 123
Total lines: 198
Line coverage: 100%
Branch coverage
80%
Covered branches: 8
Total branches: 10
Branch coverage: 80%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: LoginMutation()66.67%66100%
File 2: Initialize(...)100%22100%
File 2: .ctor(...)100%11100%
File 2: CreateParameterDescriptor_LoginMutation_input()100%11100%
File 2: LoginMutation()100%11100%
File 2: LoginMutation()100%11100%

File(s)

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Auth/Mutations/LoginMutation.cs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik <alper@alper-celik.dev>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5using System.Text;
 6using Api.Auth.Models;
 7using Api.Auth.Utils;
 8using Api.Database;
 9using Api.Database.Utils;
 10using FluentValidation;
 11using Geralt;
 12using HotChocolate.Authorization;
 13using Microsoft.EntityFrameworkCore;
 14
 15namespace Api.Auth.Mutations;
 16
 17public record LoginPayload(Queries.User User, string Token);
 18
 19public record LoginInput(string Email, string Password, string ClientName = "unknown");
 20
 21[MutationType]
 22public static partial class LoginMutations
 23{
 24    [AllowAnonymous]
 25    public static async Task<LoginPayload> LoginMutation(
 26        [Service] PGContext db,
 27        [Service] IEFTransactionDIAccessorService txAccessor,
 28        LoginInput input
 29    )
 130    {
 131        var tx = await txAccessor.BeginOrGetTransactionAsync();
 132        UserEF? user = await db
 133            .Users.Where(u => u.Email == input.Email.NormalizeEmail())
 134            .FirstOrDefaultAsync();
 35
 136        var junkHash = new char[Argon2id.HashSize];
 137        Argon2id.ComputeHash(
 138            junkHash,
 139            Encoding.UTF8.GetBytes("123"),
 140            AuthUtils.ARGON2ID_ITER,
 141            AuthUtils.ARGON2ID_MEM_BYTES
 142        );
 43
 144        var passworkdVerified = Argon2id.VerifyHash(
 145            user?.PasswordHash ?? new string(junkHash),
 146            Encoding.UTF8.GetBytes(input.Password.Normalize())
 147        );
 48
 149        if (user is not null && passworkdVerified)
 150        {
 151            var token = await AuthUtils.CreateUserSession(user.Id, input.ClientName, db);
 152            await db.SaveChangesAsync();
 153            await tx.CommitAsync();
 154            return new(Queries.UserMapper.ToDto(user), token);
 55        }
 56
 157        throw new GraphQLException(
 158            ErrorBuilder
 159                .New()
 160                .SetMessage("Invalid Credentials")
 161                .SetCode(ErrorCodes.INVALID_CREDS)
 162                .Build()
 163        );
 164    }
 65}

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/obj/Debug/net10.0/generated/HotChocolate.Types.Analyzers/HotChocolate.Types.Analyzers.GraphQLServerGenerator/LoginMutations.hs33rHgC5AHky1fX2QOBKQ.hc.g.cs

#LineLine coverage
 1// <auto-generated/>
 2
 3#nullable enable
 4#pragma warning disable
 5
 6using System;
 7using System.Runtime.CompilerServices;
 8using HotChocolate;
 9using HotChocolate.Types;
 10using HotChocolate.Execution.Configuration;
 11using Microsoft.Extensions.DependencyInjection;
 12using HotChocolate.Internal;
 13
 14namespace Api.Auth.Mutations
 15{
 16    public static partial class LoginMutations
 17    {
 18        internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor)
 119        {
 120            var extension = descriptor.Extend();
 121            var configuration = extension.Configuration;
 122            var thisType = typeof(global::Api.Auth.Mutations.LoginMutations);
 123            var bindingResolver = extension.Context.ParameterBindingResolver;
 124            var resolvers = new __Resolvers(bindingResolver);
 25
 126            HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
 127                extension.Context,
 128                descriptor,
 129                typeof(global::Api.Auth.Mutations.LoginMutations),
 130                new global::HotChocolate.Types.MutationTypeAttribute());
 131            configuration.ConfigurationsAreApplied = true;
 32
 133            var naming = descriptor.Extend().Context.Naming;
 34
 135            descriptor
 136                .Field(naming.GetMemberName("LoginMutation", global::HotChocolate.Types.MemberKind.ObjectField))
 137                .ExtendWith(static (field, context) =>
 138                {
 139                    var configuration = field.Configuration;
 140                    var typeInspector = field.Context.TypeInspector;
 141                    var bindingResolver = field.Context.ParameterBindingResolver;
 142                    var naming = field.Context.Naming;
 143
 144                    configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create(
 145                        typeInspector.GetTypeRef(typeof(global::Api.Auth.Mutations.LoginPayload), HotChocolate.Types.Typ
 146                        new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNod
 147                    configuration.ResultType = typeof(global::Api.Auth.Mutations.LoginPayload);
 148                    configuration.DeclaringType = context.ThisType;
 149
 150                    configuration.SetSourceGeneratorFlags();
 151
 152                    var bindingInfo = field.Context.ParameterBindingResolver;
 153                    var parameter = context.Resolvers.CreateParameterDescriptor_LoginMutation_input();
 154                    var parameterInfo = bindingInfo.GetBindingInfo(parameter);
 155
 156                    if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument)
 157                    {
 158                        var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentCo
 159                        {
 160                            Name = naming.GetMemberName("input", global::HotChocolate.Types.MemberKind.Argument),
 161                            Type = global::HotChocolate.Types.Descriptors.TypeReference.Create(
 162                                typeInspector.GetTypeRef(typeof(global::Api.Auth.Mutations.LoginInput), HotChocolate.Typ
 163                                new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.Name
 164                            RuntimeType = typeof(global::Api.Auth.Mutations.LoginInput)
 165                        };
 166
 167                        configuration.Arguments.Add(argumentConfiguration);
 168                    }
 169
 170                    configuration.Member = context.ThisType.GetMethod(
 171                        "LoginMutation",
 172                        global::HotChocolate.Utilities.ReflectionUtils.StaticMemberFlags,
 173                        new global::System.Type[]
 174                        {
 175                            typeof(global::Api.Database.PGContext),
 176                            typeof(global::Api.Database.Utils.IEFTransactionDIAccessorService),
 177                            typeof(global::Api.Auth.Mutations.LoginInput)
 178                        })!;
 179
 180                    var fieldDescriptor = global::HotChocolate.Types.Descriptors.ObjectFieldDescriptor.From(field.Contex
 181                    HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
 182                        field.Context,
 183                        fieldDescriptor,
 184                        configuration.Member,
 185                        new global::HotChocolate.Authorization.AllowAnonymousAttribute());
 186
 187                    bindingResolver.ApplyConfiguration(
 188                        context.Resolvers.CreateParameterDescriptor_LoginMutation_input(),
 189                        fieldDescriptor);
 190
 191                    configuration.ConfigurationsAreApplied = true;
 192                    fieldDescriptor.CreateConfiguration();
 193
 194                    configuration.Resolvers = context.Resolvers.LoginMutation();
 195                },
 196                (Resolvers: resolvers, ThisType: thisType));
 97
 98            Configure(descriptor);
 199        }
 100
 101        static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor);
 102
 103        private sealed class __Resolvers
 104        {
 105            private readonly global::HotChocolate.Internal.IParameterBinding _binding_LoginMutation_input;
 106
 1107            public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver)
 1108            {
 1109                _binding_LoginMutation_input = bindingResolver.GetBinding(CreateParameterDescriptor_LoginMutation_input(
 1110            }
 111
 112            public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_LoginMutation_input()
 1113                => new HotChocolate.Internal.ParameterDescriptor(
 1114                    "input",
 1115                    typeof(global::Api.Auth.Mutations.LoginInput),
 1116                    isNullable: false,
 1117                    []);
 118
 119            public HotChocolate.Resolvers.FieldResolverDelegates LoginMutation()
 1120                => new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: LoginMutation);
 121
 122            private async global::System.Threading.Tasks.ValueTask<global::System.Object?> LoginMutation(global::HotChoc
 1123            {
 1124                var args0 = context.Service<global::Api.Database.PGContext>();
 1125                var args1 = context.Service<global::Api.Database.Utils.IEFTransactionDIAccessorService>();
 1126                var args2 = _binding_LoginMutation_input.Execute<global::Api.Auth.Mutations.LoginInput>(context);
 1127                var result = await global::Api.Auth.Mutations.LoginMutations.LoginMutation(args0, args1, args2);
 1128                return result;
 1129            }
 130        }
 131    }
 132}
 133