< Summary

Information
Class: Services.EmailRoutingServices.RoutingRuleEvaluator
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/EmailRoutingServices/RoutingRuleEvaluator.cs
Line coverage
100%
Covered lines: 50
Uncovered lines: 0
Coverable lines: 50
Total lines: 124
Line coverage: 100%
Branch coverage
97%
Covered branches: 47
Total branches: 48
Branch coverage: 97.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Evaluate(...)100%44100%
ValidateRule(...)100%3030100%
MatchesPattern(...)75%44100%
SplitCsv(...)100%11100%
IsSenderAllowed(...)100%1010100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/EmailRoutingServices/RoutingRuleEvaluator.cs

#LineLine coverage
 1using System.Text.RegularExpressions;
 2using OzonDomains.Models;
 3
 4namespace Services.EmailRoutingServices;
 5
 6public class RoutingRuleEvaluator : IRoutingRuleEvaluator
 7{
 8    public RuleEvaluationResult Evaluate(InboxMessageDto message, InboxAttachmentDto attachment, List<EmailRoutingRule> 
 9    {
 28910        foreach (var rule in rules.OrderBy(x => x.Priority))
 11        {
 6912            var reason = ValidateRule(rule, message, attachment);
 6913            if (reason == null)
 14            {
 4915                return new RuleEvaluationResult { IsMatch = true, Rule = rule };
 16            }
 17        }
 18
 1419        return new RuleEvaluationResult
 1420        {
 1421            IsMatch = false,
 1422            RejectReason = "Не найдено подходящее активное правило."
 1423        };
 4924    }
 25
 26    private static string? ValidateRule(EmailRoutingRule rule, InboxMessageDto message, InboxAttachmentDto attachment)
 27    {
 6928        if (!rule.IsActive)
 29        {
 230            return "Правило выключено.";
 31        }
 32
 6733        if (!string.IsNullOrWhiteSpace(rule.AllowedSenders))
 34        {
 2135            var allowed = SplitCsv(rule.AllowedSenders);
 2136            var sender = (message.SenderEmail ?? string.Empty).Trim().ToLowerInvariant();
 2137            if (!IsSenderAllowed(sender, allowed))
 38            {
 1039                return "Отправитель не разрешен правилом.";
 40            }
 41        }
 42
 5743        if (!MatchesPattern(rule.SubjectPattern, message.Subject))
 44        {
 145            return "Тема письма не соответствует правилу.";
 46        }
 47
 5648        if (!MatchesPattern(rule.BodyPattern, message.BodyText))
 49        {
 150            return "Тело письма не соответствует правилу.";
 51        }
 52
 5553        if (!MatchesPattern(rule.FileNamePattern, attachment.FileName))
 54        {
 155            return "Имя файла не соответствует правилу.";
 56        }
 57
 5458        if (!string.IsNullOrWhiteSpace(rule.AllowedExtensions))
 59        {
 1360            var allowedExts = SplitCsv(rule.AllowedExtensions)
 2461                .Select(x => x.StartsWith('.') ? x : "." + x)
 1362                .ToHashSet(StringComparer.OrdinalIgnoreCase);
 1363            if (!allowedExts.Contains(attachment.Extension ?? string.Empty))
 64            {
 365                return "Расширение файла не разрешено.";
 66            }
 67        }
 68
 5169        if (rule.MinFileSizeBytes.HasValue && attachment.FileSizeBytes < rule.MinFileSizeBytes.Value)
 70        {
 171            return "Размер файла меньше минимально допустимого.";
 72        }
 73
 5074        if (rule.MaxFileSizeBytes.HasValue && attachment.FileSizeBytes > rule.MaxFileSizeBytes.Value)
 75        {
 176            return "Размер файла больше максимально допустимого.";
 77        }
 78
 4979        return null;
 80    }
 81
 82    private static bool MatchesPattern(string? pattern, string? input)
 83    {
 16884        if (string.IsNullOrWhiteSpace(pattern))
 85        {
 14886            return true;
 87        }
 88
 2089        return Regex.IsMatch(input ?? string.Empty, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
 90    }
 91
 92    private static List<string> SplitCsv(string csv)
 93    {
 3494        return csv.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
 4795            .Select(x => x.Trim().ToLowerInvariant())
 4796            .Where(x => !string.IsNullOrWhiteSpace(x))
 3497            .ToList();
 98    }
 99
 100    private static bool IsSenderAllowed(string sender, List<string> allowedItems)
 101    {
 21102        if (string.IsNullOrWhiteSpace(sender))
 103        {
 1104            return false;
 105        }
 106
 73107        foreach (var item in allowedItems)
 108        {
 109            // Exact sender match, e.g. supplier@example.com
 22110            if (sender.Equals(item, StringComparison.OrdinalIgnoreCase))
 111            {
 7112                return true;
 113            }
 114
 115            // Domain match from UI hint, e.g. @@domain.ru
 15116            if (item.StartsWith("@@") && sender.EndsWith(item[1..], StringComparison.OrdinalIgnoreCase))
 117            {
 4118                return true;
 119            }
 120        }
 121
 9122        return false;
 11123    }
 124}