Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(referrers): support referrers API #180

Merged
merged 47 commits into from
Feb 27, 2025
Merged
Changes from 1 commit
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e3e1217
push manifest with subject
Nov 18, 2024
846e173
add unit tests
Nov 19, 2024
7fca0cb
add unit tests
Nov 19, 2024
ec93662
Merge branch 'main' into feature/pushManifestWithSubject
Nov 19, 2024
7809549
resolve merge conflicts
Nov 19, 2024
c7e4418
add tests
Nov 19, 2024
a6d552f
add comments
Nov 19, 2024
685ab70
add unit test
Nov 20, 2024
1b9ade3
add unit tests
Nov 20, 2024
5041592
resolve comments
Nov 21, 2024
c26ccb6
add SetReferrersSupportLevel func and unit tests
Nov 21, 2024
ddbf048
remove NoReferrerUpdateException and update tests accordingly
Nov 22, 2024
d6e8499
add Index constructor
Nov 24, 2024
cf431f0
add license header
Nov 26, 2024
50e696e
resolve merge conflicts
Nov 26, 2024
856c0ef
add lock on SetReferrerState
Nov 29, 2024
fcb121e
simplify ApplyReferrerChanges
Nov 29, 2024
2b24e07
resolve comments
Dec 3, 2024
7df36ca
Merge branch 'main' into feature/pushManifestWithSubject
Dec 3, 2024
c30d6b2
resolve comments
Dec 13, 2024
b20a202
resolve comments
Dec 20, 2024
07d8e06
resolve comments
Dec 23, 2024
27382ae
resolve comments
Dec 23, 2024
b858999
Merge branch 'main' into feature/pushManifestWithSubject
Dec 23, 2024
404337e
resolve comments
Dec 23, 2024
1041bc0
resolve comments
Dec 23, 2024
85435cc
list referrers
Jan 10, 2025
cd05c4b
fix merge conflict
Jan 12, 2025
e17c50c
add unit tests
Jan 13, 2025
db582c2
add unit tests
Jan 15, 2025
966b2ad
add comments
Feb 3, 2025
037b092
format codes
Feb 3, 2025
71fcfcb
add headers
Feb 3, 2025
dfb1291
Merge branch 'main' into feature/listReferrers
Feb 5, 2025
447a280
add invalidResponseException tests
Feb 5, 2025
f668c22
Merge branch 'main' into feature/listReferrers
Feb 10, 2025
7f0553f
Merge branch 'main' into feature/listReferrers
Feb 18, 2025
7724d7f
address comments
Feb 19, 2025
9f1cc15
address comments
Feb 19, 2025
c64338f
verify returned manifest
Feb 24, 2025
9880d45
change callback function to return IAsyncEnumerable
Feb 25, 2025
703df70
resolve conflicts
Feb 25, 2025
b923532
resolve comments
Feb 26, 2025
5a51f41
resolve comments
Feb 26, 2025
b98b303
resolve comments
Feb 26, 2025
8c7b38e
resolve comments
Feb 26, 2025
28d3be2
resolve comments
Feb 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
simplify ApplyReferrerChanges
Signed-off-by: Patrick Pan <panjiaxuan@microsoft.com>
Patrick Pan committed Nov 29, 2024
commit fcb121e207e7fc7802e79c7d495b3d9bba99fbad
93 changes: 19 additions & 74 deletions src/OrasProject.Oras/Registry/Remote/Referrers.cs
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
// limitations under the License.

using System.Collections.Generic;
using System.Linq;
using OrasProject.Oras.Content;
using OrasProject.Oras.Exceptions;
using OrasProject.Oras.Oci;
@@ -53,9 +54,8 @@
{
// updatedReferrers is a list to store the updated referrers
var updatedReferrers = new List<Descriptor>();
// referrerIndexMap is a Dictionary to store referrer as the key
// and index(int) in the updatedReferrers list as the value
var referrerIndexMap = new Dictionary<BasicDescriptor, int>();
// updatedReferrersSet is a HashSet to store unique referrers
var updatedReferrersSet = new HashSet<BasicDescriptor>();

var updateRequired = false;
foreach (var oldReferrer in oldReferrers)
@@ -67,72 +67,48 @@
continue;
}
var basicDesc = oldReferrer.BasicDescriptor;
if (referrerIndexMap.ContainsKey(basicDesc))
if (updatedReferrersSet.Contains(basicDesc))
{
// Skip any duplicate referrers
updateRequired = true;
continue;
}
// Update the updatedReferrers list
// Add referrer index in the referrerIndexMap

// delete
// ......
if (referrerChange.ReferrerOperation == ReferrerOperation.ReferrerDelete)
// Add referrer index in the updatedReferrersSet
if (referrerChange.ReferrerOperation == ReferrerOperation.ReferrerDelete && Descriptor.Equals(basicDesc, referrerChange.Referrer.BasicDescriptor))
{
var toBeDeletedBasicDesc = referrerChange.Referrer.BasicDescriptor;
if (basicDesc == toBeDeletedBasicDesc)
{
updateRequired = true;
continue;
}
updateRequired = true;
continue;
}
updatedReferrers.Add(oldReferrer);
referrerIndexMap[basicDesc] = updatedReferrers.Count - 1;
updatedReferrersSet.Add(basicDesc);
}

// old => 1, 1
// new => nil
if (!Descriptor.IsEmptyOrNull(referrerChange.Referrer))
{
var basicDesc = referrerChange.Referrer.BasicDescriptor;
switch (referrerChange.ReferrerOperation)
if (referrerChange.ReferrerOperation == ReferrerOperation.ReferrerAdd)
{
case ReferrerOperation.ReferrerAdd:
if (!referrerIndexMap.ContainsKey(basicDesc))
{
// Add the new referrer only when it has not already existed in the referrerIndexMap
updatedReferrers.Add(referrerChange.Referrer);
referrerIndexMap[basicDesc] = updatedReferrers.Count - 1;
}

break;
case ReferrerOperation.ReferrerDelete:
if (referrerIndexMap.TryGetValue(basicDesc, out var index))
{
// Delete the referrer only when it existed in the referrerIndexMap
// updatedReferrers.Remove(basicDesc);

updatedReferrers[index] = Descriptor.EmptyDescriptor();
referrerIndexMap.Remove(basicDesc);
}
break;
default:
break;
if (!updatedReferrersSet.Contains(basicDesc))
{
// Add the new referrer only when it has not already existed in the updatedReferrersSet
updatedReferrers.Add(referrerChange.Referrer);
updatedReferrersSet.Add(basicDesc);
}
}
}

// Skip unnecessary update
if (!updateRequired && referrerIndexMap.Count == oldReferrers.Count)
if (!updateRequired && updatedReferrersSet.Count == oldReferrers.Count)
{
// Check for any new referrers in the referrerIndexMap that are not present in the oldReferrers list
// Check for any new referrers in the updatedReferrersSet that are not present in the oldReferrers list
foreach (var oldReferrer in oldReferrers)
{
var basicDesc = oldReferrer.BasicDescriptor;
if (!referrerIndexMap.ContainsKey(basicDesc))
if (!updatedReferrersSet.Contains(basicDesc))
{
updateRequired = true;
break;

Check warning on line 111 in src/OrasProject.Oras/Registry/Remote/Referrers.cs

Codecov / codecov/patch

src/OrasProject.Oras/Registry/Remote/Referrers.cs#L109-L111

Added lines #L109 - L111 were not covered by tests
}
}

@@ -140,38 +116,7 @@
{
return (updatedReferrers, false);
}
}

Check warning on line 119 in src/OrasProject.Oras/Registry/Remote/Referrers.cs

Codecov / codecov/patch

src/OrasProject.Oras/Registry/Remote/Referrers.cs#L119

Added line #L119 was not covered by tests

RemoveEmptyDescriptors(updatedReferrers, referrerIndexMap.Count);
return (updatedReferrers, true);
}

/// <summary>
/// RemoveEmptyDescriptors removes any empty or null descriptors from the provided list of descriptors,
/// ensuring that only non-empty descriptors remain in the list.
/// It optimizes the list by shifting valid descriptors forward and trimming the remaining elements at the end.
/// The list is truncated to only contain non-empty descriptors up to the specified count.
/// </summary>
/// <param name="descriptors"></param>
/// <param name="numNonEmptyDescriptors"></param>
internal static void RemoveEmptyDescriptors(List<Descriptor> descriptors, int numNonEmptyDescriptors)
{
var lastEmptyIndex = 0;
for (var i = 0; i < descriptors.Count; ++i)
{
if (Descriptor.IsEmptyOrNull(descriptors[i])) continue;
if (i > lastEmptyIndex)
{
// Move the descriptor at index i to lastEmptyIndex
descriptors[lastEmptyIndex] = descriptors[i];
}
++lastEmptyIndex;
if (lastEmptyIndex == numNonEmptyDescriptors)
{
// Break the loop when lastEmptyIndex reaches the number of Non-Empty descriptors
break;
}
}
descriptors.RemoveRange(lastEmptyIndex, descriptors.Count - lastEmptyIndex);
}
}
185 changes: 103 additions & 82 deletions tests/OrasProject.Oras.Tests/Remote/ReferrersTest.cs
Original file line number Diff line number Diff line change
@@ -71,6 +71,108 @@
Assert.True(updateRequired);
}

[Fact]
public void ApplyReferrerChanges_ShouldDeleteReferrers()
{
var oldDescriptor1 = RandomDescriptor();
var oldDescriptor2 = RandomDescriptor();
var oldDescriptor3 = RandomDescriptor();

var oldReferrers = new List<Descriptor>
{
oldDescriptor1,
oldDescriptor2,
oldDescriptor3
};

var expectedReferrers = new List<Descriptor>
{
oldDescriptor1,
oldDescriptor3
};
var referrerChange = new Referrers.ReferrerChange(
oldDescriptor2,
Referrers.ReferrerOperation.ReferrerDelete
);

var (updatedReferrers, updateRequired) = Referrers.ApplyReferrerChanges(oldReferrers, referrerChange);
Assert.Equal(2, updatedReferrers.Count);
for (var i = 0; i < updatedReferrers.Count; ++i)
{
Assert.True(AreDescriptorsEqual(updatedReferrers[i], expectedReferrers[i]));
}
Assert.True(updateRequired);
}


[Fact]
public void ApplyReferrerChanges_ShouldDeleteReferrersWithDuplicates()
{
var oldDescriptor1 = RandomDescriptor();
var oldDescriptor2 = RandomDescriptor();
var oldDescriptor3 = RandomDescriptor();

var oldReferrers = new List<Descriptor>
{
oldDescriptor1,
oldDescriptor2,
oldDescriptor3,
oldDescriptor2,
oldDescriptor2,
oldDescriptor3,
};

var expectedReferrers = new List<Descriptor>
{
oldDescriptor1,
oldDescriptor2
};
var referrerChange = new Referrers.ReferrerChange(
oldDescriptor3,
Referrers.ReferrerOperation.ReferrerDelete
);

var (updatedReferrers, updateRequired) = Referrers.ApplyReferrerChanges(oldReferrers, referrerChange);
Assert.Equal(2, updatedReferrers.Count);
for (var i = 0; i < updatedReferrers.Count; ++i)
{
Assert.True(AreDescriptorsEqual(updatedReferrers[i], expectedReferrers[i]));
}
Assert.True(updateRequired);
}

[Fact]
public void ApplyReferrerChanges_ShouldNotDeleteReferrersWhenNoUpdateRequired()
{
var oldDescriptor1 = RandomDescriptor();
var oldDescriptor2 = RandomDescriptor();
var oldDescriptor3 = RandomDescriptor();

var oldReferrers = new List<Descriptor>
{
oldDescriptor1,
oldDescriptor2,
};

var expectedReferrers = new List<Descriptor>
{
oldDescriptor1,
oldDescriptor2,
};
var referrerChange = new Referrers.ReferrerChange(
oldDescriptor3,
Referrers.ReferrerOperation.ReferrerDelete
);

var (updatedReferrers, updateRequired) = Referrers.ApplyReferrerChanges(oldReferrers, referrerChange);
Assert.Equal(2, updatedReferrers.Count);
for (var i = 0; i < updatedReferrers.Count; ++i)
{
Assert.True(AreDescriptorsEqual(updatedReferrers[i], expectedReferrers[i]));
}
Assert.False(updateRequired);
}

[Fact]
public void ApplyReferrerChanges_ShouldDiscardDuplicateReferrers()
{
@@ -146,7 +248,7 @@
Referrers.ReferrerOperation.ReferrerAdd
);

var (updatedReferrers, updateRequired) = Referrers.ApplyReferrerChanges(oldReferrers, referrerChange);

Check warning on line 251 in tests/OrasProject.Oras.Tests/Remote/ReferrersTest.cs

GitHub Actions / Analyze (8.0.x)

Argument of type 'List<Descriptor?>' cannot be used for parameter 'oldReferrers' of type 'IList<Descriptor>' in '(IList<Descriptor>, bool) Referrers.ApplyReferrerChanges(IList<Descriptor> oldReferrers, ReferrerChange referrerChange)' due to differences in the nullability of reference types.

Check warning on line 251 in tests/OrasProject.Oras.Tests/Remote/ReferrersTest.cs

GitHub Actions / build (8.0.x)

Argument of type 'List<Descriptor?>' cannot be used for parameter 'oldReferrers' of type 'IList<Descriptor>' in '(IList<Descriptor>, bool) Referrers.ApplyReferrerChanges(IList<Descriptor> oldReferrers, ReferrerChange referrerChange)' due to differences in the nullability of reference types.
Assert.Single(updatedReferrers);
for (var i = 0; i < updatedReferrers.Count; ++i)
{
@@ -156,7 +258,7 @@
}

[Fact]
public void ApplyReferrerChanges_ThrowsWhenOldAndNewReferrersAreEmpty()
public void ApplyReferrerChanges_NoUpdateWhenOldAndNewReferrersAreEmpty()
{
var oldReferrers = new List<Descriptor>();
var referrerChange = new Referrers.ReferrerChange(Descriptor.EmptyDescriptor(), Referrers.ReferrerOperation.ReferrerAdd);
@@ -165,85 +267,4 @@
Assert.Empty(updatedReferrers);
Assert.False(updateRequired);
}

[Fact]
public void RemoveEmptyDescriptors_ShouldRemoveEmptyDescriptors()
{
var randomDescriptor1 = RandomDescriptor();
var randomDescriptor2 = RandomDescriptor();
var randomDescriptor3 = RandomDescriptor();
var randomDescriptor4 = RandomDescriptor();
var descriptors = new List<Descriptor>
{
Descriptor.EmptyDescriptor(),
randomDescriptor1,
Descriptor.EmptyDescriptor(),
randomDescriptor2,
Descriptor.EmptyDescriptor(),
Descriptor.EmptyDescriptor(),
randomDescriptor3,
randomDescriptor4,
};

var expectedDescriptors = new List<Descriptor>
{
randomDescriptor1,
randomDescriptor2,
randomDescriptor3,
randomDescriptor4
};
Referrers.RemoveEmptyDescriptors(descriptors, 4);

Assert.Equal(4, descriptors.Count);
Assert.DoesNotContain(Descriptor.EmptyDescriptor(), descriptors);
for (var i = 0; i < descriptors.Count; ++i)
{
Assert.True(AreDescriptorsEqual(descriptors[i], expectedDescriptors[i]));
}
}

[Fact]
public void RemoveEmptyDescriptors_ShouldReturnAllNonEmptyDescriptors()
{
var randomDescriptor1 = RandomDescriptor();
var randomDescriptor2 = RandomDescriptor();
var randomDescriptor3 = RandomDescriptor();
var randomDescriptor4 = RandomDescriptor();
var descriptors = new List<Descriptor>
{
randomDescriptor1,
randomDescriptor2,
randomDescriptor3,
randomDescriptor4,
};

var expectedDescriptors = new List<Descriptor>
{
randomDescriptor1,
randomDescriptor2,
randomDescriptor3,
randomDescriptor4
};
Referrers.RemoveEmptyDescriptors(descriptors, 4);
Assert.Equal(4, descriptors.Count);
for (var i = 0; i < descriptors.Count; ++i)
{
Assert.True(AreDescriptorsEqual(descriptors[i], expectedDescriptors[i]));
}
}

[Fact]
public void RemoveEmptyDescriptors_ShouldRemoveAllEmptyDescriptors()
{
var descriptors = new List<Descriptor>
{
Descriptor.EmptyDescriptor(),
Descriptor.EmptyDescriptor(),
Descriptor.EmptyDescriptor(),
Descriptor.EmptyDescriptor(),
};

Referrers.RemoveEmptyDescriptors(descriptors, 0);
Assert.Empty(descriptors);
}
}

Unchanged files with check annotations Beta

public class BlobStore(Repository repository) : IBlobStore, IMounter
{
public Repository Repository { get; init; } = repository;

Check warning on line 30 in src/OrasProject.Oras/Registry/Remote/BlobStore.cs

GitHub Actions / Analyze (8.0.x)

Parameter 'Repository repository' is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event.

Check warning on line 30 in src/OrasProject.Oras/Registry/Remote/BlobStore.cs

GitHub Actions / build (8.0.x)

Parameter 'Repository repository' is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event.
public async Task<Stream> FetchAsync(Descriptor target, CancellationToken cancellationToken = default)
{
public class ManifestStore(Repository repository) : IManifestStore
{
public Repository Repository { get; init; } = repository;

Check warning on line 31 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / Analyze (8.0.x)

Parameter 'Repository repository' is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event.

Check warning on line 31 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / build (8.0.x)

Parameter 'Repository repository' is captured into the state of the enclosing type and its value is also used to initialize a field, property, or event.
/// <summary>
/// Fetches the content identified by the descriptor.
return;
}
var contentBytes = await content.ReadAllAsync(expected, cancellationToken);

Check warning on line 187 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / Analyze (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 187 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / build (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)
using (var contentDuplicate = new MemoryStream(contentBytes))
{
// Push the manifest when ReferrerState is Unknown or NotSupported
// 1. Index the referrers list using referrers tag schema when manifest contains a subject field
// And the ReferrerState is not supported
// 2. Or do nothing when the manifest does not contain a subject field when ReferrerState is not supported/unknown
await ProcessReferrersAndPushIndex(expected, contentDuplicate, cancellationToken);

Check warning on line 205 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / Analyze (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 205 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / build (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)
}
break;
default:
await DoPushAsync(expected, content, reference, cancellationToken);

Check warning on line 209 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / Analyze (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 209 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / build (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)
break;
}
}
desc.Annotations = imageManifest.Annotations;
break;
default:
return;

Check warning on line 244 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

Codecov / codecov/patch

src/OrasProject.Oras/Registry/Remote/ManifestStore.cs#L244

Added line #L244 was not covered by tests
}
Repository.SetReferrersState(Referrers.ReferrersState.ReferrersNotSupported);
await UpdateReferrersIndex(subject, new Referrers.ReferrerChange(desc, Referrers.ReferrerOperation.ReferrerAdd), cancellationToken);

Check warning on line 248 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / Analyze (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 248 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / build (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)
}
/// <summary>
{
// 1. pull the original referrers index list using referrers tag schema
var referrersTag = Referrers.BuildReferrersTag(subject);
var (oldDesc, oldReferrers) = await PullReferrersIndexList(referrersTag, cancellationToken);

Check warning on line 268 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / Analyze (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 268 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / build (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)
// 2. apply the referrer change to referrers list
var (updatedReferrers, updateRequired) =
{
try
{
var (desc, content) = await FetchAsync(referrersTag, cancellationToken);

Check warning on line 313 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / Analyze (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 313 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

GitHub Actions / build (8.0.x)

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)
var index = JsonSerializer.Deserialize<Index>(content);
if (index == null)
{
throw new JsonException("null index manifests list");

Check warning on line 317 in src/OrasProject.Oras/Registry/Remote/ManifestStore.cs

Codecov / codecov/patch

src/OrasProject.Oras/Registry/Remote/ManifestStore.cs#L316-L317

Added lines #L316 - L317 were not covered by tests
}
return (desc, index.Manifests);
}
byte[]? receivedManifestContent = null;
byte[]? receivedIndexContent = null;
var referrersTag = Referrers.BuildReferrersTag(firstExpectedManifest.Subject);

Check warning on line 309 in tests/OrasProject.Oras.Tests/Remote/ManifestStoreTest.cs

GitHub Actions / Analyze (8.0.x)

Possible null reference argument for parameter 'descriptor' in 'string Referrers.BuildReferrersTag(Descriptor descriptor)'.

Check warning on line 309 in tests/OrasProject.Oras.Tests/Remote/ManifestStoreTest.cs

GitHub Actions / build (8.0.x)

Possible null reference argument for parameter 'descriptor' in 'string Referrers.BuildReferrersTag(Descriptor descriptor)'.
var oldIndexDeleted = false;
var firstIndexDeleted = false;
var mockHttpRequestHandler = async (HttpRequestMessage req, CancellationToken cancellationToken) =>
internal Referrers.ReferrersState ReferrersState
{
get => (Referrers.ReferrersState) _referrersState;
private set => _referrersState = (int) value;

Check warning on line 55 in src/OrasProject.Oras/Registry/Remote/Repository.cs

Codecov / codecov/patch

src/OrasProject.Oras/Registry/Remote/Repository.cs#L55

Added line #L55 was not covered by tests
}
internal static readonly string[] DefaultManifestMediaTypes =