feat(history): add Atuin context scopes
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user