mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-23 00:27:16 +00:00
test(oceanbase): cover LOB expiry and reject malformed chunk payloads
This commit is contained in:
+26
@@ -14,6 +14,32 @@ import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
class OceanBaseLobValuesTest {
|
||||
@Test
|
||||
void idleAndAbsoluteExpiryFreeLocatorsAndRestorePayloadCapacity() throws Exception {
|
||||
OceanBaseLobValues values = new OceanBaseLobValues((connection, locator, offset, limit) -> chunk("x".repeat(1000), offset, limit), ignored -> 40L * 1024 * 1024);
|
||||
for (String fieldName : List.of("accessed", "created")) {
|
||||
Fixture fixture = new Fixture();
|
||||
var preview = (OceanBaseLobValues.Preview) values.preview(fixture.resultSet, 1, Types.CLOB, "CLOB");
|
||||
var entriesField = OceanBaseLobValues.class.getDeclaredField("entries");
|
||||
entriesField.setAccessible(true);
|
||||
Object entry = ((java.util.Map<?, ?>) entriesField.get(values)).get(preview.ref());
|
||||
var ageField = entry.getClass().getDeclaredField(fieldName);
|
||||
ageField.setAccessible(true);
|
||||
ageField.setLong(entry, System.currentTimeMillis() - (fieldName.equals("accessed") ? 5 : 30) * 60_000L - 1);
|
||||
assertEquals("expired", values.fetch(fixture.connection, preview.ref(), 0, 1).status());
|
||||
assertTrue(fixture.freed.get());
|
||||
assertFalse(values.hasValues());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void nclobRetainsTheExistingCompleteReaderWithoutCapturingAPreview() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
OceanBaseLobValues values = new OceanBaseLobValues((connection, locator, offset, limit) -> { fail("NCLOB must use its existing complete reader"); return null; });
|
||||
assertNull(values.preview(fixture.resultSet, 1, Types.NCLOB, "NCLOB"));
|
||||
assertFalse(values.hasValues());
|
||||
assertFalse(fixture.freed.get());
|
||||
}
|
||||
@Test
|
||||
void refusesNewLocatorsWhenRetainedPayloadBudgetIsFullAndRestoresCapacityOnRelease() throws Exception {
|
||||
OceanBaseLobValues values = new OceanBaseLobValues((connection, locator, offset, limit) -> chunk("x".repeat(1000), offset, limit), ignored -> 40L * 1024 * 1024);
|
||||
|
||||
@@ -8,6 +8,12 @@ const response = (data: string, next_offset: number, eof = false) => ({ status:
|
||||
beforeEach(() => mocks.readLargeValueChunk.mockReset());
|
||||
|
||||
describe("original-result LOB snapshot consumption", () => {
|
||||
it.each([{ eof: "false" }, { value_kind: "unknown" }, { data: null }])("rejects malformed payload fields before consuming a chunk: %j", async (invalid) => {
|
||||
mocks.readLargeValueChunk.mockResolvedValue({ ...response("text", 4, true), ...invalid });
|
||||
const consume = vi.fn();
|
||||
await expect(readLargeValueSnapshot(request, () => true, consume)).rejects.toThrow("Invalid LOB chunk payload");
|
||||
expect(consume).not.toHaveBeenCalled();
|
||||
});
|
||||
it("uses server character offsets and preserves Chinese and emoji across chunks", async () => {
|
||||
mocks.readLargeValueChunk.mockResolvedValueOnce(response("中文😀", 3)).mockResolvedValueOnce(response("末尾😀", 6, true));
|
||||
expect(await materializeLargeValueSnapshot(request, () => true)).toBe("中文😀末尾😀");
|
||||
|
||||
@@ -16,6 +16,7 @@ export async function readLargeValueSnapshot(
|
||||
const chunk = await api.readLargeValueChunk({ ...request, offset, limit: 4096 });
|
||||
if (!isCurrent()) throw new Error("LOB result context changed");
|
||||
if (chunk.status !== "ok") throw new Error(`LOB snapshot ${chunk.status}; execute the query again`);
|
||||
if (typeof chunk.data !== "string" || typeof chunk.eof !== "boolean" || (chunk.value_kind !== "text" && chunk.value_kind !== "binary")) throw new Error("Invalid LOB chunk payload");
|
||||
if (!Number.isSafeInteger(chunk.next_offset) || chunk.next_offset < offset || (!chunk.eof && chunk.next_offset === offset)) {
|
||||
throw new Error("Invalid LOB chunk offset");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user