Enhance bd doctor with operational health checks (bd-40a0)
This commit is contained in:
304
cmd/bd/doctor.go
304
cmd/bd/doctor.go
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/steveyegge/beads"
|
"github.com/steveyegge/beads"
|
||||||
|
"github.com/steveyegge/beads/internal/daemon"
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,6 +50,11 @@ This command checks:
|
|||||||
- Database version and schema compatibility
|
- Database version and schema compatibility
|
||||||
- Whether using hash-based vs sequential IDs
|
- Whether using hash-based vs sequential IDs
|
||||||
- If CLI version is current (checks GitHub releases)
|
- If CLI version is current (checks GitHub releases)
|
||||||
|
- Multiple database files
|
||||||
|
- Multiple JSONL files
|
||||||
|
- Daemon health (version mismatches, stale processes)
|
||||||
|
- Database-JSONL sync status
|
||||||
|
- File permissions
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
bd doctor # Check current directory
|
bd doctor # Check current directory
|
||||||
@@ -123,6 +129,41 @@ func runDiagnostics(path string) doctorResult {
|
|||||||
result.Checks = append(result.Checks, versionCheck)
|
result.Checks = append(result.Checks, versionCheck)
|
||||||
// Don't fail overall check for outdated CLI, just warn
|
// Don't fail overall check for outdated CLI, just warn
|
||||||
|
|
||||||
|
// Check 5: Multiple database files
|
||||||
|
multiDBCheck := checkMultipleDatabases(path)
|
||||||
|
result.Checks = append(result.Checks, multiDBCheck)
|
||||||
|
if multiDBCheck.Status == statusWarning || multiDBCheck.Status == statusError {
|
||||||
|
result.OverallOK = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check 6: Multiple JSONL files
|
||||||
|
multiJSONLCheck := checkMultipleJSONLFiles(path)
|
||||||
|
result.Checks = append(result.Checks, multiJSONLCheck)
|
||||||
|
if multiJSONLCheck.Status == statusWarning || multiJSONLCheck.Status == statusError {
|
||||||
|
result.OverallOK = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check 7: Daemon health
|
||||||
|
daemonCheck := checkDaemonStatus(path)
|
||||||
|
result.Checks = append(result.Checks, daemonCheck)
|
||||||
|
if daemonCheck.Status == statusWarning || daemonCheck.Status == statusError {
|
||||||
|
result.OverallOK = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check 8: Database-JSONL sync
|
||||||
|
syncCheck := checkDatabaseJSONLSync(path)
|
||||||
|
result.Checks = append(result.Checks, syncCheck)
|
||||||
|
if syncCheck.Status == statusWarning || syncCheck.Status == statusError {
|
||||||
|
result.OverallOK = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check 9: Permissions
|
||||||
|
permCheck := checkPermissions(path)
|
||||||
|
result.Checks = append(result.Checks, permCheck)
|
||||||
|
if permCheck.Status == statusError {
|
||||||
|
result.OverallOK = false
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,6 +536,269 @@ func printDiagnostics(result doctorResult) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkMultipleDatabases(path string) doctorCheck {
|
||||||
|
beadsDir := filepath.Join(path, ".beads")
|
||||||
|
|
||||||
|
// Find all .db files (excluding backups and vc.db)
|
||||||
|
files, err := filepath.Glob(filepath.Join(beadsDir, "*.db"))
|
||||||
|
if err != nil {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Database Files",
|
||||||
|
Status: statusError,
|
||||||
|
Message: "Unable to check for multiple databases",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out backups and vc.db
|
||||||
|
var dbFiles []string
|
||||||
|
for _, f := range files {
|
||||||
|
base := filepath.Base(f)
|
||||||
|
if !strings.HasSuffix(base, ".backup.db") && base != "vc.db" {
|
||||||
|
dbFiles = append(dbFiles, base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dbFiles) == 0 {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Database Files",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: "No database files (JSONL-only mode)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dbFiles) == 1 {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Database Files",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: "Single database file",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple databases found
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Database Files",
|
||||||
|
Status: statusWarning,
|
||||||
|
Message: fmt.Sprintf("Multiple database files found: %s", strings.Join(dbFiles, ", ")),
|
||||||
|
Fix: "Run 'bd migrate' to consolidate databases or manually remove old .db files",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkMultipleJSONLFiles(path string) doctorCheck {
|
||||||
|
beadsDir := filepath.Join(path, ".beads")
|
||||||
|
|
||||||
|
var jsonlFiles []string
|
||||||
|
for _, name := range []string{"issues.jsonl", "beads.jsonl"} {
|
||||||
|
jsonlPath := filepath.Join(beadsDir, name)
|
||||||
|
if _, err := os.Stat(jsonlPath); err == nil {
|
||||||
|
jsonlFiles = append(jsonlFiles, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jsonlFiles) == 0 {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "JSONL Files",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: "No JSONL files found (database-only mode)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jsonlFiles) == 1 {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "JSONL Files",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: fmt.Sprintf("Using %s", jsonlFiles[0]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple JSONL files found
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "JSONL Files",
|
||||||
|
Status: statusWarning,
|
||||||
|
Message: fmt.Sprintf("Multiple JSONL files found: %s", strings.Join(jsonlFiles, ", ")),
|
||||||
|
Fix: "Standardize on one JSONL file (issues.jsonl recommended). Delete or rename the other.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDaemonStatus(path string) doctorCheck {
|
||||||
|
// Import daemon discovery from internal package
|
||||||
|
daemons, err := daemon.DiscoverDaemons([]string{path})
|
||||||
|
if err != nil {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Daemon Health",
|
||||||
|
Status: statusWarning,
|
||||||
|
Message: "Unable to check daemon health",
|
||||||
|
Detail: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter to this workspace
|
||||||
|
var workspaceDaemons []daemon.DaemonInfo
|
||||||
|
for _, d := range daemons {
|
||||||
|
if d.WorkspacePath == path {
|
||||||
|
workspaceDaemons = append(workspaceDaemons, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(workspaceDaemons) == 0 {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Daemon Health",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: "No daemon running (will auto-start on next command)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for version mismatches
|
||||||
|
for _, d := range workspaceDaemons {
|
||||||
|
if !d.Alive {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Daemon Health",
|
||||||
|
Status: statusWarning,
|
||||||
|
Message: "Stale daemon detected",
|
||||||
|
Fix: "Run 'bd daemons killall' to clean up stale daemons",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.Version != Version {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Daemon Health",
|
||||||
|
Status: statusWarning,
|
||||||
|
Message: fmt.Sprintf("Version mismatch (daemon: %s, CLI: %s)", d.Version, Version),
|
||||||
|
Fix: "Run 'bd daemons killall' to restart daemons with current version",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Daemon Health",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: fmt.Sprintf("Daemon running (PID %d, version %s)", workspaceDaemons[0].PID, workspaceDaemons[0].Version),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDatabaseJSONLSync(path string) doctorCheck {
|
||||||
|
beadsDir := filepath.Join(path, ".beads")
|
||||||
|
dbPath := filepath.Join(beadsDir, beads.CanonicalDatabaseName)
|
||||||
|
|
||||||
|
// Find JSONL file
|
||||||
|
var jsonlPath string
|
||||||
|
for _, name := range []string{"issues.jsonl", "beads.jsonl"} {
|
||||||
|
path := filepath.Join(beadsDir, name)
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
jsonlPath = path
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no database, skip this check
|
||||||
|
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "DB-JSONL Sync",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: "N/A (no database)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no JSONL, skip this check
|
||||||
|
if jsonlPath == "" {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "DB-JSONL Sync",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: "N/A (no JSONL file)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare modification times
|
||||||
|
dbInfo, err := os.Stat(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "DB-JSONL Sync",
|
||||||
|
Status: statusWarning,
|
||||||
|
Message: "Unable to check database file",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonlInfo, err := os.Stat(jsonlPath)
|
||||||
|
if err != nil {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "DB-JSONL Sync",
|
||||||
|
Status: statusWarning,
|
||||||
|
Message: "Unable to check JSONL file",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If JSONL is newer, warn about potential sync issue
|
||||||
|
if jsonlInfo.ModTime().After(dbInfo.ModTime()) {
|
||||||
|
timeDiff := jsonlInfo.ModTime().Sub(dbInfo.ModTime())
|
||||||
|
if timeDiff > 30*time.Second {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "DB-JSONL Sync",
|
||||||
|
Status: statusWarning,
|
||||||
|
Message: "JSONL is newer than database",
|
||||||
|
Fix: "Run 'bd sync --import-only' to import JSONL updates",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "DB-JSONL Sync",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: "Database and JSONL are in sync",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPermissions(path string) doctorCheck {
|
||||||
|
beadsDir := filepath.Join(path, ".beads")
|
||||||
|
|
||||||
|
// Check if .beads/ is writable
|
||||||
|
testFile := filepath.Join(beadsDir, ".doctor-test-write")
|
||||||
|
if err := os.WriteFile(testFile, []byte("test"), 0600); err != nil {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Permissions",
|
||||||
|
Status: statusError,
|
||||||
|
Message: ".beads/ directory is not writable",
|
||||||
|
Fix: fmt.Sprintf("Fix permissions: chmod u+w %s", beadsDir),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = os.Remove(testFile) // Clean up test file (intentionally ignore error)
|
||||||
|
|
||||||
|
// Check database permissions
|
||||||
|
dbPath := filepath.Join(beadsDir, beads.CanonicalDatabaseName)
|
||||||
|
if _, err := os.Stat(dbPath); err == nil {
|
||||||
|
// Try to open database
|
||||||
|
db, err := sql.Open("sqlite", dbPath)
|
||||||
|
if err != nil {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Permissions",
|
||||||
|
Status: statusError,
|
||||||
|
Message: "Database file exists but cannot be opened",
|
||||||
|
Fix: fmt.Sprintf("Check database permissions: %s", dbPath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = db.Close() // Intentionally ignore close error
|
||||||
|
|
||||||
|
// Try a write test
|
||||||
|
db, err = sql.Open("sqlite", dbPath)
|
||||||
|
if err == nil {
|
||||||
|
_, err = db.Exec("SELECT 1")
|
||||||
|
_ = db.Close() // Intentionally ignore close error
|
||||||
|
if err != nil {
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Permissions",
|
||||||
|
Status: statusError,
|
||||||
|
Message: "Database file is not readable",
|
||||||
|
Fix: fmt.Sprintf("Fix permissions: chmod u+rw %s", dbPath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return doctorCheck{
|
||||||
|
Name: "Permissions",
|
||||||
|
Status: statusOK,
|
||||||
|
Message: "All permissions OK",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
doctorCmd.Flags().Bool("json", false, "Output JSON format")
|
doctorCmd.Flags().Bool("json", false, "Output JSON format")
|
||||||
rootCmd.AddCommand(doctorCmd)
|
rootCmd.AddCommand(doctorCmd)
|
||||||
|
|||||||
@@ -171,3 +171,205 @@ func TestCompareVersions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckMultipleDatabases(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
dbFiles []string
|
||||||
|
expectedStatus string
|
||||||
|
expectWarning bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no databases",
|
||||||
|
dbFiles: []string{},
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
expectWarning: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single database",
|
||||||
|
dbFiles: []string{"beads.db"},
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
expectWarning: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple databases",
|
||||||
|
dbFiles: []string{"beads.db", "old.db"},
|
||||||
|
expectedStatus: statusWarning,
|
||||||
|
expectWarning: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "backup files ignored",
|
||||||
|
dbFiles: []string{"beads.db", "beads.backup.db"},
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
expectWarning: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vc.db ignored",
|
||||||
|
dbFiles: []string{"beads.db", "vc.db"},
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
expectWarning: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||||
|
if err := os.Mkdir(beadsDir, 0750); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create test database files
|
||||||
|
for _, dbFile := range tc.dbFiles {
|
||||||
|
path := filepath.Join(beadsDir, dbFile)
|
||||||
|
if err := os.WriteFile(path, []byte{}, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check := checkMultipleDatabases(tmpDir)
|
||||||
|
|
||||||
|
if check.Status != tc.expectedStatus {
|
||||||
|
t.Errorf("Expected status %s, got %s", tc.expectedStatus, check.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.expectWarning && check.Fix == "" {
|
||||||
|
t.Error("Expected fix message for warning status")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckMultipleJSONLFiles(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
jsonlFiles []string
|
||||||
|
expectedStatus string
|
||||||
|
expectWarning bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no JSONL files",
|
||||||
|
jsonlFiles: []string{},
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
expectWarning: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single issues.jsonl",
|
||||||
|
jsonlFiles: []string{"issues.jsonl"},
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
expectWarning: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single beads.jsonl",
|
||||||
|
jsonlFiles: []string{"beads.jsonl"},
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
expectWarning: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both JSONL files",
|
||||||
|
jsonlFiles: []string{"issues.jsonl", "beads.jsonl"},
|
||||||
|
expectedStatus: statusWarning,
|
||||||
|
expectWarning: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||||
|
if err := os.Mkdir(beadsDir, 0750); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create test JSONL files
|
||||||
|
for _, jsonlFile := range tc.jsonlFiles {
|
||||||
|
path := filepath.Join(beadsDir, jsonlFile)
|
||||||
|
if err := os.WriteFile(path, []byte{}, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check := checkMultipleJSONLFiles(tmpDir)
|
||||||
|
|
||||||
|
if check.Status != tc.expectedStatus {
|
||||||
|
t.Errorf("Expected status %s, got %s", tc.expectedStatus, check.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.expectWarning && check.Fix == "" {
|
||||||
|
t.Error("Expected fix message for warning status")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckPermissions(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||||
|
if err := os.Mkdir(beadsDir, 0750); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
check := checkPermissions(tmpDir)
|
||||||
|
|
||||||
|
if check.Status != statusOK {
|
||||||
|
t.Errorf("Expected ok status for writable directory, got %s: %s", check.Status, check.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckDatabaseJSONLSync(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
hasDB bool
|
||||||
|
hasJSONL bool
|
||||||
|
expectedStatus string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no database",
|
||||||
|
hasDB: false,
|
||||||
|
hasJSONL: true,
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no JSONL",
|
||||||
|
hasDB: true,
|
||||||
|
hasJSONL: false,
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both present",
|
||||||
|
hasDB: true,
|
||||||
|
hasJSONL: true,
|
||||||
|
expectedStatus: statusOK,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||||
|
if err := os.Mkdir(beadsDir, 0750); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.hasDB {
|
||||||
|
dbPath := filepath.Join(beadsDir, "beads.db")
|
||||||
|
if err := os.WriteFile(dbPath, []byte{}, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.hasJSONL {
|
||||||
|
jsonlPath := filepath.Join(beadsDir, "issues.jsonl")
|
||||||
|
if err := os.WriteFile(jsonlPath, []byte{}, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check := checkDatabaseJSONLSync(tmpDir)
|
||||||
|
|
||||||
|
if check.Status != tc.expectedStatus {
|
||||||
|
t.Errorf("Expected status %s, got %s", tc.expectedStatus, check.Status)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user