fix: preserve record query preview contract

This commit is contained in:
Dennis committed 2026-08-17 14:13:47 +08:00
1 parent 37b9a1dc31
commit 7aa8240629
4 files changed
+59 -13

No files matched your search

+2 -3
View File
@@ -334,9 +334,8 @@ func queryDeletedRecordsByIDs(rt *shortcut.RuntimeContext, baseID, tableID strin
}
return nil, fmt.Errorf("query_records deletion read-back chunk %d is missing records", offset/recordQueryServicePageSize+1)
}
if responseHasMore(data) {
return nil, fmt.Errorf("query_records deletion read-back chunk %d is incomplete", offset/recordQueryServicePageSize+1)
}
// Exact-ID absence is complete evidence for deletion even when the
// service retains an unrelated continuation marker.
remaining = append(remaining, records...)
}
return remaining, nil
+33 -4
View File
@@ -21,10 +21,12 @@ const (
)
type recordQueryWindow struct {
Records []map[string]any
HasMore bool
NextCursor string
Pages int
Records []map[string]any
HasMore bool
NextCursor string
Pages int
TotalCount any
HasTotalCount bool
}
func queryRecordWindow(rt *shortcut.RuntimeContext, params map[string]any, limit int) (recordQueryWindow, error) {
@@ -53,6 +55,9 @@ func queryRecordWindow(rt *shortcut.RuntimeContext, params map[string]any, limit
if err != nil {
return recordQueryWindow{}, err
}
if !window.HasTotalCount {
window.TotalCount, window.HasTotalCount = responseTotalCount(data)
}
records, found := findRecords(data)
if !found {
if explicitEmptyRecordQuery(data) {
@@ -119,6 +124,14 @@ func explicitEmptyRecordQuery(data map[string]any) bool {
}
func executeRecordQuery(rt *shortcut.RuntimeContext, params map[string]any) error {
if rt.DryRun() {
return rt.Output(map[string]any{
"dry_run": true,
"executed": false,
"tool": "query_records",
"arguments": params,
})
}
limit := 100
if rt.Changed("limit") {
limit = rt.Int("limit")
@@ -168,6 +181,9 @@ func executeRecordQuery(rt *shortcut.RuntimeContext, params map[string]any) erro
if window.NextCursor != "" {
data["nextCursor"] = window.NextCursor
}
if window.HasTotalCount {
data["totalCount"] = window.TotalCount
}
return rt.Output(map[string]any{
"success": true,
"status": "success",
@@ -175,6 +191,19 @@ func executeRecordQuery(rt *shortcut.RuntimeContext, params map[string]any) erro
})
}
func responseTotalCount(data map[string]any) (any, bool) {
for {
if totalCount, exists := data["totalCount"]; exists {
return totalCount, true
}
nested, ok := data["data"].(map[string]any)
if !ok {
return nil, false
}
data = nested
}
}
func recordQueryRequestedIDs(params map[string]any) ([]string, bool, error) {
raw, exists := params["recordIds"]
if !exists {
@@ -280,7 +280,7 @@ func pagedRecordQueryResponse(t *testing.T, records []map[string]any, args map[s
}
end := minInt(offset+limit, len(records))
pageRecords := records[offset:end]
data := map[string]any{"records": pageRecords, "hasMore": end < len(records)}
data := map[string]any{"records": pageRecords, "hasMore": end < len(records), "totalCount": len(records)}
if end < len(records) {
data["nextCursor"] = fmt.Sprintf("offset-%d", end)
}
@@ -337,7 +337,7 @@ func TestCrossPlatformCoverageRecordQueryServicePageBoundariesE2E(t *testing.T)
t.Fatalf("record query size %d: %v", size, err)
}
data, ok := payload["data"].(map[string]any)
if !ok || payload["success"] != true || data["hasMore"] != false || len(data["records"].([]any)) != size {
if !ok || payload["success"] != true || data["hasMore"] != false || data["totalCount"] != float64(size) || len(data["records"].([]any)) != size {
t.Fatalf("record query size %d payload = %#v", size, payload)
}
wantCalls := (size + recordQueryServicePageSize - 1) / recordQueryServicePageSize
@@ -348,6 +348,22 @@ func TestCrossPlatformCoverageRecordQueryServicePageBoundariesE2E(t *testing.T)
}
}
func TestCrossPlatformCoverageRecordQueryDryRunStopsBeforeTransportE2E(t *testing.T) {
caller := &upsertByKeyCaller{}
payload, err := runRecordQueryShortcutCLI(t, caller, 5, "--query", "needle", "--dry-run")
if err != nil {
t.Fatalf("record query dry-run error = %v", err)
}
if len(caller.calls) != 0 {
t.Fatalf("record query dry-run crossed transport: %#v", caller.calls)
}
arguments, _ := payload["arguments"].(map[string]any)
if payload["dry_run"] != true || payload["executed"] != false || payload["tool"] != "query_records" ||
arguments["baseId"] != "base" || arguments["tableId"] != "table" || arguments["keyword"] != "needle" || arguments["limit"] != float64(5) {
t.Fatalf("record query dry-run payload = %#v", payload)
}
}
func TestCrossPlatformCoverageRecordQueryExactIDsIgnoreResidualContinuationE2E(t *testing.T) {
caller := &upsertByKeyCaller{callFn: func(call int, _, tool string, args map[string]any) (string, error) {
if tool != "query_records" {
@@ -538,15 +554,16 @@ func TestCrossPlatformCoverageRecordQueryFailureAndContinuationBranches(t *testi
}
})
t.Run("delete readback rejects continuation", func(t *testing.T) {
t.Run("delete readback ignores residual continuation", func(t *testing.T) {
caller := &upsertByKeyCaller{callFn: func(_ int, _, tool string, _ map[string]any) (string, error) {
if tool == "query_records" {
return `{"success":true,"data":{"records":[],"hasMore":true,"nextCursor":"unexpected"}}`, nil
}
return `{"deletedCount":1}`, nil
}}
if _, err := runRecordDeleteCLI(t, caller, []string{"r1"}); err == nil || len(caller.calls) != 2 || caller.calls[1].tool != "query_records" {
t.Fatalf("delete continuation error = %v calls=%#v", err, caller.calls)
out, err := runRecordDeleteCLI(t, caller, []string{"r1"})
if err != nil || out == "" || len(caller.calls) != 2 || caller.calls[1].tool != "query_records" {
t.Fatalf("delete residual continuation output=%q error=%v calls=%#v", out, err, caller.calls)
}
})
}