diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 76b2a9f..5d1e07e 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -153,6 +153,17 @@ func TestStatusSearchSQLAndListings(t *testing.T) { require.NotEmpty(t, out.String()) } + for _, args := range [][]string{ + {"--config", cfgPath, "metadata", "--json"}, + {"--config", cfgPath, "status", "--json"}, + } { + var out bytes.Buffer + require.NoError(t, Run(ctx, args, &out, &bytes.Buffer{})) + var payload map[string]any + require.NoError(t, json.Unmarshal(out.Bytes(), &payload)) + require.NotEmpty(t, payload) + } + before, err := os.ReadFile(dbPath) require.NoError(t, err) var out bytes.Buffer @@ -183,6 +194,60 @@ func TestTUIHelpReturnsUsage(t *testing.T) { require.Empty(t, stderr.String()) } +func TestControlStatusIncludesShareAndFileSizes(t *testing.T) { + dir := t.TempDir() + dbPath := filepath.Join(dir, "discrawl.db") + require.NoError(t, os.WriteFile(dbPath, []byte("db"), 0o600)) + require.NoError(t, os.WriteFile(dbPath+"-wal", []byte("wal"), 0o600)) + cfg := config.Default() + cfg.DBPath = dbPath + cfg.Share.Remote = "https://github.com/openclaw/discrawl-share.git" + cfg.Share.RepoPath = filepath.Join(dir, "share") + status := store.Status{ + DBPath: dbPath, + MessageCount: 5, + ChannelCount: 2, + } + + out := controlStatus(filepath.Join(dir, "config.toml"), cfg, status, true) + require.Equal(t, int64(2), out.DatabaseBytes) + require.Equal(t, int64(3), out.WALBytes) + require.Zero(t, fileSize(filepath.Join(dir, "missing.db"))) + require.NotNil(t, out.Share) + require.True(t, out.Share.Enabled) + require.True(t, out.Share.NeedsUpdate) + require.Contains(t, out.Summary, "5 messages") +} + +func TestFormattingAndTUISourceBranches(t *testing.T) { + require.Equal(t, "-", formatDaysSilent(-1)) + require.Equal(t, "4", formatDaysSilent(4)) + require.Equal(t, "0", formatWindowDuration(0)) + require.Equal(t, "2d", formatWindowDuration(48*time.Hour)) + require.Equal(t, "3h", formatWindowDuration(3*time.Hour)) + require.Equal(t, "1h30m0s", formatWindowDuration(90*time.Minute)) + require.Equal(t, 6*time.Hour, mustDuration("bogus")) + require.Equal(t, 15*time.Minute, mustDuration("15m")) + + cfg := config.Default() + cfg.DBPath = "/tmp/discrawl.db" + r := &runtime{cfg: cfg} + require.Equal(t, "local", r.archiveSourceKind()) + require.Equal(t, cfg.DBPath, r.archiveSourceLocation()) + guilds, err := r.resolveTUIGuilds(false, "", "") + require.NoError(t, err) + require.Empty(t, guilds) + + r.cfg.DefaultGuildID = "guild-one" + guilds, err = r.resolveTUIGuilds(false, "", "") + require.NoError(t, err) + require.Equal(t, []string{"guild-one"}, guilds) + + r.cfg.Share.Remote = "https://github.com/openclaw/discrawl-share.git" + require.Equal(t, "remote", r.archiveSourceKind()) + require.Equal(t, r.cfg.Share.Remote, r.archiveSourceLocation()) +} + func TestWiretapImportsDesktopDirectMessages(t *testing.T) { ctx := context.Background() dir := t.TempDir() diff --git a/internal/share/share.go b/internal/share/share.go index 4466e9c..1c9b131 100644 --- a/internal/share/share.go +++ b/internal/share/share.go @@ -380,71 +380,6 @@ func NeedsImport(ctx context.Context, s *store.Store, staleAfter time.Duration) return time.Since(t) >= staleAfter } -func exportTable(ctx context.Context, db *sql.DB, repoPath, table string) (TableManifest, error) { - query, args := snapshotExportQuery(table) - rows, err := db.QueryContext(ctx, query, args...) - if err != nil { - return TableManifest{}, fmt.Errorf("query %s: %w", table, err) - } - defer func() { _ = rows.Close() }() - columns, err := rows.Columns() - if err != nil { - return TableManifest{}, fmt.Errorf("columns %s: %w", table, err) - } - tableDir := filepath.Join(repoPath, "tables", table) - if err := os.MkdirAll(tableDir, 0o755); err != nil { - return TableManifest{}, fmt.Errorf("mkdir %s: %w", table, err) - } - writer := tableShardWriter{rootDir: repoPath, relDir: filepath.ToSlash(filepath.Join("tables", table)), label: table} - if err := writer.open(); err != nil { - return TableManifest{}, err - } - defer func() { _ = writer.close() }() - - count := 0 - values := make([]any, len(columns)) - ptrs := make([]any, len(columns)) - for i := range values { - ptrs[i] = &values[i] - } - for rows.Next() { - if err := ctx.Err(); err != nil { - return TableManifest{}, err - } - if err := rows.Scan(ptrs...); err != nil { - return TableManifest{}, fmt.Errorf("scan %s: %w", table, err) - } - row := make(map[string]any, len(columns)) - for i, column := range columns { - row[column] = exportValue(values[i]) - } - body, err := json.Marshal(row) - if err != nil { - return TableManifest{}, fmt.Errorf("marshal %s row: %w", table, err) - } - if err := writer.rotateIfNeeded(); err != nil { - return TableManifest{}, err - } - if _, err := writer.Write(body); err != nil { - return TableManifest{}, fmt.Errorf("write %s row: %w", table, err) - } - if _, err := writer.Write([]byte{'\n'}); err != nil { - return TableManifest{}, fmt.Errorf("write %s newline: %w", table, err) - } - count++ - if err := writer.finishRow(); err != nil { - return TableManifest{}, err - } - } - if err := rows.Err(); err != nil { - return TableManifest{}, fmt.Errorf("iterate %s: %w", table, err) - } - if err := writer.close(); err != nil { - return TableManifest{}, err - } - return TableManifest{Name: table, Files: writer.files, Columns: columns, Rows: count}, nil -} - func exportEmbeddings(ctx context.Context, db *sql.DB, opts Options) (EmbeddingManifest, error) { provider := strings.ToLower(strings.TrimSpace(opts.EmbeddingProvider)) model := strings.TrimSpace(opts.EmbeddingModel) diff --git a/internal/store/messages.go b/internal/store/messages.go index 1ed10e5..d5f43ef 100644 --- a/internal/store/messages.go +++ b/internal/store/messages.go @@ -263,6 +263,9 @@ func (s *Store) hydrateMessageThreadContext(ctx context.Context, rows []MessageR if err != nil { return nil, err } + if err := contextRows.Err(); err != nil { + return nil, err + } if err := s.resolveMessageDisplayMentions(ctx, extra); err != nil { return nil, err }