< Summary

Information
Class: ArturRios.Util.Test.Mock.FakeRepository<T>
Assembly: ArturRios.Util.Test
File(s): D:\Repositories\dotnet-test-util\src\Mock\FakeRepository.cs
Line coverage
100%
Covered lines: 72
Uncovered lines: 0
Coverable lines: 72
Total lines: 160
Line coverage: 100%
Branch coverage
90%
Covered branches: 20
Total branches: 22
Branch coverage: 90.9%
Method coverage
100%
Covered methods: 11
Fully covered methods: 3
Total methods: 11
Method coverage: 100%
Full method coverage: 27.2%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
Query()100%11100%
GetAll()100%11100%
GetById(...)100%22100%
Create(...)100%11100%
CreateRange(...)100%22100%
Update(...)100%22100%
UpdateRange(...)100%44100%
Delete(...)100%22100%
DeleteRange(...)75%44100%
CopyWritableProperties(...)83.33%66100%

File(s)

D:\Repositories\dotnet-test-util\src\Mock\FakeRepository.cs

#LineLine coverage
 1using ArturRios.Data.Relational.Core.Entities;
 2using ArturRios.Data.Relational.Core.Interfaces;
 3using ArturRios.Output;
 4
 5namespace ArturRios.Util.Test.Mock;
 6
 7/// <summary>
 8/// In-memory implementation of <see cref="IRepository{T}"/> for use in tests.
 9/// Entities are stored in a backing list and identifiers are assigned sequentially starting at <c>1</c>.
 10/// Lookups that find no matching entity return a failed <see cref="DataOutput{T}"/> carrying an error rather than throw
 11/// </summary>
 12/// <typeparam name="T">The entity type handled by the repository.</typeparam>
 13public class FakeRepository<T> : IRepository<T> where T : Entity
 14{
 1315    private readonly List<T> _items = [];
 1316    private long _nextId = 1;
 17
 18    /// <summary>Exposes the stored entities as a queryable sequence.</summary>
 19    /// <returns>A queryable over every stored entity.</returns>
 120    public IQueryable<T> Query() => _items.AsQueryable();
 21
 22    /// <summary>Returns all stored entities.</summary>
 23    /// <returns>A successful output whose data contains every stored entity.</returns>
 224    public DataOutput<IEnumerable<T>> GetAll() => DataOutput<IEnumerable<T>>.New.WithData(_items);
 25
 26    /// <summary>Returns the entity with the given identifier.</summary>
 27    /// <param name="id">The identifier to look up.</param>
 28    /// <returns>
 29    /// A successful output carrying the matching entity, or a failed output when no stored entity has that identifier.
 30    /// </returns>
 31    public DataOutput<T?> GetById(long id)
 932    {
 1733        var item = _items.FirstOrDefault(x => x.Id == id);
 34
 935        return item is null
 936            ? DataOutput<T?>.New.WithError($"Entity with Id {id} not found")
 937            : DataOutput<T?>.New.WithData(item);
 938    }
 39
 40    /// <summary>Adds <paramref name="entity"/> to the store, assigning it a fresh identifier.</summary>
 41    /// <param name="entity">The entity to store.</param>
 42    /// <returns>A successful output carrying the identifier assigned to the entity.</returns>
 43    public DataOutput<long> Create(T entity)
 1544    {
 1545        entity.Id = _nextId++;
 46
 1547        _items.Add(entity);
 48
 1549        return DataOutput<long>.New.WithData(entity.Id);
 1550    }
 51
 52    /// <summary>Adds every entity in <paramref name="entities"/> to the store, assigning each a fresh identifier.</summ
 53    /// <param name="entities">The entities to store.</param>
 54    /// <returns>A successful output carrying the identifiers assigned, in insertion order.</returns>
 55    public DataOutput<IEnumerable<long>> CreateRange(IEnumerable<T> entities)
 156    {
 157        var ids = new List<long>();
 58
 759        foreach (var entity in entities)
 260        {
 261            entity.Id = _nextId++;
 62
 263            _items.Add(entity);
 264            ids.Add(entity.Id);
 265        }
 66
 167        return DataOutput<IEnumerable<long>>.New.WithData(ids);
 168    }
 69
 70    /// <summary>Copies the writable properties of <paramref name="entity"/> onto the stored entity with the same identi
 71    /// <param name="entity">The entity carrying the new values.</param>
 72    /// <returns>
 73    /// A successful output carrying the updated stored entity, or a failed output when no stored entity has a matching 
 74    /// </returns>
 75    public DataOutput<T> Update(T entity)
 276    {
 377        var existingItem = _items.FirstOrDefault(item => item.Id == entity.Id);
 78
 279        if (existingItem is null)
 180        {
 181            return DataOutput<T>.New.WithError($"Entity with Id {entity.Id} not found");
 82        }
 83
 184        CopyWritableProperties(entity, existingItem);
 85
 186        return DataOutput<T>.New.WithData(existingItem);
 287    }
 88
 89    /// <summary>Updates every entity that exists in the store, silently skipping identifiers that are not found.</summa
 90    /// <param name="entities">The entities carrying the new values.</param>
 91    /// <returns>A successful output carrying the stored entities that were updated.</returns>
 92    public DataOutput<IEnumerable<T>> UpdateRange(IEnumerable<T> entities)
 293    {
 294        var updated = new List<T>();
 95
 1496        foreach (var entity in entities)
 497        {
 998            var existingItem = _items.FirstOrDefault(item => item.Id == entity.Id);
 99
 4100            if (existingItem is null)
 1101            {
 102                // Entities that do not exist are silently skipped.
 1103                continue;
 104            }
 105
 3106            CopyWritableProperties(entity, existingItem);
 3107            updated.Add(existingItem);
 3108        }
 109
 2110        return DataOutput<IEnumerable<T>>.New.WithData(updated);
 2111    }
 112
 113    /// <summary>Removes the stored entity with the same identifier as <paramref name="entity"/>.</summary>
 114    /// <param name="entity">The entity to remove.</param>
 115    /// <returns>
 116    /// A successful output carrying the identifier of the removed entity, or a failed output when no stored entity has 
 117    /// </returns>
 118    public DataOutput<long> Delete(T entity)
 2119    {
 3120        var existingItem = _items.FirstOrDefault(item => item.Id == entity.Id);
 121
 2122        if (existingItem is null)
 1123        {
 1124            return DataOutput<long>.New.WithError($"Entity with Id {entity.Id} not found");
 125        }
 126
 1127        _items.Remove(existingItem);
 128
 1129        return DataOutput<long>.New.WithData(existingItem.Id);
 2130    }
 131
 132    /// <summary>Removes every stored entity whose identifier is in <paramref name="ids"/>.</summary>
 133    /// <param name="ids">The identifiers to remove.</param>
 134    /// <returns>A successful output carrying the identifiers that were actually removed.</returns>
 135    public DataOutput<IEnumerable<long>> DeleteRange(IEnumerable<long> ids)
 1136    {
 1137        var idSet = ids.ToHashSet();
 4138        var entities = _items.Where(e => idSet.Contains(e.Id)).ToList();
 139
 7140        foreach (var entity in entities)
 2141        {
 2142            _items.Remove(entity);
 2143        }
 144
 3145        return DataOutput<IEnumerable<long>>.New.WithData(entities.Select(e => e.Id).ToList());
 1146    }
 147
 148    private static void CopyWritableProperties(T source, T target)
 4149    {
 36150        foreach (var prop in typeof(T).GetProperties())
 12151        {
 12152            if (!prop.CanWrite || prop.Name == nameof(Entity.Id))
 4153            {
 4154                continue;
 155            }
 156
 8157            prop.SetValue(target, prop.GetValue(source));
 8158        }
 4159    }
 160}