diff --git a/src/NATS.Client.ObjectStore/Models/ObjectMetadata.cs b/src/NATS.Client.ObjectStore/Models/ObjectMetadata.cs index 4a3e94259..92b199250 100644 --- a/src/NATS.Client.ObjectStore/Models/ObjectMetadata.cs +++ b/src/NATS.Client.ObjectStore/Models/ObjectMetadata.cs @@ -35,11 +35,12 @@ public record ObjectMetadata public string? Nuid { get; set; } /// - /// Max chunk size + /// The size of the object in bytes. + /// It only includes the size of the object itself, not the metadata. /// [JsonPropertyName("size")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public int Size { get; set; } + public ulong Size { get; set; } /// /// Modified timestamp @@ -53,7 +54,7 @@ public record ObjectMetadata /// [JsonPropertyName("chunks")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] - public int Chunks { get; set; } + public uint Chunks { get; set; } /// /// Object digest diff --git a/src/NATS.Client.ObjectStore/NatsObjStore.cs b/src/NATS.Client.ObjectStore/NatsObjStore.cs index 759d0f0b1..befdca53c 100644 --- a/src/NATS.Client.ObjectStore/NatsObjStore.cs +++ b/src/NATS.Client.ObjectStore/NatsObjStore.cs @@ -83,7 +83,7 @@ public async ValueTask GetAsync(string key, Stream stream, bool string digest; var chunks = 0; - var size = 0; + ulong size = 0; using (var sha256 = SHA256.Create()) { #if NETSTANDARD2_0 @@ -107,7 +107,7 @@ public async ValueTask GetAsync(string key, Stream stream, bool { using var memoryOwner = msg.Data; chunks++; - size += memoryOwner.Memory.Length; + size += (ulong)memoryOwner.Memory.Length; #if NETSTANDARD2_0 var segment = memoryOwner.DangerousGetArray(); await hashedStream.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken); @@ -182,8 +182,8 @@ public async ValueTask PutAsync(ObjectMetadata meta, Stream stre meta.Options.MaxChunkSize = DefaultChunkSize; } - var size = 0; - var chunks = 0; + ulong size = 0; + uint chunks = 0; var chunkSize = meta.Options.MaxChunkSize.Value; string digest; @@ -250,7 +250,7 @@ public async ValueTask PutAsync(ObjectMetadata meta, Stream stre if (currentChunkSize > 0) { - size += currentChunkSize; + size += (ulong)currentChunkSize; chunks++; } diff --git a/tests/NATS.Client.ObjectStore.Tests/ObjectStoreTest.cs b/tests/NATS.Client.ObjectStore.Tests/ObjectStoreTest.cs index acbe86ecf..6fe6634b7 100644 --- a/tests/NATS.Client.ObjectStore.Tests/ObjectStoreTest.cs +++ b/tests/NATS.Client.ObjectStore.Tests/ObjectStoreTest.cs @@ -82,7 +82,7 @@ public async Task Put_chunks() Assert.Equal($"SHA-256={sha}", data.Digest); Assert.Equal(chunks, data.Chunks); - Assert.Equal(size, data.Size); + Assert.Equal((ulong)size, data.Size); } // buffer with smaller last chunk @@ -102,7 +102,7 @@ public async Task Put_chunks() Assert.Equal($"SHA-256={sha}", data.Digest); Assert.Equal(chunks, data.Chunks); - Assert.Equal(size, data.Size); + Assert.Equal((ulong)size, data.Size); } // Object name checks @@ -195,7 +195,7 @@ public async Task Delete_object() await store.PutAsync("k1", new byte[] { 65, 66, 67 }, cancellationToken); var info = await store.GetInfoAsync("k1", cancellationToken: cancellationToken); - Assert.Equal(3, info.Size); + Assert.Equal(3UL, info.Size); var bytes = await store.GetBytesAsync("k1", cancellationToken); Assert.Equal(bytes, new byte[] { 65, 66, 67 }); @@ -207,8 +207,8 @@ public async Task Delete_object() var info2 = await store.GetInfoAsync("k1", showDeleted: true, cancellationToken: cancellationToken); Assert.True(info2.Deleted); - Assert.Equal(0, info2.Size); - Assert.Equal(0, info2.Chunks); + Assert.Equal(0UL, info2.Size); + Assert.Equal(0U, info2.Chunks); Assert.Equal(string.Empty, info2.Digest); // Put again @@ -622,4 +622,45 @@ await Assert.ThrowsAsync(async () => await b1.GetBytesAsync("name1", cancellationToken: cancellationToken); }); } + + [Fact] + public async Task Metadata_field_types_match_spec() + { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var cancellationToken = cts.Token; + + await using var server = await NatsServerProcess.StartAsync(); + await using var nats = new NatsConnection(new NatsOpts { Url = server.Url }); + var js = new NatsJSContext(nats); + var obj = new NatsObjContext(js); + + var store = await obj.CreateObjectStoreAsync(new NatsObjConfig("b1"), cancellationToken); + + // Test that Size property is ulong (uint64 in NATS spec) + var metadata = new ObjectMetadata { Name = "test" }; + metadata.Size = ulong.MaxValue; // Should compile without error + Assert.Equal(18446744073709551615UL, metadata.Size); + + // Test that Chunks property is uint (uint32 in NATS spec) + metadata.Chunks = uint.MaxValue; // Should compile without error + Assert.Equal(4294967295U, metadata.Chunks); + + // Test with actual object metadata from store + await store.PutAsync("k1", new byte[] { 1, 2, 3 }, cancellationToken: cancellationToken); + var info = await store.GetInfoAsync("k1", cancellationToken: cancellationToken); + + // Verify Size is ulong +#pragma warning disable IDE0007 + // ReSharper disable once SuggestVarOrType_BuiltInTypes + ulong size = info.Size; // Should compile without error +#pragma warning restore IDE0007 + Assert.Equal(3UL, size); + + // Verify Chunks is uint +#pragma warning disable IDE0007 + // ReSharper disable once SuggestVarOrType_BuiltInTypes + uint chunks = info.Chunks; // Should compile without error +#pragma warning restore IDE0007 + Assert.Equal(1U, chunks); + } } diff --git a/tests/NATS.Client.ObjectStore.Tests/WatcherTest.cs b/tests/NATS.Client.ObjectStore.Tests/WatcherTest.cs index ff4268c53..73fa43f0f 100644 --- a/tests/NATS.Client.ObjectStore.Tests/WatcherTest.cs +++ b/tests/NATS.Client.ObjectStore.Tests/WatcherTest.cs @@ -30,7 +30,7 @@ public async Task Watcher_test() { count++; signal.Pulse(); - Assert.Equal(count, info.Size); + Assert.Equal((ulong)count, info.Size); if (count == 3) break; }