test(sheets): cover undo shortcut edge cases

This commit is contained in:
zhengzhijie
2026-07-03 14:53:22 +08:00
parent f5b09daf2c
commit d48bf26278

View File

@@ -28,7 +28,7 @@ func TestUndo_DryRun(t *testing.T) {
func TestExecute_Undo(t *testing.T) {
t.Parallel()
stub := toolOutputStub(testToken, "write", `{"undone":3,"op_ids":["op-3","op-2","op-1"],"results":[{"op_id":"op-3"},{"op_id":"op-2"},{"op_id":"op-1"}]}`)
stub := toolOutputStub(testToken, "write", `{"undone":3,"op_id":"op-3","top_doc_revision":30,"new_revision":31,"op_ids":["op-3","op-2","op-1"],"results":[{"op_id":"op-3","top_doc_revision":30,"new_revision":31},{"op_id":"op-2","top_doc_revision":20,"new_revision":32},{"op_id":"op-1","top_doc_revision":10,"new_revision":33}]}`)
out, err := runShortcutWithStubs(t, Undo, []string{"--url", testURL, "--count", "3", "--as", "user"}, stub)
if err != nil {
t.Fatalf("execute failed: %v\nout=%s", err, out)
@@ -45,16 +45,97 @@ func TestExecute_Undo(t *testing.T) {
if data["undone"].(float64) != 3 {
t.Fatalf("unexpected output data: %#v", data)
}
results := data["results"].([]interface{})
first := results[0].(map[string]interface{})
if data["op_id"] != first["op_id"] ||
data["top_doc_revision"] != first["top_doc_revision"] ||
data["new_revision"] != first["new_revision"] {
t.Fatalf("legacy fields are not aligned with first result: %#v", data)
}
}
func TestUndo_ValidateCount(t *testing.T) {
t.Parallel()
_, err := runShortcutWithStubs(t, Undo, []string{"--url", testURL, "--count", "21", "--as", "user"})
if err == nil {
t.Fatal("expected count validation error")
tests := []struct {
name string
count string
wantSub string
}{
{name: "zero", count: "0", wantSub: "--count must be between 1 and 20"},
{name: "negative", count: "-1", wantSub: "--count must be between 1 and 20"},
{name: "too large", count: "21", wantSub: "--count must be between 1 and 20"},
{name: "non integer", count: "1.5", wantSub: "invalid argument"},
}
if !strings.Contains(err.Error(), "--count must be between 1 and 20") {
t.Fatalf("unexpected error: %v", err)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
_, err := runShortcutWithStubs(t, Undo, []string{"--url", testURL, "--count", tt.count, "--as", "user"})
if err == nil {
t.Fatal("expected count validation error")
}
if !strings.Contains(err.Error(), tt.wantSub) {
t.Fatalf("unexpected error: %v", err)
}
})
}
}
func TestExecute_UndoReasonOutputs(t *testing.T) {
t.Parallel()
tests := []struct {
name string
outputJSON string
wantReason string
wantUndone float64
}{
{
name: "partial success because requested count exceeds stack",
outputJSON: `{"undone":2,"reason":"undo_stack_empty","op_ids":["op-2","op-1"],"results":[{"op_id":"op-2"},{"op_id":"op-1"}],"warning_message":"Requested 3 undo step(s), but only 2 active undo entries were available."}`,
wantReason: "undo_stack_empty",
wantUndone: 2,
},
{
name: "raw undo changeset missing or expired",
outputJSON: `{"undone":0,"reason":"undo_entry_missing","warning_message":"The undo stack entry points to raw undo changeset data that is missing or expired. Use history_list/history_revert to roll the spreadsheet back via history."}`,
wantReason: "undo_entry_missing",
wantUndone: 0,
},
{
name: "unsupported object undo",
outputJSON: `{"undone":0,"reason":"unsupported_object_undo","warning_message":"The top undo entry contains changes this undo implementation cannot replay."}`,
wantReason: "unsupported_object_undo",
wantUndone: 0,
},
{
name: "apply failed",
outputJSON: `{"undone":0,"reason":"undo_apply_failed","warning_message":"Failed to apply the top undo entry."}`,
wantReason: "undo_apply_failed",
wantUndone: 0,
},
{
name: "marker persist failed after apply",
outputJSON: `{"undone":1,"reason":"undo_state_persist_failed","op_ids":["op-1"],"results":[{"op_id":"op-1","reason":"undo_state_persist_failed","warning_message":"Undo was applied, but the marker was not persisted."}]}`,
wantReason: "undo_state_persist_failed",
wantUndone: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
stub := toolOutputStub(testToken, "write", tt.outputJSON)
out, err := runShortcutWithStubs(t, Undo, []string{"--url", testURL, "--as", "user"}, stub)
if err != nil {
t.Fatalf("execute failed: %v\nout=%s", err, out)
}
data := decodeEnvelopeData(t, out)
if data["undone"].(float64) != tt.wantUndone || data["reason"] != tt.wantReason {
t.Fatalf("unexpected output data: %#v", data)
}
if tt.wantReason == "undo_entry_missing" && !strings.Contains(data["warning_message"].(string), "history_list/history_revert") {
t.Fatalf("missing history fallback hint: %#v", data)
}
})
}
}