| 1 | package control |
| 2 | |
| 3 | import ( |
| 4 | "path/filepath" |
| 5 | "strings" |
| 6 | "testing" |
| 7 | |
| 8 | "reasonix/internal/event" |
| 9 | "reasonix/internal/sessioninbox" |
| 10 | ) |
| 11 | |
| 12 | func TestInboxQueueFullBodySavePreservesEnvelopeAndSession(t *testing.T) { |
| 13 | dir := t.TempDir() |
| 14 | path := filepath.Join(dir, "first.jsonl") |
| 15 | c := newOwnedTestController(t, Options{SessionDir: dir, SessionPath: path, Sink: event.Discard}) |
| 16 | defer c.Close() |
| 17 | if err := c.SetInboxPaused(true); err != nil { |
| 18 | t.Fatal(err) |
| 19 | } |
| 20 | body := strings.Repeat("正文", 150) + "\n尾部" |
| 21 | receipt, err := c.EnqueueInbox(InboxRequest{Submit: body, Idempotency: "original"}) |
| 22 | if err != nil { |
| 23 | t.Fatal(err) |
| 24 | } |
| 25 | st, _ := c.ensureInbox() |
| 26 | meta, env, _ := st.ReadItem(receipt.ItemID) |
| 27 | env.FrozenRefBlock = "frozen reference bytes" |
| 28 | env.Extra = map[string]string{"owned": "keep"} |
| 29 | env.Attachments = []string{"report.txt"} |
| 30 | if _, err := st.UpdateItemIfVersion(meta.ID, env, sessioninbox.ContentVersion(meta)); err != nil { |
| 31 | t.Fatal(err) |
| 32 | } |
| 33 | read, err := c.InboxQueue(path, InboxQueueRequest{Kind: "read", ItemID: meta.ID}) |
| 34 | if err != nil || read.Edit == nil || read.Edit.Text != body { |
| 35 | t.Fatalf("full read: %+v %v", read, err) |
| 36 | } |
| 37 | newBody := " " + body + "\n修改 " |
| 38 | result, err := c.InboxQueue(path, InboxQueueRequest{Kind: "edit", ItemID: meta.ID, ContentVersion: read.Edit.ContentVersion, Text: newBody}) |
| 39 | if err != nil || result.Outcome != "applied" { |
| 40 | t.Fatalf("save: %+v %v", result, err) |
| 41 | } |
| 42 | _, saved, _ := st.ReadItem(meta.ID) |
| 43 | if saved.SubmitText != newBody || saved.FrozenRefBlock != env.FrozenRefBlock || saved.Extra["owned"] != "keep" || len(saved.Attachments) != 1 || saved.Idempotency != "original" { |
| 44 | t.Fatalf("lost envelope: %+v", saved) |
| 45 | } |
| 46 | if !result.Snapshot.Paused || result.Snapshot.Items[0].ID != meta.ID { |
| 47 | t.Fatal("save changed queue policy") |
| 48 | } |
| 49 | conflict, _ := c.InboxQueue(path, InboxQueueRequest{Kind: "edit", ItemID: meta.ID, ContentVersion: read.Edit.ContentVersion, Text: "stale"}) |
| 50 | if conflict.Reason != "content_changed" { |
| 51 | t.Fatal(conflict) |
| 52 | } |
| 53 | c.SetSessionPath(filepath.Join(dir, "second.jsonl")) |
| 54 | stale, err := c.InboxQueue(path, InboxQueueRequest{Kind: "pause", Paused: true}) |
| 55 | if err != nil || stale.Reason != "session_changed" || stale.Snapshot.SessionPath != "" { |
| 56 | t.Fatalf("wrong-session result: %+v %v", stale, err) |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | func TestInboxQueueDispatchExecutesSavedTextInMovedOrder(t *testing.T) { |
| 61 | c, runner, done := newInboxDispatchController(t) |
| 62 | if err := c.SetInboxPaused(true); err != nil { |
| 63 | t.Fatal(err) |
| 64 | } |
| 65 | a, err := c.EnqueueInbox(InboxRequest{Submit: "first"}) |
| 66 | if err != nil { |
| 67 | t.Fatal(err) |
| 68 | } |
| 69 | b, err := c.EnqueueInbox(InboxRequest{Submit: "second"}) |
| 70 | if err != nil { |
| 71 | t.Fatal(err) |
| 72 | } |
| 73 | path := c.SessionPath() |
| 74 | read, _ := c.InboxQueue(path, InboxQueueRequest{Kind: "read", ItemID: b.ItemID}) |
| 75 | saved, err := c.InboxQueue(path, InboxQueueRequest{Kind: "edit", ItemID: b.ItemID, Text: "second edited", ContentVersion: read.Edit.ContentVersion}) |
| 76 | if err != nil || saved.Outcome != "applied" { |
| 77 | t.Fatalf("save: %+v %v", saved, err) |
| 78 | } |
| 79 | moved, err := c.InboxQueue(path, InboxQueueRequest{Kind: "move", ItemID: b.ItemID, BeforeItemID: &a.ItemID, QueueRevision: saved.Snapshot.Revision}) |
| 80 | if err != nil || moved.Outcome != "applied" { |
| 81 | t.Fatalf("move: %+v %v", moved, err) |
| 82 | } |
| 83 | if err := c.SetInboxPaused(false); err != nil { |
| 84 | t.Fatal(err) |
| 85 | } |
| 86 | if got := waitForInboxDispatch(t, c, runner); got != "second edited" { |
| 87 | t.Fatalf("first dispatched %q", got) |
| 88 | } |
| 89 | waitForInboxTurnDone(t, c, done) |
| 90 | if got := waitForInboxDispatch(t, c, runner); got != "first" { |
| 91 | t.Fatalf("second dispatched %q", got) |
| 92 | } |
| 93 | waitForInboxTurnDone(t, c, done) |
| 94 | } |
| 95 |