Add canonicalize, stable block map, diff, risk floor, memory ledger service, contract fixtures, concurrency tests, and backfill CLI. Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
2.8 KiB
Go
101 lines
2.8 KiB
Go
package docrevision
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
func TestConcurrentHumanRevisions(t *testing.T) {
|
|
svc := NewService(nil)
|
|
ctx := context.Background()
|
|
st, err := svc.OpenTemplateVersion(ctx, "doc_conc", "# base\n", ActorSnapshot{}, 1, "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var wg sync.WaitGroup
|
|
errs := make(chan error, 50)
|
|
for i := 0; i < 50; i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
md := fmt.Sprintf("# base\n\nedit %d\n", i)
|
|
// Only first expected head will succeed; others conflict — that's OK.
|
|
_, err := svc.CommitHumanRevision(ctx, HumanRevisionRequest{
|
|
DocID: "doc_conc", Markdown: md,
|
|
ExpectedHeadSnapshot: &st.HeadSnapshotID, ExpectedRevisionNo: &st.HeadRevisionNo,
|
|
IdempotencyKey: fmt.Sprintf("conc-%d", i), ActorUserID: int64(i + 1), LifecycleState: "drafting",
|
|
})
|
|
if err != nil && err != ErrHeadConflict {
|
|
errs <- err
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
close(errs)
|
|
for err := range errs {
|
|
t.Fatal(err)
|
|
}
|
|
// Sequential chain should produce continuous revision numbers
|
|
svc2 := NewService(nil)
|
|
st2, _ := svc2.OpenTemplateVersion(ctx, "doc_seq", "# s\n", ActorSnapshot{}, 1, "")
|
|
head := st2.HeadSnapshotID
|
|
rev := st2.HeadRevisionNo
|
|
for i := 0; i < 20; i++ {
|
|
r, err := svc2.CommitHumanRevision(ctx, HumanRevisionRequest{
|
|
DocID: "doc_seq", Markdown: fmt.Sprintf("# s\n\n%d\n", i),
|
|
ExpectedHeadSnapshot: &head, ExpectedRevisionNo: &rev,
|
|
IdempotencyKey: fmt.Sprintf("seq-%d", i), ActorUserID: 1, LifecycleState: "drafting",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if r.State.HeadRevisionNo != i+1 {
|
|
t.Fatalf("revision_no want %d got %d", i+1, r.State.HeadRevisionNo)
|
|
}
|
|
head = r.State.HeadSnapshotID
|
|
rev = r.State.HeadRevisionNo
|
|
}
|
|
vers, _ := svc2.ListVersions(ctx, "doc_seq")
|
|
open := 0
|
|
for _, v := range vers {
|
|
if v.Status == VersionOpen {
|
|
open++
|
|
}
|
|
}
|
|
if open != 1 {
|
|
t.Fatalf("want exactly one open version, got %d", open)
|
|
}
|
|
}
|
|
|
|
func TestFreeformLegacyImport(t *testing.T) {
|
|
svc := NewService(nil)
|
|
ctx := context.Background()
|
|
st, err := svc.ImportFreeformLegacy(ctx, "ff1", 9, 1, []struct {
|
|
VersionNo int
|
|
Markdown string
|
|
SHA string
|
|
}{
|
|
{VersionNo: 1, Markdown: "# v1\n"},
|
|
{VersionNo: 2, Markdown: "# v2\n"},
|
|
}, "# v3 head\n")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st.CurrentVersionNo != 3 {
|
|
t.Fatalf("want version 3 open, got %d", st.CurrentVersionNo)
|
|
}
|
|
r, err := svc.CommitHumanRevision(ctx, HumanRevisionRequest{
|
|
DocID: "ff1", Markdown: "# v3 head\n\nedit\n",
|
|
ExpectedHeadSnapshot: &st.HeadSnapshotID, ExpectedRevisionNo: &st.HeadRevisionNo,
|
|
IdempotencyKey: "ff-e1", ActorUserID: 9, LifecycleState: "drafting",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if r.State.CurrentVersionNo != 3 || r.State.HeadRevisionNo != 1 {
|
|
t.Fatalf("human save should only bump revision: %+v", r.State)
|
|
}
|
|
}
|