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

AndreuCodina / CrossValidation / 4282269105

pending completion
4282269105

Pull #19

github

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

328 of 360 branches covered (91.11%)

Branch coverage included in aggregate %.

55 of 55 new or added lines in 5 files covered. (100.0%)

970 of 1038 relevant lines covered (93.45%)

128.32 hits per line

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

96.58
/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 TakeCustomizationsFromError(ICrossError error);
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>(
189✔
41
            fieldValue: fieldValue,
189✔
42
            crossErrorToException: crossErrorToException,
189✔
43
            generalizeError: generalizeError,
189✔
44
            fieldFullPath: fieldFullPath,
189✔
45
            context: context,
189✔
46
            index: index,
189✔
47
            parentPath: parentPath,
189✔
48
            error: error,
189✔
49
            message: message,
189✔
50
            code: code,
189✔
51
            details: details,
189✔
52
            httpStatusCode: httpStatusCode,
189✔
53
            fieldDisplayName: fieldDisplayName);
189✔
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; }
780✔
109
    public ValidationContext Context { get; set; }
8,495✔
110
    public string? FieldFullPath { get; set; }
283✔
111
    public Type CrossErrorToException { get; set; }
550✔
112

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

310
    public void TakeCustomizationsFromError(ICrossError error)
311
    {
312
        Code = error.Code ?? Code;
37✔
313
        Message = error.Message ?? Message;
37✔
314
        Details = error.Details ?? Details;
37✔
315
        HttpStatusCode = error.HttpStatusCode ?? HttpStatusCode;
37✔
316
        FieldDisplayName = error.FieldDisplayName ?? FieldDisplayName;
37✔
317
    }
37✔
318

319
    public void HandleError(ICrossError error)
320
    {
321
        var errorToAdd = Error ?? error;
139✔
322
        AddError(errorToAdd);
139✔
323

324
        if (Context is {ValidationMode: ValidationMode.StopValidationOnFirstError})
139✔
325
        {
326
            if (Context.ErrorsCollected!.Count == 1)
104✔
327
            {
328
                throw Context.ErrorsCollected[0].ToException();
104✔
329
            }
330
        }
331
    }
35✔
332

333
    public override TField Instance()
334
    {
335
        return FieldValue;
6✔
336
    }
337

338
    public override TInstance Instance<TInstance>(Func<TField, TInstance> fieldToInstance)
339
    {
340
        try
341
        {
342
            return fieldToInstance(FieldValue);
11✔
343
        }
344
        catch (CrossException e)
11✔
345
        {
346
            e.Error.FieldName = null;
11✔
347
            e.Error.FieldValue = null;
11✔
348
            e.Error.PlaceholderValues = null;
11✔
349
            e.Error.CrossErrorToException = CrossErrorToException;
11✔
350
            TakeCustomizationsFromInstanceError(e.Error);
11✔
351
            HandleError(e.Error);
11✔
352
            throw new UnreachableException();
×
353
        }
354
    }
×
355

356
    public void Clean()
357
    {
358
        Code = null;
144✔
359
        Message = null;
144✔
360
        Details = null;
144✔
361
        Error = null;
144✔
362
        HttpStatusCode = null;
144✔
363
        FieldDisplayName = null;
144✔
364
        ExecuteNextValidator = true;
144✔
365
    }
144✔
366

367
    private void AddError(ICrossError error)
368
    {
369
        AddCustomizationsToError(error);
139✔
370
        Context.ErrorsCollected ??= new List<ICrossError>();
139✔
371
        error.AddPlaceholderValues();
139✔
372
        Context.ErrorsCollected.Add(error);
139✔
373
    }
139✔
374

375
    private void AddCustomizationsToError(ICrossError error)
376
    {
377
        error.FieldName = Context.FieldName;
139✔
378
        error.FieldDisplayName = GetFieldDisplayNameToFill(error);
139✔
379
        error.FieldValue = FieldValue;
139✔
380
        error.Code = GetCodeToFill(error);
139✔
381
        error.Message = GetMessageToFill(error);
139✔
382
        error.Details = Details ?? error.Details;
139✔
383
        error.HttpStatusCode = HttpStatusCode ?? error.HttpStatusCode;
139✔
384
    }
139✔
385

386
    private string? GetCodeToFill(ICrossError error)
387
    {
388
        if (Code is not null)
139✔
389
        {
390
            return Code;
44✔
391
        }
392
        
393
        if (Context.GeneralizeError)
95✔
394
        {
395
            return nameof(ErrorResource.General);
40✔
396
        }
397

398
        return error.Code;
55✔
399
    }
400

401
    private string? GetMessageToFill(ICrossError error)
402
    {
403
        if (Message is not null)
139✔
404
        {
405
            return Message;
33✔
406
        }
407

408
        if (Code is not null)
106✔
409
        {
410
            return CrossValidationOptions.GetMessageFromCode(Code);
23✔
411
        }
412
        
413
        if (Context.GeneralizeError)
83✔
414
        {
415
            return CrossValidationOptions.GetMessageFromCode(nameof(ErrorResource.General));
35✔
416
        }
417

418
        if (error.Message is not null)
48!
419
        {
420
            return error.Message;
48✔
421
        }
422

423
        if (error.Code is not null)
×
424
        {
425
            return CrossValidationOptions.GetMessageFromCode(error.Code);
×
426
        }
427

428
        return null;
×
429
    }
430

431
    private string? GetFieldDisplayNameToFill(ICrossError error)
432
    {
433
        if (FieldDisplayName is not null)
139✔
434
        {
435
            return FieldDisplayName;
17✔
436
        }
437

438
        if (error.FieldDisplayName is not null)
122✔
439
        {
440
            return error.FieldDisplayName;
4✔
441
        }
442

443
        if (error.FieldName is not null)
118✔
444
        {
445
            return error.FieldName;
71✔
446
        }
447

448
        return null;
47✔
449
    }
450
    
451
    private void TakeCustomizationsFromInstanceError(ICrossError error)
452
    {
453
        if (!Context.GeneralizeError)
11✔
454
        {
455
            return;
3✔
456
        }
457
        
458
        Code ??= error.Code;
8✔
459
        Message ??= error.Message;
8✔
460
    }
8✔
461
}
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