< Summary

Information
Class: Chronicis.Api.Utilities.InvitationCodeGenerator
Assembly: Chronicis.Api
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Api/Utilities/InvitationCodeGenerator.cs
Line coverage
100%
Covered lines: 29
Uncovered lines: 0
Coverable lines: 29
Total lines: 96
Line coverage: 100%
Branch coverage
100%
Covered branches: 22
Total branches: 22
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
GenerateCode()100%11100%
GenerateWordLikePart()100%66100%
NormalizeCode(...)100%66100%
IsValidFormat(...)100%1010100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Api/Utilities/InvitationCodeGenerator.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2
 3namespace Chronicis.Api.Utilities;
 4
 5/// <summary>
 6/// Generates memorable invitation codes in XXXX-XXXX format.
 7/// Uses word-like patterns that are easy to read aloud in Discord.
 8/// </summary>
 9public static class InvitationCodeGenerator
 10{
 11    // Consonants and vowels for pronounceable codes
 112    private static readonly char[] Consonants = "BCDFGHJKLMNPRSTVWXZ".ToCharArray();
 113    private static readonly char[] Vowels = "AEIOU".ToCharArray();
 14
 15    /// <summary>
 16    /// Generate a memorable code like "FROG-AXLE" or "MINT-RUBY"
 17    /// Pattern: CVCC-VCCV or similar pronounceable combinations
 18    /// </summary>
 19    public static string GenerateCode()
 20    {
 23821        var part1 = GenerateWordLikePart();
 23822        var part2 = GenerateWordLikePart();
 23823        return $"{part1}-{part2}";
 24    }
 25
 26    private static string GenerateWordLikePart()
 27    {
 28        // Generate a 4-character pronounceable part
 29        // Pattern alternates to create word-like strings
 47630        var chars = new char[4];
 31
 32        // Random pattern: start with consonant or vowel
 47633        bool startWithConsonant = RandomNumberGenerator.GetInt32(2) == 0;
 34
 476035        for (int i = 0; i < 4; i++)
 36        {
 190437            bool useConsonant = (i % 2 == 0) ? startWithConsonant : !startWithConsonant;
 38
 190439            if (useConsonant)
 40            {
 95241                chars[i] = Consonants[RandomNumberGenerator.GetInt32(Consonants.Length)];
 42            }
 43            else
 44            {
 95245                chars[i] = Vowels[RandomNumberGenerator.GetInt32(Vowels.Length)];
 46            }
 47        }
 48
 47649        return new string(chars);
 50    }
 51
 52    /// <summary>
 53    /// Normalize a code for comparison (uppercase, with hyphen)
 54    /// </summary>
 55    public static string NormalizeCode(string code)
 56    {
 14457        if (string.IsNullOrWhiteSpace(code))
 558            return string.Empty;
 59
 60        // Remove spaces, uppercase
 13961        var cleaned = code.Trim().ToUpperInvariant().Replace(" ", "");
 62
 63        // If no hyphen and 8 chars, insert hyphen
 13964        if (!cleaned.Contains('-') && cleaned.Length == 8)
 65        {
 366            cleaned = $"{cleaned[..4]}-{cleaned[4..]}";
 67        }
 68
 13969        return cleaned;
 70    }
 71
 72    /// <summary>
 73    /// Validate code format (XXXX-XXXX)
 74    /// </summary>
 75    public static bool IsValidFormat(string code)
 76    {
 12577        var normalized = NormalizeCode(code);
 78
 12579        if (normalized.Length != 9) // XXXX-XXXX
 580            return false;
 81
 12082        if (normalized[4] != '-')
 183            return false;
 84
 85        // Check all other chars are letters
 234486        for (int i = 0; i < 9; i++)
 87        {
 105788            if (i == 4)
 89                continue;
 94090            if (!char.IsLetter(normalized[i]))
 491                return false;
 92        }
 93
 11594        return true;
 95    }
 96}