test: add failing tests for short ID support in comments (#1070)

Add tests demonstrating that `bd comments add` and `bd comments list`
don't accept short IDs in daemon mode, while other commands do.

Tests added:
- TestCLI_CommentsAddShortID (cli_fast_test.go)
  - Tests short ID, partial ID, and comment alias in direct mode (passes)

- TestCommentAddWithShortID (internal/rpc/comments_test.go)
  - Tests RPC layer with short ID (FAILS - demonstrates bug)

- TestCommentListWithShortID (internal/rpc/comments_test.go)
  - Tests listing comments with short ID (FAILS - demonstrates bug)

The fix should add daemonClient.ResolveID() before AddComment/ListComments,
following the pattern in update.go and label.go.

Refs: https://github.com/steveyegge/beads/issues/1070
This commit is contained in:
Mike
2026-01-13 13:22:19 +00:00
parent a55b9c3064
commit 1f84f7cce3
2 changed files with 264 additions and 0 deletions

View File

@@ -972,4 +972,128 @@ func TestCLI_CreateDryRun(t *testing.T) {
}) })
} }
// TestCLI_CommentsAddShortID tests that 'comments add' accepts short IDs (issue #1070)
// Most bd commands accept short IDs (e.g., "5wbm") but comments add previously required
// full IDs (e.g., "mike.vibe-coding-5wbm"). This test ensures short IDs work.
//
// Note: This test runs with --no-daemon (direct mode) where short IDs already work
// because the code calls utils.ResolvePartialID(). The actual bug (GitHub #1070) is
// in daemon mode where the ID isn't resolved before being sent to the RPC server.
// The fix should add daemonClient.ResolveID() before daemonClient.AddComment(),
// following the pattern in update.go and label.go.
func TestCLI_CommentsAddShortID(t *testing.T) {
if testing.Short() {
t.Skip("skipping slow CLI test in short mode")
}
t.Run("ShortIDWithCommentsAdd", func(t *testing.T) {
tmpDir := setupCLITestDB(t)
// Create an issue and get its full ID
out := runBDInProcess(t, tmpDir, "create", "Issue for comment test", "-p", "1", "--json")
jsonStart := strings.Index(out, "{")
if jsonStart < 0 {
t.Fatalf("No JSON found in output: %s", out)
}
jsonOut := out[jsonStart:]
var issue map[string]interface{}
if err := json.Unmarshal([]byte(jsonOut), &issue); err != nil {
t.Fatalf("Failed to parse JSON: %v\nOutput: %s", err, jsonOut)
}
fullID := issue["id"].(string)
t.Logf("Created issue with full ID: %s", fullID)
// Extract short ID (the part after the last hyphen in prefix-hash format)
// For IDs like "test-abc123", the short ID is "abc123"
parts := strings.Split(fullID, "-")
if len(parts) < 2 {
t.Fatalf("Unexpected ID format: %s", fullID)
}
shortID := parts[len(parts)-1]
t.Logf("Using short ID: %s", shortID)
// Add a comment using the SHORT ID (not full ID)
stdout, stderr, err := runBDInProcessAllowError(t, tmpDir, "comments", "add", shortID, "Test comment with short ID")
if err != nil {
t.Fatalf("comments add failed: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if !strings.Contains(stdout, "Comment added") {
t.Errorf("Expected 'Comment added' in output, got: %s", stdout)
}
// Verify the comment was actually added by listing comments (use full ID for list)
stdout, stderr, err = runBDInProcessAllowError(t, tmpDir, "comments", fullID)
if err != nil {
t.Fatalf("comments list failed: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if !strings.Contains(stdout, "Test comment with short ID") {
t.Errorf("Expected comment text in list output, got: %s", stdout)
}
})
t.Run("PartialIDWithCommentsAdd", func(t *testing.T) {
tmpDir := setupCLITestDB(t)
// Create an issue
out := runBDInProcess(t, tmpDir, "create", "Issue for partial ID test", "-p", "1", "--json")
jsonStart := strings.Index(out, "{")
jsonOut := out[jsonStart:]
var issue map[string]interface{}
json.Unmarshal([]byte(jsonOut), &issue)
fullID := issue["id"].(string)
// Extract short ID and use only first 4 characters (partial match)
parts := strings.Split(fullID, "-")
shortID := parts[len(parts)-1]
if len(shortID) > 4 {
shortID = shortID[:4] // Use only first 4 chars for partial match
}
t.Logf("Full ID: %s, Partial ID: %s", fullID, shortID)
// Add comment using partial ID
stdout, stderr, err := runBDInProcessAllowError(t, tmpDir, "comments", "add", shortID, "Comment via partial ID")
if err != nil {
t.Fatalf("comments add with partial ID failed: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if !strings.Contains(stdout, "Comment added") {
t.Errorf("Expected 'Comment added' in output, got: %s", stdout)
}
})
t.Run("CommentAliasWithShortID", func(t *testing.T) {
tmpDir := setupCLITestDB(t)
// Create an issue
out := runBDInProcess(t, tmpDir, "create", "Issue for alias test", "-p", "1", "--json")
jsonStart := strings.Index(out, "{")
jsonOut := out[jsonStart:]
var issue map[string]interface{}
json.Unmarshal([]byte(jsonOut), &issue)
fullID := issue["id"].(string)
// Extract short ID
parts := strings.Split(fullID, "-")
shortID := parts[len(parts)-1]
// Use the 'comment' alias (deprecated but should still work)
stdout, stderr, err := runBDInProcessAllowError(t, tmpDir, "comment", shortID, "Comment via alias with short ID")
if err != nil {
t.Fatalf("comment alias failed: %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
}
if !strings.Contains(stdout, "Comment added") {
t.Errorf("Expected 'Comment added' in output, got: %s", stdout)
}
})
}

View File

@@ -69,3 +69,143 @@ func TestCommentOperationsViaRPC(t *testing.T) {
t.Fatalf("expected comment text 'first comment', got %q", comments[0].Text) t.Fatalf("expected comment text 'first comment', got %q", comments[0].Text)
} }
} }
// TestCommentAddWithShortID tests that AddComment accepts short IDs (issue #1070).
// Currently, the RPC layer requires full IDs. The fix should resolve short IDs
// to full IDs before performing operations.
//
// This test is expected to FAIL until the bug is fixed.
func TestCommentAddWithShortID(t *testing.T) {
if testing.Short() {
t.Skip("skipping slow RPC test in short mode")
}
_, client, cleanup := setupTestServer(t)
defer cleanup()
// Create an issue
createResp, err := client.Create(&CreateArgs{
Title: "Short ID comment test",
IssueType: "task",
Priority: 2,
})
if err != nil {
t.Fatalf("create issue failed: %v", err)
}
var created types.Issue
if err := json.Unmarshal(createResp.Data, &created); err != nil {
t.Fatalf("failed to decode create response: %v", err)
}
fullID := created.ID
t.Logf("Created issue with full ID: %s", fullID)
// Extract the short ID (hash portion after the prefix)
// For IDs like "test-abc123", the short ID is "abc123"
shortID := fullID
for i := len(fullID) - 1; i >= 0; i-- {
if fullID[i] == '-' {
shortID = fullID[i+1:]
break
}
}
t.Logf("Using short ID: %s", shortID)
// Try to add a comment using the SHORT ID (not full ID)
// This should work once the bug is fixed
addResp, err := client.AddComment(&CommentAddArgs{
ID: shortID, // Using short ID instead of full ID
Author: "tester",
Text: "comment via short ID",
})
if err != nil {
t.Fatalf("add comment with short ID failed: %v (this is the bug - short IDs should work)", err)
}
var added types.Comment
if err := json.Unmarshal(addResp.Data, &added); err != nil {
t.Fatalf("failed to decode add comment response: %v", err)
}
if added.Text != "comment via short ID" {
t.Errorf("expected comment text 'comment via short ID', got %q", added.Text)
}
// Verify by listing comments using the full ID
listResp, err := client.ListComments(&CommentListArgs{ID: fullID})
if err != nil {
t.Fatalf("list comments failed: %v", err)
}
var comments []*types.Comment
if err := json.Unmarshal(listResp.Data, &comments); err != nil {
t.Fatalf("failed to decode comment list: %v", err)
}
if len(comments) != 1 {
t.Fatalf("expected 1 comment, got %d", len(comments))
}
}
// TestCommentListWithShortID tests that ListComments accepts short IDs (issue #1070).
// This test is expected to FAIL until the bug is fixed.
func TestCommentListWithShortID(t *testing.T) {
if testing.Short() {
t.Skip("skipping slow RPC test in short mode")
}
_, client, cleanup := setupTestServer(t)
defer cleanup()
// Create an issue and add a comment
createResp, err := client.Create(&CreateArgs{
Title: "Short ID list test",
IssueType: "task",
Priority: 2,
})
if err != nil {
t.Fatalf("create issue failed: %v", err)
}
var created types.Issue
if err := json.Unmarshal(createResp.Data, &created); err != nil {
t.Fatalf("failed to decode create response: %v", err)
}
fullID := created.ID
// Add a comment using full ID
_, err = client.AddComment(&CommentAddArgs{
ID: fullID,
Author: "tester",
Text: "test comment for list",
})
if err != nil {
t.Fatalf("add comment failed: %v", err)
}
// Extract short ID
shortID := fullID
for i := len(fullID) - 1; i >= 0; i-- {
if fullID[i] == '-' {
shortID = fullID[i+1:]
break
}
}
t.Logf("Full ID: %s, Short ID: %s", fullID, shortID)
// Try to list comments using the SHORT ID
// This should work once the bug is fixed
listResp, err := client.ListComments(&CommentListArgs{ID: shortID})
if err != nil {
t.Fatalf("list comments with short ID failed: %v (this is the bug - short IDs should work)", err)
}
var comments []*types.Comment
if err := json.Unmarshal(listResp.Data, &comments); err != nil {
t.Fatalf("failed to decode comment list: %v", err)
}
if len(comments) != 1 {
t.Fatalf("expected 1 comment, got %d (short ID resolution may have failed)", len(comments))
}
}