< Summary

Information
Class: ArturRios.Util.Test.Mock.AsyncFakeRepository<T1, T2>
Assembly: ArturRios.Util.Test
File(s): /home/runner/work/dotnet-test-util/dotnet-test-util/src/Mock/AsyncFakeRepository.cs
Line coverage
100%
Covered lines: 13
Uncovered lines: 0
Coverable lines: 13
Total lines: 138
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 12
Fully covered methods: 12
Total methods: 12
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
.ctor(...)100%11100%
Query()100%11100%
GetAllAsync(...)100%11100%
GetByIdAsync(...)100%11100%
CreateAsync(...)100%11100%
CreateRangeAsync(...)100%11100%
UpdateAsync(...)100%11100%
UpdateRangeAsync(...)100%11100%
DeleteAsync(...)100%11100%
DeleteRangeAsync(...)100%11100%
Run(...)100%11100%

File(s)

/home/runner/work/dotnet-test-util/dotnet-test-util/src/Mock/AsyncFakeRepository.cs

#LineLine coverage
 1using ArturRios.Data.Relational.Core.Entities;
 2using ArturRios.Data.Relational.Core.Interfaces;
 3using ArturRios.Data.Relational.Core.Repositories;
 4using ArturRios.Output;
 5
 6namespace ArturRios.Util.Test.Mock;
 7
 8/// <summary>
 9/// In-memory implementation of <see cref="IAsyncRepository{T, TKey}"/> for use in tests.
 10/// Entities are stored in a backing list. Outcomes match <c>EfRepository&lt;T, TKey&gt;</c>, so code tested against the
 11/// fake meets the same results in production: a lookup that finds nothing succeeds with no data; an update or delete
 12/// that would affect no row — an unknown id, or an <see cref="IVersionedEntity"/> whose
 13/// <see cref="IVersionedEntity.ConcurrencyStamp"/> is stale — fails with <see cref="RelationalErrors.ConcurrencyMessage
 14/// and changes nothing; and an update issues a new stamp, as the real context does.
 15/// Operations complete synchronously; each returns an already-completed <see cref="Task"/> and honors the supplied
 16/// <see cref="CancellationToken"/>.
 17/// </summary>
 18/// <remarks>
 19/// <para>How <see cref="CreateAsync"/> and <see cref="CreateRangeAsync"/> assign identifiers depends on the key type:</
 20/// <list type="bullet">
 21/// <item><description><see cref="long"/> and <see cref="int"/>: sequentially, starting at <c>1</c>.</description></item
 22/// <item><description><see cref="Guid"/>: <see cref="Guid.NewGuid"/>.</description></item>
 23/// <item><description>
 24/// Any other key type (e.g. <see cref="string"/>): the caller assigns the identifier before creating the entity;
 25/// creating one whose identifier is unset, or already stored, returns a failed output.
 26/// </description></item>
 27/// </list>
 28/// <para>
 29/// When a generator applies, it overwrites any identifier the caller set. Pass an id generator to the constructor
 30/// to replace the default for any key type.
 31/// </para>
 32/// </remarks>
 33/// <typeparam name="T">The entity type handled by the repository.</typeparam>
 34/// <typeparam name="TKey">The entity's primary key type.</typeparam>
 35public class AsyncFakeRepository<T, TKey> : IAsyncRepository<T, TKey> where T : Entity<TKey> where TKey : IEquatable<TKe
 36{
 37    private readonly FakeEntityStore<T, TKey> _store;
 38
 39    /// <summary>Creates an empty repository that assigns identifiers with the default for <typeparamref name="TKey"/>.<
 5840    public AsyncFakeRepository() : this(null) { }
 41
 42    /// <summary>Creates an empty repository that assigns identifiers with <paramref name="idGenerator"/>.</summary>
 43    /// <param name="idGenerator">
 44    /// Called once per created entity to produce its identifier, or <c>null</c> to use the default for
 45    /// <typeparamref name="TKey"/>.
 46    /// </param>
 6047    public AsyncFakeRepository(Func<TKey>? idGenerator) => _store = new FakeEntityStore<T, TKey>(idGenerator);
 48
 49    /// <summary>Exposes the stored entities as a queryable sequence.</summary>
 50    /// <returns>
 51    /// A queryable over every stored entity backed by an async query provider, so EF Core's async operators
 52    /// (<c>ToListAsync</c>, <c>FirstOrDefaultAsync</c>, <c>CountAsync</c>, …) can be composed on top of it.
 53    /// </returns>
 1054    public IQueryable<T> Query() => new TestAsyncEnumerable<T>(_store.Items);
 55
 56    /// <summary>Returns all stored entities.</summary>
 57    /// <param name="ct">A token to observe for cancellation.</param>
 58    /// <returns>A successful output whose data is a snapshot of every stored entity, unaffected by later writes.</retur
 59    public Task<DataOutput<IEnumerable<T>>> GetAllAsync(CancellationToken ct = default) =>
 460        Run(_store.GetAll, ct);
 61
 62    /// <summary>Returns the entity with the given identifier.</summary>
 63    /// <param name="id">The identifier to look up.</param>
 64    /// <param name="ct">A token to observe for cancellation.</param>
 65    /// <returns>
 66    /// A successful output carrying the matching entity, or a successful output with no data when no stored entity
 67    /// has that identifier.
 68    /// </returns>
 69    public Task<DataOutput<T?>> GetByIdAsync(TKey id, CancellationToken ct = default) =>
 3470        Run(() => _store.GetById(id), ct);
 71
 72    /// <summary>Adds <paramref name="entity"/> to the store, assigning its identifier as described on the class.</summa
 73    /// <param name="entity">The entity to store.</param>
 74    /// <param name="ct">A token to observe for cancellation.</param>
 75    /// <returns>
 76    /// A successful output carrying the entity's identifier, or a failed output when the identifier is unset (for
 77    /// key types without a generator) or already stored (<see cref="RelationalErrors.UniqueViolationMessage"/>).
 78    /// </returns>
 79    public Task<DataOutput<TKey>> CreateAsync(T entity, CancellationToken ct = default) =>
 6280        Run(() => _store.Create(entity), ct);
 81
 82    /// <summary>Adds every entity in <paramref name="entities"/> to the store, assigning identifiers as described on th
 83    /// <param name="entities">The entities to store.</param>
 84    /// <param name="ct">A token to observe for cancellation.</param>
 85    /// <returns>
 86    /// A successful output carrying the identifiers, in insertion order, or a failed output when any identifier is unse
 87    /// or duplicated — in which case nothing is stored.
 88    /// </returns>
 89    public Task<DataOutput<IEnumerable<TKey>>> CreateRangeAsync(IEnumerable<T> entities, CancellationToken ct = default)
 890        Run(() => _store.CreateRange(entities), ct);
 91
 92    /// <summary>Copies the writable properties of <paramref name="entity"/> onto the stored entity with the same identi
 93    /// <param name="entity">The entity carrying the new values.</param>
 94    /// <param name="ct">A token to observe for cancellation.</param>
 95    /// <returns>
 96    /// A successful output carrying the updated stored entity, or a failed output carrying
 97    /// <see cref="RelationalErrors.ConcurrencyMessage"/> when no stored entity has a matching identifier (and, for an
 98    /// <see cref="IVersionedEntity"/>, concurrency stamp).
 99    /// </returns>
 100    public Task<DataOutput<T>> UpdateAsync(T entity, CancellationToken ct = default) =>
 10101        Run(() => _store.Update(entity), ct);
 102
 103    /// <summary>Updates every entity in <paramref name="entities"/>, all or none: when any of them would update no stor
 104    /// (see the single-entity update), nothing is updated.</summary>
 105    /// <param name="entities">The entities carrying the new values.</param>
 106    /// <param name="ct">A token to observe for cancellation.</param>
 107    /// <returns>
 108    /// A successful output carrying the updated stored entities, or a failed output carrying
 109    /// <see cref="RelationalErrors.ConcurrencyMessage"/>.
 110    /// </returns>
 111    public Task<DataOutput<IEnumerable<T>>> UpdateRangeAsync(IEnumerable<T> entities, CancellationToken ct = default) =>
 6112        Run(() => _store.UpdateRange(entities), ct);
 113
 114    /// <summary>Removes the stored entity with the same identifier as <paramref name="entity"/>.</summary>
 115    /// <param name="entity">The entity to remove.</param>
 116    /// <param name="ct">A token to observe for cancellation.</param>
 117    /// <returns>
 118    /// A successful output carrying the identifier of the removed entity, or a failed output carrying
 119    /// <see cref="RelationalErrors.ConcurrencyMessage"/> when no stored entity has a matching identifier (and, for an
 120    /// <see cref="IVersionedEntity"/>, concurrency stamp).
 121    /// </returns>
 122    public Task<DataOutput<TKey>> DeleteAsync(T entity, CancellationToken ct = default) =>
 4123        Run(() => _store.Delete(entity), ct);
 124
 125    /// <summary>Removes every stored entity whose identifier is in <paramref name="ids"/>.</summary>
 126    /// <param name="ids">The identifiers to remove.</param>
 127    /// <param name="ct">A token to observe for cancellation.</param>
 128    /// <returns>A successful output carrying the identifiers that were actually removed.</returns>
 129    public Task<DataOutput<IEnumerable<TKey>>> DeleteRangeAsync(IEnumerable<TKey> ids, CancellationToken ct = default) =
 4130        Run(() => _store.DeleteRange(ids), ct);
 131
 132    private static Task<TResult> Run<TResult>(Func<TResult> operation, CancellationToken ct)
 133    {
 69134        ct.ThrowIfCancellationRequested();
 135
 66136        return Task.FromResult(operation());
 137    }
 138}