fix: replace string(rune()) with strconv.Itoa in tests (bd-fmc)

Also updated CONFIG.md to clarify mass delete threshold requires >5 issues (bd-in6).

The string(rune('0'+i)) pattern produces incorrect characters when i >= 10.
Changed to strconv.Itoa(i) for reliable conversion.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-02 22:41:07 -08:00
parent a067796055
commit cd7830a5a6
7 changed files with 22 additions and 15 deletions

View File

@@ -6,6 +6,7 @@ import (
"encoding/hex"
"os"
"path/filepath"
"strconv"
"testing"
"github.com/steveyegge/beads/internal/storage/sqlite"
@@ -42,9 +43,9 @@ func TestExportIntegrityAfterJSONLTruncation(t *testing.T) {
var allIssues []*types.Issue
for i := 1; i <= numIssues; i++ {
issue := &types.Issue{
ID: "bd-" + string(rune('0'+i)),
Title: "Test issue " + string(rune('0'+i)),
Description: "Description " + string(rune('0'+i)),
ID: "bd-" + strconv.Itoa(i),
Title: "Test issue " + strconv.Itoa(i),
Description: "Description " + strconv.Itoa(i),
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeTask,
@@ -276,8 +277,8 @@ func TestMultipleExportsStayConsistent(t *testing.T) {
var issues []*types.Issue
for i := 1; i <= 5; i++ {
issue := &types.Issue{
ID: "bd-" + string(rune('0'+i)),
Title: "Issue " + string(rune('0'+i)),
ID: "bd-" + strconv.Itoa(i),
Title: "Issue " + strconv.Itoa(i),
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeTask,

View File

@@ -3,6 +3,7 @@ package main
import (
"context"
"path/filepath"
"strconv"
"testing"
"time"
@@ -215,7 +216,7 @@ func TestStaleIssuesWithLimit(t *testing.T) {
for i := 1; i <= 5; i++ {
updatedAt := oldTime.Add(time.Duration(i) * time.Hour) // Slightly different times for sorting
issue := &types.Issue{
ID: "test-stale-limit-" + string(rune('0'+i)),
ID: "test-stale-limit-" + strconv.Itoa(i),
Title: "Stale issue",
Status: types.StatusOpen,
Priority: 1,
@@ -231,7 +232,7 @@ func TestStaleIssuesWithLimit(t *testing.T) {
// Update timestamps directly in DB using datetime() function
db := s.UnderlyingDB()
for i := 1; i <= 5; i++ {
id := "test-stale-limit-" + string(rune('0'+i))
id := "test-stale-limit-" + strconv.Itoa(i)
// Make each slightly different (40 days ago + i hours)
_, err := db.ExecContext(ctx, "UPDATE issues SET updated_at = datetime('now', '-40 days', '+' || ? || ' hours') WHERE id = ?", i, id)
if err != nil {

View File

@@ -187,7 +187,7 @@ Configuration keys use dot-notation namespaces to organize settings:
- `export.write_manifest` - Write .manifest.json with export metadata (default: false)
- `auto_export.error_policy` - Override error policy for auto-exports (default: `best-effort`)
- `sync.branch` - Name of the dedicated sync branch for beads data (see docs/PROTECTED_BRANCHES.md)
- `sync.require_confirmation_on_mass_delete` - Require interactive confirmation before pushing when >50% of issues vanish during a merge (default: `false`)
- `sync.require_confirmation_on_mass_delete` - Require interactive confirmation before pushing when >50% of issues vanish during a merge AND more than 5 issues existed before (default: `false`)
### Integration Namespaces
@@ -336,7 +336,8 @@ Controls for the sync branch workflow (see docs/PROTECTED_BRANCHES.md):
bd config set sync.branch beads-metadata
# Enable mass deletion protection (optional, default: false)
# When enabled, if >50% of issues vanish during a merge, bd sync will:
# When enabled, if >50% of issues vanish during a merge AND more than 5
# issues existed before the merge, bd sync will:
# 1. Show forensic info about vanished issues
# 2. Prompt for confirmation before pushing
bd config set sync.require_confirmation_on_mass_delete "true"

View File

@@ -2,6 +2,7 @@ package sqlite
import (
"context"
"strconv"
"testing"
"github.com/steveyegge/beads/internal/types"
@@ -289,7 +290,7 @@ func TestDeepHierarchyCacheCorrectness(t *testing.T) {
var issues []*types.Issue
for i := 0; i < 4; i++ {
issue := &types.Issue{
Title: "Level " + string(rune('0'+i)),
Title: "Level " + strconv.Itoa(i),
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeEpic,

View File

@@ -2,6 +2,7 @@ package sqlite
import (
"context"
"strconv"
"testing"
"github.com/steveyegge/beads/internal/types"
@@ -143,7 +144,7 @@ func TestGetIssueCommentsOrdering(t *testing.T) {
// Add comments with identifiable ordering
for i := 1; i <= 5; i++ {
text := "Comment " + string(rune('0'+i))
text := "Comment " + strconv.Itoa(i)
_, err := store.AddIssueComment(ctx, issue.ID, "alice", text)
if err != nil {
t.Fatalf("AddIssueComment failed: %v", err)
@@ -162,7 +163,7 @@ func TestGetIssueCommentsOrdering(t *testing.T) {
}
for i := 0; i < len(comments); i++ {
expectedText := "Comment " + string(rune('0'+i+1))
expectedText := "Comment " + strconv.Itoa(i+1)
if comments[i].Text != expectedText {
t.Errorf("Comment %d: expected text %s, got %s", i, expectedText, comments[i].Text)
}

View File

@@ -4,6 +4,7 @@ package sqlite
import (
"context"
"strconv"
"testing"
"time"
@@ -112,9 +113,9 @@ func BenchmarkCheckEligibility(b *testing.B) {
}
}
func generateID(b testing.TB, prefix string, n int) string{
func generateID(b testing.TB, prefix string, n int) string {
b.Helper()
return prefix + string(rune('0'+n/10)) + string(rune('0'+n%10))
return prefix + strconv.Itoa(n)
}
func setupBenchDB(tb testing.TB) (*SQLiteStorage, func()) {

View File

@@ -2,6 +2,7 @@ package sqlite
import (
"context"
"strconv"
"testing"
"github.com/steveyegge/beads/internal/types"
@@ -415,7 +416,7 @@ func TestDetectCyclesLongCycle(t *testing.T) {
issues := make([]*types.Issue, cycleLength)
for i := 0; i < cycleLength; i++ {
issues[i] = &types.Issue{
Title: "Issue " + string(rune('0'+i)),
Title: "Issue " + strconv.Itoa(i),
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeTask,