Opening a template stored database IDs without advancing nextSnapID, so the next temporary snapshot could overwrite and then delete the real baseline. Adopt helpers now bump every counter and only drop a map entry when it still points at the same object. Co-authored-by: Cursor <cursoragent@cursor.com>
71 lines
2.1 KiB
Go
71 lines
2.1 KiB
Go
package docrevision
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNextFreeID_SkipsOccupied(t *testing.T) {
|
|
var counter int64 = 248
|
|
occupied := map[int64]bool{249: true}
|
|
got := nextFreeID(&counter, func(id int64) bool { return occupied[id] })
|
|
if got != 250 {
|
|
t.Fatalf("next free id=%d want 250", got)
|
|
}
|
|
}
|
|
|
|
func TestAdoptSnapshotID_DoesNotDeleteOtherObject(t *testing.T) {
|
|
s := NewService(nil)
|
|
baseline := &Snapshot{ID: 249, DocID: "doc_a", ContentSHA256: "aa", CreatedAt: time.Now().UTC()}
|
|
temp := &Snapshot{ID: 249, DocID: "doc_b", ContentSHA256: "bb", CreatedAt: time.Now().UTC()}
|
|
s.snapshots[249] = baseline
|
|
s.nextSnapID = 248
|
|
|
|
s.adoptSnapshotID(temp, 250)
|
|
if s.snapshots[249] != baseline {
|
|
t.Fatal("adopting a different object must not delete the real baseline")
|
|
}
|
|
if s.snapshots[250] != temp || temp.ID != 250 {
|
|
t.Fatalf("temp must live at 250, id=%d", temp.ID)
|
|
}
|
|
if s.nextSnapID != 250 {
|
|
t.Fatalf("counter=%d want 250", s.nextSnapID)
|
|
}
|
|
}
|
|
|
|
func TestAdoptSnapshotID_RemovesOnlySameObject(t *testing.T) {
|
|
s := NewService(nil)
|
|
snap := &Snapshot{ID: 1, DocID: "doc_a", ContentSHA256: "aa"}
|
|
s.snapshots[1] = snap
|
|
s.adoptSnapshotID(snap, 249)
|
|
if _, ok := s.snapshots[1]; ok {
|
|
t.Fatal("same-object remap must drop the old temp key")
|
|
}
|
|
if s.snapshots[249] != snap || snap.ID != 249 {
|
|
t.Fatal("same object must occupy the database id")
|
|
}
|
|
if s.nextSnapID < 249 {
|
|
t.Fatalf("counter must advance to db id, got %d", s.nextSnapID)
|
|
}
|
|
}
|
|
|
|
func TestPutSnapshotLocked_DoesNotClobberExistingBaseline(t *testing.T) {
|
|
s := NewService(nil)
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
baseline := &Snapshot{ID: 249, DocID: "doc_seed", ContentSHA256: "base"}
|
|
s.snapshots[249] = baseline
|
|
s.nextSnapID = 248
|
|
s.blobs["out"] = &ContentBlob{ContentSHA256: "out", ContentMarkdown: "# out\n"}
|
|
got := s.putSnapshotLocked("doc_new", "# out\n", "out", nil, SourceAgent, nil, "run", ActorSnapshot{}, "run", nil)
|
|
if got.ID == 249 {
|
|
t.Fatal("temporary snapshot must not reuse the occupied baseline id")
|
|
}
|
|
if s.snapshots[249] != baseline {
|
|
t.Fatal("baseline snapshot must remain")
|
|
}
|
|
if s.snapshots[got.ID] != got {
|
|
t.Fatal("new snapshot must be stored under its own id")
|
|
}
|
|
}
|