M10: provenance populate + re-capture from source (bank-only)
Pure provenance core (recipe fingerprint, FX-chain identity, parent detection) + shell reads; capture stamps provenance on resample-from-sample; re-capture regenerates a provenanced sample from its source, never touching the timeline. Adds BankIndex/BankBook in-place update. CTest-covered.
This commit is contained in:
@@ -164,6 +164,46 @@ static void testRelativePathInvariant() {
|
||||
CHECK(idx.add(noId) == AddResult::RejectedEmptyId);
|
||||
}
|
||||
|
||||
// M10: updateInPlace refreshes an entry while preserving its slot + identity, and
|
||||
// bypasses dedup (an in-place refresh is not a new insert). The relative-paths-only
|
||||
// invariant still guards the replacement.
|
||||
static void testUpdateInPlace() {
|
||||
BankIndex idx;
|
||||
CHECK(idx.add(minimalSample("a")) == AddResult::Added); // id "min-a"
|
||||
CHECK(idx.add(minimalSample("b")) == AddResult::Added); // id "min-b"
|
||||
CHECK(idx.add(minimalSample("c")) == AddResult::Added); // id "min-c"
|
||||
|
||||
// Refresh the MIDDLE entry: new file/hash/name, same id — position must be kept.
|
||||
Sample updated = minimalSample("b");
|
||||
updated.relativePath = "reasampler_bank/regenerated.wav";
|
||||
updated.contentHash = "new-hash-b";
|
||||
updated.displayName = "regenerated";
|
||||
CHECK(idx.updateInPlace("min-b", updated));
|
||||
|
||||
CHECK(idx.size() == 3); // no new entry, no removal
|
||||
CHECK(idx.all()[1].id == "min-b"); // slot preserved (still middle)
|
||||
CHECK(idx.all()[1].relativePath == "reasampler_bank/regenerated.wav");
|
||||
CHECK(idx.all()[1].contentHash == "new-hash-b");
|
||||
CHECK(idx.query("min-b")->displayName == "regenerated");
|
||||
|
||||
// An updated hash colliding with ANOTHER entry does NOT collapse (updateInPlace
|
||||
// is not an insert): the refreshed entry keeps its slot even sharing a hash.
|
||||
Sample collide = minimalSample("b");
|
||||
collide.contentHash = idx.query("min-a")->contentHash; // same as entry "min-a"
|
||||
CHECK(idx.updateInPlace("min-b", collide));
|
||||
CHECK(idx.size() == 3); // still three; no collapse
|
||||
|
||||
// Updating an absent id fails without mutation.
|
||||
CHECK(!idx.updateInPlace("nope", minimalSample("x")));
|
||||
CHECK(idx.size() == 3);
|
||||
|
||||
// An absolute replacement path is rejected (invariant preserved).
|
||||
Sample bad = minimalSample("b");
|
||||
bad.relativePath = "C:/evil.wav";
|
||||
CHECK(!idx.updateInPlace("min-b", bad));
|
||||
CHECK(idx.query("min-b")->relativePath != "C:/evil.wav");
|
||||
}
|
||||
|
||||
static void testEmptyIndexRoundTrip() {
|
||||
BankIndex idx;
|
||||
CHECK(idx.empty());
|
||||
@@ -375,6 +415,7 @@ int main() {
|
||||
testDedupByHash();
|
||||
testTierFilterAndMove();
|
||||
testRelativePathInvariant();
|
||||
testUpdateInPlace();
|
||||
testEmptyIndexRoundTrip();
|
||||
testMalformedJson();
|
||||
testRemoveAndQuery();
|
||||
|
||||
Reference in New Issue
Block a user