• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

AndreuCodina / CrossValidation / 4276616263

pending completion
4276616263

Pull #19

github

GitHub
Merge 5b105545e into 33f23a935
Pull Request #19: General errors by default with That()

324 of 356 branches covered (91.01%)

Branch coverage included in aggregate %.

46 of 46 new or added lines in 4 files covered. (100.0%)

966 of 1034 relevant lines covered (93.42%)

123.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.48
/src/CrossValidation/Validations/ValidValidation.cs
1
using System.Diagnostics;
2
using System.Net;
3
using CrossValidation.Errors;
4
using CrossValidation.Exceptions;
5
using CrossValidation.Resources;
6

7
namespace CrossValidation.Validations;
8

9
public interface IValidValidation<out TField> : IValidation<TField>
10
{
11
    public string? Code { get; set; }
12
    public string? Message { get; set; }
13
    public string? Details { get; set; }
14
    public ICrossError? Error { get; set; }
15
    public string? FieldDisplayName { get; set; }
16
    public HttpStatusCode? HttpStatusCode { get; set; }
17
    bool ExecuteNextValidator { get; set; }
18
    public TField GetFieldValue();
19
    public ValidationContext Context { get; set; }
20
    public string? FieldFullPath { get; set; }
21
    public Type CrossErrorToException { get; set; }
22
    void HandleError(ICrossError error);
23
    void TakeErrorCustomizations(ICrossError error, bool overrideCustomizations);
24

25
    public static IValidation<TField> CreateFromField(
26
        TField fieldValue,
27
        Type crossErrorToException,
28
        bool generalizeError = true,
29
        string? fieldFullPath = null,
30
        ValidationContext? context = null,
31
        int? index = null,
32
        string? parentPath = null,
33
        ICrossError? error = null,
34
        string? message = null,
35
        string? code = null,
36
        string? details = null,
37
        HttpStatusCode? httpStatusCode = null,
38
        string? fieldDisplayName = null)
39
    {
40
        return new ValidValidation<TField>(
174✔
41
            fieldValue: fieldValue,
174✔
42
            crossErrorToException: crossErrorToException,
174✔
43
            generalizeError: generalizeError,
174✔
44
            fieldFullPath: fieldFullPath,
174✔
45
            context: context,
174✔
46
            index: index,
174✔
47
            parentPath: parentPath,
174✔
48
            error: error,
174✔
49
            message: message,
174✔
50
            code: code,
174✔
51
            details: details,
174✔
52
            httpStatusCode: httpStatusCode,
174✔
53
            fieldDisplayName: fieldDisplayName);
174✔
54
    }
55

56
    public static IValidation<TField> CreateFromFieldName(
57
        TField fieldValue,
58
        Type crossErrorToException,
59
        string fieldName,
60
        bool allowFieldNameWithoutModel,
61
        ValidationContext? context = null,
62
        ICrossError? error = null,
63
        string? message = null,
64
        string? code = null,
65
        string? details = null,
66
        HttpStatusCode? httpStatusCode = null,
67
        string? fieldDisplayName = null)
68
    {
69
        if (!allowFieldNameWithoutModel)
87✔
70
        {
71
            if (!fieldName.Contains("."))
85✔
72
            {
73
                throw new ArgumentException("Use Field without a model is not allowed");
1✔
74
            }
75
        }
76

77
        var fieldFullPath = allowFieldNameWithoutModel
86✔
78
            ? fieldName
86✔
79
            : fieldName.Substring(fieldName.IndexOf('.') + 1);
86✔
80
        return new ValidValidation<TField>(
86✔
81
            fieldValue: fieldValue,
86✔
82
            crossErrorToException: crossErrorToException,
86✔
83
            generalizeError: false,
86✔
84
            fieldFullPath: fieldFullPath,
86✔
85
            context: context,
86✔
86
            error: error,
86✔
87
            message: message,
86✔
88
            code: code,
86✔
89
            details: details,
86✔
90
            httpStatusCode: httpStatusCode,
86✔
91
            fieldDisplayName: fieldDisplayName);
86✔
92
    }
93

94
    void Clean();
95
}
96

97
file class ValidValidation<TField> :
98
    Validation<TField>,
99
    IValidValidation<TField>
100
{
101
    private string? _code;
102
    private string? _message;
103
    private string? _details;
104
    private ICrossError? _error;
105
    private string? _fieldDisplayName;
106
    private HttpStatusCode? _httpStatusCode;
107

108
    public TField FieldValue { get; set; }
735✔
109
    public ValidationContext Context { get; set; }
7,946✔
110
    public string? FieldFullPath { get; set; }
268✔
111
    public Type CrossErrorToException { get; set; }
517✔
112

113
    public string? Code
114
    {
115
        get
116
        {
117
            if (Context.Code is not null)
348✔
118
            {
119
                return Context.Code;
10✔
120
            }
121

122
            return _code;
338✔
123
        }
124
        set
125
        {
126
            if (Context.Code is not null)
233✔
127
            {
128
                return;
6✔
129
            }
130

131
            _code = value;
227✔
132
        }
227✔
133
    }
134

135
    public string? Message
136
    {
137
        get
138
        {
139
            if (Context.Message is not null)
224✔
140
            {
141
                return Context.Message;
10✔
142
            }
143

144
            return _message;
214✔
145
        }
146
        set
147
        {
148
            if (Context.Message is not null)
216✔
149
            {
150
                return;
6✔
151
            }
152

153
            _message = value;
210✔
154
        }
210✔
155
    }
156

157
    public string? Details
158
    {
159
        get
160
        {
161
            if (Context.Details is not null)
200✔
162
            {
163
                return Context.Details;
7✔
164
            }
165

166
            return _details;
193✔
167
        }
168
        set
169
        {
170
            if (Context.Details is not null)
206✔
171
            {
172
                return;
6✔
173
            }
174

175
            _details = value;
200✔
176
        }
200✔
177
    }
178

179
    public ICrossError? Error
180
    {
181
        get
182
        {
183
            if (Context.Error is not null)
168✔
184
            {
185
                return Context.Error;
4✔
186
            }
187

188
            return _error;
164✔
189
        }
190
        set
191
        {
192
            if (Context.Error is not null)
178✔
193
            {
194
                return;
3✔
195
            }
196

197
            _error = value;
175✔
198
        }
175✔
199
    }
200

201
    public string? FieldDisplayName
202
    {
203
        get
204
        {
205
            if (Context.FieldDisplayName is not null)
212✔
206
            {
207
                return Context.FieldDisplayName;
10✔
208
            }
209

210
            return _fieldDisplayName;
202✔
211
        }
212
        set
213
        {
214
            if (Context.FieldDisplayName is not null)
470✔
215
            {
216
                return;
8✔
217
            }
218

219
            _fieldDisplayName = value;
462✔
220
        }
462✔
221
    }
222

223
    public HttpStatusCode? HttpStatusCode
224
    {
225
        get
226
        {
227
            if (Context.HttpStatusCode is not null)
201✔
228
            {
229
                return Context.HttpStatusCode;
7✔
230
            }
231

232
            return _httpStatusCode;
194✔
233
        }
234
        set
235
        {
236
            if (Context.HttpStatusCode is not null)
203✔
237
            {
238
                return;
6✔
239
            }
240

241
            _httpStatusCode = value;
197✔
242
        }
197✔
243
    }
244

245
    public bool ExecuteNextValidator { get; set; } = true;
900✔
246

247
    public ValidValidation(
260✔
248
        TField fieldValue,
260✔
249
        Type crossErrorToException,
260✔
250
        bool generalizeError,
260✔
251
        string? fieldFullPath = null,
260✔
252
        ValidationContext? context = null,
260✔
253
        int? index = null,
260✔
254
        string? parentPath = null,
260✔
255
        ICrossError? error = null,
260✔
256
        string? message = null,
260✔
257
        string? code = null,
260✔
258
        string? details = null,
260✔
259
        HttpStatusCode? httpStatusCode = null,
260✔
260
        string? fieldDisplayName = null)
260✔
261
    {
262
        FieldValue = fieldValue;
260✔
263
        CrossErrorToException = crossErrorToException;
260✔
264
        Context = context ?? new ValidationContext();
260✔
265
        FieldDisplayName = null;
260✔
266
        FieldFullPath = fieldFullPath;
260✔
267
        Context.GeneralizeError = Context.IsChildContext && Context.GeneralizeError
260✔
268
            ? true
260✔
269
            : generalizeError;;
260✔
270

271
        var indexRepresentation = Context.FieldName is not null && index is not null
260✔
272
            ? $"[{index}]"
260✔
273
            : null;
260✔
274
        string? parentPathValue = null;
260✔
275

276
        if (parentPath is not null)
260!
277
        {
278
            parentPathValue = parentPath;
×
279
        }
280
        else if (Context.ParentPath is not null)
260✔
281
        {
282
            parentPathValue = Context.ParentPath;
8✔
283
        }
284

285
        if (parentPathValue is not null)
260✔
286
        {
287
            parentPathValue += ".";
8✔
288
        }
289

290
        Context.FieldName = parentPathValue + fieldFullPath + indexRepresentation;
260✔
291

292
        if (Context.FieldName is "")
260✔
293
        {
294
            Context.FieldName = null;
110✔
295
        }
296

297
        Context.Error = error;
260✔
298
        Context.Message = message;
260✔
299
        Context.Code = code;
260✔
300
        Context.Details = details;
260✔
301
        Context.HttpStatusCode = httpStatusCode;
260✔
302
        Context.FieldDisplayName = fieldDisplayName;
260✔
303
    }
260✔
304

305
    public TField GetFieldValue()
306
    {
307
        return FieldValue;
341✔
308
    }
309

310
    public void TakeErrorCustomizations(ICrossError error, bool overrideCustomizations)
311
    {
312
        if (!overrideCustomizations)
38✔
313
        {
314
            return;
4✔
315
        }
316

317
        Code = error.Code ?? Code;
34✔
318
        Message = error.Message ?? Message;
34✔
319
        Details = error.Details ?? Details;
34✔
320
        HttpStatusCode = error.HttpStatusCode ?? HttpStatusCode;
34✔
321
        FieldDisplayName = error.FieldDisplayName ?? FieldDisplayName;
34✔
322
    }
34✔
323

324
    public void HandleError(ICrossError error)
325
    {
326
        var errorToAdd = Error ?? error;
124✔
327
        AddError(errorToAdd);
124✔
328

329
        if (Context is {ValidationMode: ValidationMode.StopValidationOnFirstError})
124✔
330
        {
331
            if (Context.ErrorsCollected!.Count == 1)
89✔
332
            {
333
                throw Context.ErrorsCollected[0].ToException();
89✔
334
            }
335
        }
336
    }
35✔
337

338
    public override TField Instance()
339
    {
340
        return FieldValue;
6✔
341
    }
342

343
    public override TInstance Instance<TInstance>(Func<TField, TInstance> fieldToInstance)
344
    {
345
        try
346
        {
347
            return fieldToInstance(FieldValue);
4✔
348
        }
349
        catch (CrossException e)
4✔
350
        {
351
            e.Error.FieldName = null;
4✔
352
            e.Error.FieldValue = null;
4✔
353
            e.Error.PlaceholderValues = null;
4✔
354
            e.Error.CrossErrorToException = CrossErrorToException;
4✔
355
            TakeErrorCustomizations(e.Error, overrideCustomizations: false);
4✔
356
            HandleError(e.Error);
4✔
357
            throw new UnreachableException();
×
358
        }
359
    }
×
360

361
    public void Clean()
362
    {
363
        Code = null;
144✔
364
        Message = null;
144✔
365
        Details = null;
144✔
366
        Error = null;
144✔
367
        HttpStatusCode = null;
144✔
368
        FieldDisplayName = null;
144✔
369
        ExecuteNextValidator = true;
144✔
370
    }
144✔
371

372
    private void AddError(ICrossError error)
373
    {
374
        AddCustomizationsToError(error);
124✔
375
        Context.ErrorsCollected ??= new List<ICrossError>();
124✔
376
        error.AddPlaceholderValues();
124✔
377
        Context.ErrorsCollected.Add(error);
124✔
378
    }
124✔
379

380
    private void AddCustomizationsToError(ICrossError error)
381
    {
382
        error.FieldName = Context.FieldName;
124✔
383
        error.FieldDisplayName = GetFieldDisplayNameToFill(error);
124✔
384
        error.FieldValue = FieldValue;
124✔
385
        
386
        if (Code is not null)
124✔
387
        {
388
            error.Code = Code;
34✔
389
        }
390
        else if (Context.GeneralizeError)
90✔
391
        {
392
            error.Code = nameof(ErrorResource.General);
35✔
393
        }
394
        else
395
        {
396
            error.Code = error.Code;
55✔
397
        }
398
        
399
        error.Message = GetMessageToFill(error);
124✔
400
        error.Details = Details ?? error.Details;
124✔
401
        error.HttpStatusCode = HttpStatusCode ?? error.HttpStatusCode;
124✔
402
    }
124✔
403

404
    private string? GetMessageToFill(ICrossError error)
405
    {
406
        if (Message is not null)
124✔
407
        {
408
            return Message;
25✔
409
        }
410

411
        if (Code is not null)
99✔
412
        {
413
            // if (Context.GeneralizeError)
414
            // {
415
            //     return CrossValidationOptions.GetMessageFromCode(nameof(ErrorResource.General));
416
            // }
417
            
418
            return CrossValidationOptions.GetMessageFromCode(Code);
20✔
419
        }
420

421
        if (error.Message is not null)
79✔
422
        {
423
            if (Context.GeneralizeError)
74✔
424
            {
425
                return CrossValidationOptions.GetMessageFromCode(nameof(ErrorResource.General));
26✔
426
            }
427
            
428
            return error.Message;
48✔
429
        }
430

431
        if (error.Code is not null)
5!
432
        {
433
            if (Context.GeneralizeError)
5!
434
            {
435
                return CrossValidationOptions.GetMessageFromCode(nameof(ErrorResource.General));
5✔
436
            }
437
            
438
            return CrossValidationOptions.GetMessageFromCode(error.Code);
×
439
        }
440

441
        return null;
×
442
    }
443

444
    private string GetFieldDisplayNameToFill(ICrossError error)
445
    {
446
        if (FieldDisplayName is not null)
124✔
447
        {
448
            return FieldDisplayName;
10✔
449
        }
450

451
        if (error.FieldDisplayName is not null)
114!
452
        {
453
            return error.FieldDisplayName;
×
454
        }
455

456
        return error.FieldName!;
114✔
457
    }
458
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc