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:
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
@@ -42,9 +43,9 @@ func TestExportIntegrityAfterJSONLTruncation(t *testing.T) {
|
|||||||
var allIssues []*types.Issue
|
var allIssues []*types.Issue
|
||||||
for i := 1; i <= numIssues; i++ {
|
for i := 1; i <= numIssues; i++ {
|
||||||
issue := &types.Issue{
|
issue := &types.Issue{
|
||||||
ID: "bd-" + string(rune('0'+i)),
|
ID: "bd-" + strconv.Itoa(i),
|
||||||
Title: "Test issue " + string(rune('0'+i)),
|
Title: "Test issue " + strconv.Itoa(i),
|
||||||
Description: "Description " + string(rune('0'+i)),
|
Description: "Description " + strconv.Itoa(i),
|
||||||
Status: types.StatusOpen,
|
Status: types.StatusOpen,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
IssueType: types.TypeTask,
|
IssueType: types.TypeTask,
|
||||||
@@ -276,8 +277,8 @@ func TestMultipleExportsStayConsistent(t *testing.T) {
|
|||||||
var issues []*types.Issue
|
var issues []*types.Issue
|
||||||
for i := 1; i <= 5; i++ {
|
for i := 1; i <= 5; i++ {
|
||||||
issue := &types.Issue{
|
issue := &types.Issue{
|
||||||
ID: "bd-" + string(rune('0'+i)),
|
ID: "bd-" + strconv.Itoa(i),
|
||||||
Title: "Issue " + string(rune('0'+i)),
|
Title: "Issue " + strconv.Itoa(i),
|
||||||
Status: types.StatusOpen,
|
Status: types.StatusOpen,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
IssueType: types.TypeTask,
|
IssueType: types.TypeTask,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -215,7 +216,7 @@ func TestStaleIssuesWithLimit(t *testing.T) {
|
|||||||
for i := 1; i <= 5; i++ {
|
for i := 1; i <= 5; i++ {
|
||||||
updatedAt := oldTime.Add(time.Duration(i) * time.Hour) // Slightly different times for sorting
|
updatedAt := oldTime.Add(time.Duration(i) * time.Hour) // Slightly different times for sorting
|
||||||
issue := &types.Issue{
|
issue := &types.Issue{
|
||||||
ID: "test-stale-limit-" + string(rune('0'+i)),
|
ID: "test-stale-limit-" + strconv.Itoa(i),
|
||||||
Title: "Stale issue",
|
Title: "Stale issue",
|
||||||
Status: types.StatusOpen,
|
Status: types.StatusOpen,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
@@ -231,7 +232,7 @@ func TestStaleIssuesWithLimit(t *testing.T) {
|
|||||||
// Update timestamps directly in DB using datetime() function
|
// Update timestamps directly in DB using datetime() function
|
||||||
db := s.UnderlyingDB()
|
db := s.UnderlyingDB()
|
||||||
for i := 1; i <= 5; i++ {
|
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)
|
// 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)
|
_, err := db.ExecContext(ctx, "UPDATE issues SET updated_at = datetime('now', '-40 days', '+' || ? || ' hours') WHERE id = ?", i, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ Configuration keys use dot-notation namespaces to organize settings:
|
|||||||
- `export.write_manifest` - Write .manifest.json with export metadata (default: false)
|
- `export.write_manifest` - Write .manifest.json with export metadata (default: false)
|
||||||
- `auto_export.error_policy` - Override error policy for auto-exports (default: `best-effort`)
|
- `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.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
|
### Integration Namespaces
|
||||||
|
|
||||||
@@ -336,7 +336,8 @@ Controls for the sync branch workflow (see docs/PROTECTED_BRANCHES.md):
|
|||||||
bd config set sync.branch beads-metadata
|
bd config set sync.branch beads-metadata
|
||||||
|
|
||||||
# Enable mass deletion protection (optional, default: false)
|
# 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
|
# 1. Show forensic info about vanished issues
|
||||||
# 2. Prompt for confirmation before pushing
|
# 2. Prompt for confirmation before pushing
|
||||||
bd config set sync.require_confirmation_on_mass_delete "true"
|
bd config set sync.require_confirmation_on_mass_delete "true"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
@@ -289,7 +290,7 @@ func TestDeepHierarchyCacheCorrectness(t *testing.T) {
|
|||||||
var issues []*types.Issue
|
var issues []*types.Issue
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
issue := &types.Issue{
|
issue := &types.Issue{
|
||||||
Title: "Level " + string(rune('0'+i)),
|
Title: "Level " + strconv.Itoa(i),
|
||||||
Status: types.StatusOpen,
|
Status: types.StatusOpen,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
IssueType: types.TypeEpic,
|
IssueType: types.TypeEpic,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
@@ -143,7 +144,7 @@ func TestGetIssueCommentsOrdering(t *testing.T) {
|
|||||||
|
|
||||||
// Add comments with identifiable ordering
|
// Add comments with identifiable ordering
|
||||||
for i := 1; i <= 5; i++ {
|
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)
|
_, err := store.AddIssueComment(ctx, issue.ID, "alice", text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("AddIssueComment failed: %v", err)
|
t.Fatalf("AddIssueComment failed: %v", err)
|
||||||
@@ -162,7 +163,7 @@ func TestGetIssueCommentsOrdering(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(comments); i++ {
|
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 {
|
if comments[i].Text != expectedText {
|
||||||
t.Errorf("Comment %d: expected text %s, got %s", i, expectedText, comments[i].Text)
|
t.Errorf("Comment %d: expected text %s, got %s", i, expectedText, comments[i].Text)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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()
|
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()) {
|
func setupBenchDB(tb testing.TB) (*SQLiteStorage, func()) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
@@ -415,7 +416,7 @@ func TestDetectCyclesLongCycle(t *testing.T) {
|
|||||||
issues := make([]*types.Issue, cycleLength)
|
issues := make([]*types.Issue, cycleLength)
|
||||||
for i := 0; i < cycleLength; i++ {
|
for i := 0; i < cycleLength; i++ {
|
||||||
issues[i] = &types.Issue{
|
issues[i] = &types.Issue{
|
||||||
Title: "Issue " + string(rune('0'+i)),
|
Title: "Issue " + strconv.Itoa(i),
|
||||||
Status: types.StatusOpen,
|
Status: types.StatusOpen,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
IssueType: types.TypeTask,
|
IssueType: types.TypeTask,
|
||||||
|
|||||||
Reference in New Issue
Block a user