Skip to content

Commit b86d689

Browse files
dfa1claude
andcommitted
test(reader): cover DecimalEncodingDecoder (was untested)
Add DecimalEncodingDecoderTest: accepts/encodingId, every DecimalType width (1/2/4/8/16/32 bytes), unknown values_type, buffer-too-small, and the missing/empty/invalid metadata guards. 100% line + branch. values_type is encoded as an explicit field-1 varint so the proto3 default (0) is present on the wire (DecimalMetadata(0).encode() omits it → reads as empty). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 5144f94 commit b86d689

1 file changed

Lines changed: 115 additions & 0 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package io.github.dfa1.vortex.reader.decode;
2+
3+
import io.github.dfa1.vortex.core.DType;
4+
import io.github.dfa1.vortex.core.PType;
5+
import io.github.dfa1.vortex.core.VortexException;
6+
import io.github.dfa1.vortex.encoding.EncodingId;
7+
import io.github.dfa1.vortex.reader.ReadRegistry;
8+
import io.github.dfa1.vortex.reader.array.Array;
9+
import io.github.dfa1.vortex.reader.array.LazyDecimalArray;
10+
import org.junit.jupiter.api.Nested;
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.params.ParameterizedTest;
13+
import org.junit.jupiter.params.provider.CsvSource;
14+
15+
import java.lang.foreign.Arena;
16+
import java.lang.foreign.MemorySegment;
17+
import java.nio.ByteBuffer;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
21+
22+
class DecimalEncodingDecoderTest {
23+
24+
private static final DecimalEncodingDecoder SUT = new DecimalEncodingDecoder();
25+
private static final DType DECIMAL = new DType.Decimal((byte) 10, (byte) 2, false);
26+
27+
private static Array decode(int valuesType, int rowCount, int bufferBytes) {
28+
// Encode values_type explicitly (field 1, varint) so even the proto3 default (0) is
29+
// present on the wire — DecimalMetadata(0).encode() would omit it and read as empty.
30+
ByteBuffer meta = ByteBuffer.wrap(new byte[]{0x08, (byte) valuesType});
31+
return decode(meta, rowCount, bufferBytes);
32+
}
33+
34+
private static Array decode(ByteBuffer meta, int rowCount, int bufferBytes) {
35+
MemorySegment buf = MemorySegment.ofArray(new byte[bufferBytes]);
36+
ArrayNode node = ArrayNode.of(EncodingId.VORTEX_DECIMAL, meta, new ArrayNode[0], new int[]{0});
37+
DecodeContext ctx = new DecodeContext(node, DECIMAL, rowCount,
38+
new MemorySegment[]{buf}, ReadRegistry.empty(), Arena.ofAuto());
39+
return SUT.decode(ctx);
40+
}
41+
42+
@Test
43+
void acceptsDecimalRejectsOthers() {
44+
// Given / When / Then
45+
assertThat(SUT.accepts(DECIMAL)).isTrue();
46+
assertThat(SUT.accepts(new DType.Primitive(PType.I32, false))).isFalse();
47+
}
48+
49+
@Test
50+
void encodingId() {
51+
assertThat(SUT.encodingId()).isEqualTo(EncodingId.VORTEX_DECIMAL);
52+
}
53+
54+
@Nested
55+
class Decode {
56+
57+
@ParameterizedTest(name = "valuesType={0} -> {1} bytes")
58+
@CsvSource({"0,1", "1,2", "2,4", "3,8", "4,16", "5,32"})
59+
void everyDecimalTypeWidth(int valuesType, int byteWidth) {
60+
// Given — buffer sized exactly for the declared width × rows
61+
int rows = 2;
62+
63+
// When
64+
Array result = decode(valuesType, rows, rows * byteWidth);
65+
66+
// Then
67+
assertThat(result).isInstanceOf(LazyDecimalArray.class);
68+
assertThat(result.length()).isEqualTo(rows);
69+
}
70+
71+
@Test
72+
void unknownValuesType_throws() {
73+
// When / Then
74+
assertThatThrownBy(() -> decode(6, 1, 64))
75+
.isInstanceOf(VortexException.class)
76+
.hasMessageContaining("unknown DecimalType");
77+
}
78+
79+
@Test
80+
void bufferTooSmall_throws() {
81+
// Given — values_type 3 = 8 bytes/row × 2 rows = 16 needed, only 8 provided
82+
// When / Then
83+
assertThatThrownBy(() -> decode(3, 2, 8))
84+
.isInstanceOf(VortexException.class)
85+
.hasMessageContaining("buffer too small");
86+
}
87+
88+
@Test
89+
void missingMetadata_throws() {
90+
// When / Then — null metadata
91+
assertThatThrownBy(() -> decode((ByteBuffer) null, 1, 8))
92+
.isInstanceOf(VortexException.class)
93+
.hasMessageContaining("missing metadata");
94+
}
95+
96+
@Test
97+
void emptyMetadata_throws() {
98+
// When / Then — present but zero remaining
99+
assertThatThrownBy(() -> decode(ByteBuffer.allocate(0), 1, 8))
100+
.isInstanceOf(VortexException.class)
101+
.hasMessageContaining("missing metadata");
102+
}
103+
104+
@Test
105+
void invalidMetadata_throws() {
106+
// Given — a truncated varint that proto decode rejects
107+
ByteBuffer bad = ByteBuffer.wrap(new byte[]{0x08, (byte) 0x80});
108+
109+
// When / Then
110+
assertThatThrownBy(() -> decode(bad, 1, 8))
111+
.isInstanceOf(VortexException.class)
112+
.hasMessageContaining("invalid metadata");
113+
}
114+
}
115+
}

0 commit comments

Comments
 (0)