Walk rows lows: Rust @directory error kind and delete, stale known-gaps (#116)
This commit is contained in:
parent
af9142539e
commit
19b95ba2ab
4 changed files with 16 additions and 4 deletions
|
|
@ -1609,7 +1609,7 @@ The one door per runtime, and who walks through it:
|
|||
- **Shared paths.** cultnet-ts reaches the header decision only through `readSingleFileStore` (AUTH:4, :321) and checks only record count, schema id and type. cultnet-ts therefore needs a cultcache-ts release that exports it, which is the release that ships with this change; the manifests move to that version then (c0-ships-with-c2a), not before.
|
||||
- **Cut line.** `merge-r1` h3 (Rust) and h4 (TS, cultnet-ts); spec `cut-merge-r1.r3` authority map.
|
||||
- **Pinned by.** `tests/vectors/document-variants-c2a/readability/manifest.txt`, walked by `StoreReadabilityTests.OpenReadsExactlyTheFilesTheVectorsSayItReads` (C#), `CultMeshSingleFileReadabilityTests.AWriteReplacesAFileExactlyWhenItReads` (Mesh), `a_file_is_replaced_exactly_when_it_reads` (RS:5132), "SingleFileMessagePackBackingStore replaces a file exactly when it reads" (TS test :2321) and `test_single_file_replaces_a_file_exactly_when_it_reads` (Python). AUTH cases: `packages/cultnet-ts/test/idunn-runtime-authority.test.ts`. Declared divergences in the manifest: `legacy-envelopes.bin` (C# refuses; the others read and replace it) and `variant-slot-v3.bin` (C# reads; the others refuse).
|
||||
- **Known gaps.** `variants:follow_up:c0-bad-canonical-in-manifest` (`current-catalog-bad-canonical.msgpack` is not in the manifest; TS reads it, Rust refuses). `variants:follow_up:legacy-vectors-all-runtimes` (the three `legacy-catalog-*` c0 vectors have a verdict only in C# tests; all four runtimes refuse them, probed). `variants:follow_up:range-check-in-ci` (`npm run test:pack:ts` builds every admitted tag and checks exports, but `.github/workflows/publish-packages.yml` does not run it).
|
||||
- **Known gaps.** `variants:follow_up:range-check-in-ci` (`npm run test:pack:ts` builds every admitted tag and checks exports, but `.github/workflows/publish-packages.yml` does not run it).
|
||||
|
||||
### B. Existence and links (what "nothing at the path" means)
|
||||
|
||||
|
|
@ -1640,7 +1640,7 @@ The one door per runtime, and who walks through it:
|
|||
- **Shared paths.** Open, commit, Mesh read and write.
|
||||
- **Cut line.** `mesh-single-file-reader` h1 (the C# clause). Rust, TS and Python were correct by construction of their reads.
|
||||
- **Pinned by.** C#: `ADirectoryAtTheStorePathIsAnIoErrorOnOpenAndOnCommitNotAnEmptyStore` (`StoreReadabilityTests.cs:236`). Mesh: `ADirectoryAtThePathIsAnIoErrorOnReadAndWriteAndIsLeftAlone` (:149). Rust: `a_write_over_a_directory_stops_at_the_read_and_leaves_no_staging_file` RS:5726.
|
||||
- **Known gaps.** TS and Python have no test for a directory at the store path. No follow-up is registered; the natural home is `write-set-vectors` with the other readability pins.
|
||||
- **Known gaps.** None open. The `@directory` manifest row is walked by all four runtimes: every operation including delete is refused and leaves the directory untouched; Rust pins an I/O failure rather than a typed unreadable store.
|
||||
|
||||
### E. Lock
|
||||
|
||||
|
|
|
|||
|
|
@ -6148,7 +6148,12 @@ mod tests {
|
|||
let bytes = if directory { Vec::new() } else { std::fs::read(root.join(vector))? };
|
||||
// A v3 store keeps its marker through a rewrite; every other file that reads is written v1.
|
||||
let header = if vector.contains("v3") { "cultcache.store.v3" } else { STORE_FORMAT_V1 };
|
||||
for operation in ["open", "soft_push_all", "push_all", "push"] {
|
||||
let operations: &[&str] = if directory {
|
||||
&["open", "soft_push_all", "push_all", "push", "delete"]
|
||||
} else {
|
||||
&["open", "soft_push_all", "push_all", "push"]
|
||||
};
|
||||
for &operation in operations {
|
||||
let temp = tempfile::tempdir()?;
|
||||
let path = temp.path().join("store.msgpack");
|
||||
if directory {
|
||||
|
|
@ -6161,11 +6166,15 @@ mod tests {
|
|||
"open" => store.pull_all().map(|_| ()),
|
||||
"soft_push_all" => store.push_all(&[snapshot_envelope("x", b"one")], PushAllOptions { soft: true }),
|
||||
"push_all" => store.push_all(&[snapshot_envelope("x", b"one")], PushAllOptions::default()),
|
||||
"delete" => store.delete(&snapshot_envelope("x", b"one")),
|
||||
_ => store.push(&snapshot_envelope("x", b"one")),
|
||||
};
|
||||
let what = format!("{vector} {operation}");
|
||||
assert_eq!(outcome.is_ok(), reads, "{what}: {outcome:?}");
|
||||
if directory {
|
||||
// A directory is an I/O failure, never a typed unreadable store.
|
||||
let error = outcome.unwrap_err();
|
||||
assert!(io_error_in(&error).is_some() && unreadable_kind(&error).is_none(), "{what}: {error:#}");
|
||||
assert!(path.is_dir() && std::fs::read_dir(&path)?.next().is_none(), "{what} touched the directory");
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2635,7 +2635,7 @@ test("SingleFileMessagePackBackingStore replaces a file exactly when it reads",
|
|||
assert.ok(!keys.includes(envelopes[0]!.key), `${vector} ${operation}`);
|
||||
}
|
||||
} else if (isDirectory) {
|
||||
await assert.rejects(run, `${vector} ${operation}`);
|
||||
await assert.rejects(run, (error) => !(error instanceof StoreUnreadableError), `${vector} ${operation}: an I/O failure, not a typed refusal`);
|
||||
assert.ok((await lstat(file)).isDirectory(), `${vector} ${operation} replaced the directory`);
|
||||
assert.deepEqual(await readdir(file), [], `${vector} ${operation} wrote into the directory`);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -314,6 +314,9 @@ namespace GameCult.Caching.Tests
|
|||
Assert.That(() => cache.Commit(batch => batch.Upsert(typeof(IdDeck), new IdDeck { Name = "e" }, new CultRecordKey("e"))),
|
||||
Throws.TypeOf<IOException>().With.Message.Contains("directory"));
|
||||
|
||||
Assert.That(() => cache.Commit(batch => batch.Remove(new CultRecordKey("e"))),
|
||||
Throws.TypeOf<IOException>().With.Message.Contains("directory"));
|
||||
|
||||
Assert.That(Directory.GetFileSystemEntries(path), Is.Empty);
|
||||
Assert.That(Directory.GetFileSystemEntries(later), Is.Empty);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue