feat(history): add Atuin context scopes

This commit is contained in:
2026-08-14 13:30:10 +03:00
parent 2b82e016b9
commit e897b4a89a
13 changed files with 560 additions and 30 deletions
+229
View File
@@ -0,0 +1,229 @@
package integration
import (
"database/sql"
"fmt"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/versenilvis/iris/internal/config"
)
func TestLoadAtuinEntriesPreservesContext(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "history.db")
db, err := sql.Open("sqlite", dbPath)
if err != nil {
t.Fatal(err)
}
_, err = db.Exec(`CREATE TABLE history (
command TEXT NOT NULL,
cwd TEXT NOT NULL,
timestamp INTEGER NOT NULL,
exit INTEGER NOT NULL,
session TEXT NOT NULL,
hostname TEXT NOT NULL,
deleted_at INTEGER
)`)
if err != nil {
t.Fatal(err)
}
rows := []struct {
command, cwd, session, hostname string
timestamp, exit, deletedAt int
}{
{"old command", "/repo", "s1", "host", 10, 0, 0},
{"new\ncommand", "/repo/pkg", "s2", "host", 30, 1, 0},
{"deleted command", "/repo", "s3", "host", 40, 0, 99},
}
for _, row := range rows {
var deletedAt any
if row.deletedAt != 0 {
deletedAt = row.deletedAt
}
_, err = db.Exec(
"INSERT INTO history VALUES (?, ?, ?, ?, ?, ?, ?)",
row.command, row.cwd, row.timestamp, row.exit, row.session, row.hostname, deletedAt,
)
if err != nil {
t.Fatal(err)
}
}
if err := db.Close(); err != nil {
t.Fatal(err)
}
originalConfig := config.Get()
cfg := config.DefaultConfig()
cfg.Core.AtuinDBPath = dbPath
config.Init(cfg)
t.Cleanup(func() { config.Init(originalConfig) })
entries, err := loadAtuinEntries()
if err != nil {
t.Fatalf("load Atuin entries: %v", err)
}
if len(entries) != 2 {
t.Fatalf("expected two active entries, got %d", len(entries))
}
if entries[0].Command != "new command" || entries[0].CWD != "/repo/pkg" || entries[0].Exit != 1 {
t.Fatalf("unexpected newest entry: %#v", entries[0])
}
if entries[1].Command != "old command" || entries[1].Session != "s1" {
t.Fatalf("unexpected oldest entry: %#v", entries[1])
}
}
func TestApplyAtuinScope(t *testing.T) {
repo := t.TempDir()
cwd := filepath.Join(repo, "internal", "pkg")
if err := os.MkdirAll(filepath.Join(repo, ".git"), 0755); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(cwd, 0755); err != nil {
t.Fatal(err)
}
originalConfig := config.Get()
originalEntries := atuinEntries
t.Cleanup(func() {
config.Init(originalConfig)
atuinEntries = originalEntries
})
cfg := config.DefaultConfig()
cfg.Core.Atuin = 1
config.Init(cfg)
atuinEntries = []atuinEntry{
{Command: "git status", CWD: cwd, Timestamp: 50},
{Command: "git pull", CWD: repo, Timestamp: 40},
{Command: "git status", CWD: cwd, Timestamp: 30},
{Command: "git push", CWD: filepath.Join(t.TempDir(), "other"), Timestamp: 20},
{Command: "git unknown", CWD: "unknown", Timestamp: 10},
}
global := []HistResult{
{Cmd: "git push", Source: "atuin"},
{Cmd: "git pull", Source: "atuin"},
{Cmd: "git status", Source: "atuin"},
}
tests := []struct {
scope string
want []string
}{
{"global", []string{"git push", "git pull", "git status"}},
{"directory", []string{"git status"}},
{"workspace", []string{"git status", "git pull"}},
{"hybrid", []string{"git status", "git pull", "git push"}},
}
for _, tt := range tests {
t.Run(tt.scope, func(t *testing.T) {
cfg.History.Scope = tt.scope
got := resultCommands(applyAtuinScope(global, "g", map[string]string{"g": "git"}, cwd))
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("scope %s: got %v, want %v", tt.scope, got, tt.want)
}
})
}
cfg.History.Scope = "hybrid"
if got := resultCommands(applyAtuinScope(global, "", nil, cwd)); !reflect.DeepEqual(got, resultCommands(global)) {
t.Fatalf("empty query must stay global: got %v", got)
}
}
func TestWorkspaceScopeOutsideGitUsesDirectory(t *testing.T) {
cwd := t.TempDir()
originalConfig := config.Get()
originalEntries := atuinEntries
t.Cleanup(func() {
config.Init(originalConfig)
atuinEntries = originalEntries
})
cfg := config.DefaultConfig()
cfg.Core.Atuin = 1
cfg.History.Scope = "workspace"
config.Init(cfg)
atuinEntries = []atuinEntry{
{Command: "make local", CWD: cwd},
{Command: "make elsewhere", CWD: t.TempDir()},
}
got := resultCommands(applyAtuinScope(nil, "make", nil, cwd))
if !reflect.DeepEqual(got, []string{"make local"}) {
t.Fatalf("got %v, want directory-only result", got)
}
}
func BenchmarkContextualAtuinCommands(b *testing.B) {
repo := b.TempDir()
cwd := filepath.Join(repo, "internal", "pkg")
if err := os.MkdirAll(filepath.Join(repo, ".git"), 0755); err != nil {
b.Fatal(err)
}
if err := os.MkdirAll(cwd, 0755); err != nil {
b.Fatal(err)
}
entries := make([]atuinEntry, 10000)
for i := range entries {
entryCWD := repo
if i%3 == 0 {
entryCWD = cwd
}
entries[i] = atuinEntry{Command: fmt.Sprintf("command %d", i), CWD: entryCWD}
}
b.ResetTimer()
for range b.N {
contextualAtuinCommands(entries, cwd)
}
}
func BenchmarkApplyAtuinScope(b *testing.B) {
repo := b.TempDir()
cwd := filepath.Join(repo, "internal", "pkg")
if err := os.MkdirAll(filepath.Join(repo, ".git"), 0755); err != nil {
b.Fatal(err)
}
if err := os.MkdirAll(cwd, 0755); err != nil {
b.Fatal(err)
}
originalConfig := config.Get()
originalEntries := atuinEntries
b.Cleanup(func() {
config.Init(originalConfig)
atuinEntries = originalEntries
})
cfg := config.DefaultConfig()
cfg.Core.Atuin = 1
cfg.History.Scope = "hybrid"
config.Init(cfg)
atuinEntries = make([]atuinEntry, 10000)
global := make([]HistResult, 10000)
for i := range atuinEntries {
entryCWD := repo
if i%3 == 0 {
entryCWD = cwd
}
command := fmt.Sprintf("command %d", i)
atuinEntries[i] = atuinEntry{Command: command, CWD: entryCWD}
global[i] = HistResult{Cmd: command, Source: "atuin"}
}
b.ResetTimer()
for range b.N {
applyAtuinScope(global, "command 999", nil, cwd)
}
}
func resultCommands(results []HistResult) []string {
commands := make([]string, len(results))
for i, result := range results {
commands[i] = result.Cmd
}
return commands
}