# QWP/WebSocket Java Store-and-Forward fixtures
These fixtures are committed compatibility data for the Rust QWP/WebSocket
Store-and-Forward on-disk formats: the `.sfa` segment codec and the
`.symbol-dict` side-file. Every one of them holds bytes the **Java reference
client actually wrote**, so the Rust tests that load them prove cross-client
parity rather than re-asserting this client's own round-trip. Normal Rust tests
load these hex files and do not need a Java checkout.
## `.sfa` segment fixtures
The fixtures model one Java `MmapSegment` file:
- file size: 64 bytes
- `baseSeq`: 42
- `createdMicros`: normalized to `1234567890123` after Java generation so the
fixture is deterministic
- payloads: `one`, `two-two`
- frame format: `[u32 crc32c][u32 payloadLen][payload]`
- CRC32C covers `payloadLen || payload`
Files:
- `java-two-frame.sfa.hex` - segment with two valid frames and a zero-filled
tail.
- `java-two-frame-torn-tail.sfa.hex` - same segment, but the second payload has
one byte flipped. Recovery must keep the first frame, set append offset to
`35`, and report `29` torn-tail bytes.
## `.symbol-dict` side-file fixtures
The fixtures model one Java `PersistedSymbolDict` file (34 bytes), written with
the two append shapes a Java producer uses -- `appendSymbol` for a frame that
introduces one symbol, `appendSymbols(dict, from, to)` for one that introduces
several:
- chunk 0: `["one"]`
- chunk 1: `["two", "three"]`
- layout: `[u32 magic 'SYD1'][u8 version 1][3 reserved]` then chunks of
`[entryCount varint][entryBytes varint][entries][u32 crc32c]`, where entries
are `[len varint][utf8]` and the CRC32C covers both header varints and the
entry region
- no timestamp field, and Java truncates its mmap reserve to the logical length
on close, so the bytes need no normalization
A multi-entry chunk is deliberately included: it is the case per-chunk (rather
than per-entry) checksumming exists for, and the case a single-symbol fixture
would not exercise at all.
Files:
- `java-two-chunk.symbol-dict.hex` - the two chunks above.
- `java-two-chunk-torn-tail.symbol-dict.hex` - the same bytes with offset `21`
(the `t` of `two`, inside chunk 1) flipped to `u`. Same length, so only that
chunk's stored CRC goes stale: recovery must keep chunk 0, recover `["one"]`
alone, and truncate the file back to `18` bytes.
## Proving and regenerating the fixtures
Point `QDB_JAVA_CLIENT_CORE` at the Java client's `core` directory (the one
holding `pom.xml` and `target/classes`; run `mvn -f <core>/pom.xml test-compile`
first if `target/classes` is missing). Then, to prove the fixtures against the
Java source:
```sh
QDB_JAVA_CLIENT_CORE=<core> cargo test --manifest-path questdb-rs/Cargo.toml \
java_and_rust_read_each_others_segments -- --ignored --nocapture
QDB_JAVA_CLIENT_CORE=<core> cargo test --manifest-path questdb-rs/Cargo.toml \
java_and_rust_read_each_others_symbol_dicts -- --ignored --nocapture
```
Each test compiles a small Java helper into a temp dir, drives Java's real
`MmapSegment` / `PersistedSymbolDict`, and verifies both directions:
Java-written file read by Rust, Rust-written file read by Java, and torn-tail
recovery agreement.
To print regenerated fixture hex from Java-generated bytes:
```sh
QDB_JAVA_CLIENT_CORE=<core> cargo test --manifest-path questdb-rs/Cargo.toml \
print_java_sfa_fixture_hex -- --ignored --nocapture
QDB_JAVA_CLIENT_CORE=<core> cargo test --manifest-path questdb-rs/Cargo.toml \
print_java_symbol_dict_fixture_hex -- --ignored --nocapture
```