< 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
89%
Covered lines: 26
Uncovered lines: 3
Coverable lines: 29
Total lines: 96
Line coverage: 89.6%
Branch coverage
86%
Covered branches: 19
Total branches: 22
Branch coverage: 86.3%
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(...)83.33%6683.33%
IsValidFormat(...)80%111080%

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    {
 1621        var part1 = GenerateWordLikePart();
 1622        var part2 = GenerateWordLikePart();
 1623        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
 3230        var chars = new char[4];
 31
 32        // Random pattern: start with consonant or vowel
 3233        bool startWithConsonant = RandomNumberGenerator.GetInt32(2) == 0;
 34
 32035        for (int i = 0; i < 4; i++)
 36        {
 12837            bool useConsonant = (i % 2 == 0) ? startWithConsonant : !startWithConsonant;
 38
 12839            if (useConsonant)
 40            {
 6441                chars[i] = Consonants[RandomNumberGenerator.GetInt32(Consonants.Length)];
 42            }
 43            else
 44            {
 6445                chars[i] = Vowels[RandomNumberGenerator.GetInt32(Vowels.Length)];
 46            }
 47        }
 48
 3249        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    {
 2257        if (string.IsNullOrWhiteSpace(code))
 058            return string.Empty;
 59
 60        // Remove spaces, uppercase
 2261        var cleaned = code.Trim().ToUpperInvariant().Replace(" ", "");
 62
 63        // If no hyphen and 8 chars, insert hyphen
 2264        if (!cleaned.Contains('-') && cleaned.Length == 8)
 65        {
 166            cleaned = $"{cleaned[..4]}-{cleaned[4..]}";
 67        }
 68
 2269        return cleaned;
 70    }
 71
 72    /// <summary>
 73    /// Validate code format (XXXX-XXXX)
 74    /// </summary>
 75    public static bool IsValidFormat(string code)
 76    {
 1177        var normalized = NormalizeCode(code);
 78
 1179        if (normalized.Length != 9) // XXXX-XXXX
 180            return false;
 81
 1082        if (normalized[4] != '-')
 083            return false;
 84
 85        // Check all other chars are letters
 20086        for (int i = 0; i < 9; i++)
 87        {
 9088            if (i == 4)
 89                continue;
 8090            if (!char.IsLetter(normalized[i]))
 091                return false;
 92        }
 93
 1094        return true;
 95    }
 96}