f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
1095 lines
37 KiB
Go
1095 lines
37 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestWorktreeProjectMappingsCRUDNormalizesAndScopesByMachine(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
prefix := filepath.Join(t.TempDir(), "my-app.worktrees")
|
|
m, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop",
|
|
PathPrefix: prefix + string(filepath.Separator),
|
|
Project: "my-app",
|
|
Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping")
|
|
assert.Equal(t, "laptop", m.Machine, "machine")
|
|
assert.Equal(t, prefix, m.PathPrefix, "path_prefix")
|
|
assert.Equal(t, WorktreeMappingLayoutExplicit, m.Layout, "layout")
|
|
assert.Equal(t, "my_app", m.Project, "project")
|
|
|
|
got, err := d.ListWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "list laptop mappings")
|
|
require.Len(t, got, 1, "laptop mappings")
|
|
assert.Equal(t, m.ID, got[0].ID, "laptop mapping ID")
|
|
assert.Equal(t, WorktreeMappingLayoutExplicit, got[0].Layout, "listed layout")
|
|
|
|
other, err := d.ListWorktreeProjectMappings(ctx, "server")
|
|
require.NoError(t, err, "list server mappings")
|
|
assert.Empty(t, other, "server mappings")
|
|
}
|
|
|
|
func TestWorktreeProjectMappingsRejectInvalidAndDuplicateRows(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
prefix := filepath.Join(t.TempDir(), "repo.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: " ", Project: "repo", Enabled: true,
|
|
})
|
|
require.Error(t, err, "empty path prefix accepted")
|
|
_, err = d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: " ", Enabled: true,
|
|
})
|
|
require.Error(t, err, "empty project accepted")
|
|
|
|
_, err = d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create first mapping")
|
|
_, err = d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Layout: "bogus", Project: "repo",
|
|
Enabled: true,
|
|
})
|
|
require.Error(t, err, "invalid layout accepted")
|
|
_, err = d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo2", Enabled: true,
|
|
})
|
|
require.ErrorIs(t, err, ErrWorktreeMappingDuplicate)
|
|
}
|
|
|
|
func TestWorktreeProjectMappingsLayoutResolution(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
layoutRoot := filepath.Join(root, "service")
|
|
layoutPrefix := filepath.Join(layoutRoot, "service.worktrees")
|
|
|
|
explicitMapping, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop",
|
|
PathPrefix: filepath.Join(layoutPrefix, "feature"),
|
|
Project: "feature-service",
|
|
Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create explicit mapping")
|
|
require.Equal(t, WorktreeMappingLayoutExplicit, explicitMapping.Layout)
|
|
|
|
layoutMapping, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop",
|
|
PathPrefix: layoutRoot,
|
|
Layout: WorktreeMappingLayoutRepoDotWorktrees,
|
|
Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create layout mapping")
|
|
assert.Equal(t, WorktreeMappingLayoutRepoDotWorktrees, layoutMapping.Layout)
|
|
assert.Empty(t, layoutMapping.Project, "layout rows should not persist a project")
|
|
|
|
project, ok, err := d.ResolveWorktreeProjectMapping(ctx, "laptop",
|
|
filepath.Join(layoutPrefix, "feature", "src"), "leaf")
|
|
require.NoError(t, err, "resolve explicit")
|
|
assert.True(t, ok, "explicit resolve")
|
|
assert.Equal(t, "feature_service", project, "explicit resolve")
|
|
|
|
project, ok, err = d.ResolveWorktreeProjectMapping(ctx, "laptop",
|
|
filepath.Join(layoutRoot, "service.worktrees", "main"), "leaf")
|
|
require.NoError(t, err, "resolve layout")
|
|
assert.True(t, ok, "layout resolve")
|
|
assert.Equal(t, "service", project, "layout resolve")
|
|
|
|
_, ok, err = d.ResolveWorktreeProjectMapping(ctx, "laptop",
|
|
filepath.Join(layoutRoot, "service.worktrees-other", "main"), "leaf")
|
|
require.NoError(t, err, "resolve boundary miss")
|
|
assert.False(t, ok, "shared string prefix must not match")
|
|
}
|
|
|
|
func TestResolveWorktreeProjectFromSortedMappings(t *testing.T) {
|
|
root := t.TempDir()
|
|
layoutRoot := filepath.Join(root, "service")
|
|
layoutPrefix := filepath.Join(layoutRoot, "service.worktrees")
|
|
cwd := filepath.Join(layoutPrefix, "feature", "src")
|
|
|
|
mappings := []WorktreeProjectMapping{
|
|
{
|
|
PathPrefix: filepath.Join(layoutPrefix, "feature"),
|
|
Layout: WorktreeMappingLayoutExplicit,
|
|
Project: "feature-service",
|
|
},
|
|
{
|
|
PathPrefix: layoutRoot,
|
|
Layout: WorktreeMappingLayoutRepoDotWorktrees,
|
|
},
|
|
}
|
|
|
|
project, ok := ResolveWorktreeProjectFromSortedMappings(mappings, cwd, "leaf")
|
|
assert.True(t, ok, "longest prefix resolve")
|
|
assert.Equal(t, "feature-service", project, "longest prefix resolve")
|
|
|
|
project, ok = ResolveWorktreeProjectFromMappings([]WorktreeProjectMapping{
|
|
{
|
|
PathPrefix: layoutRoot,
|
|
Layout: WorktreeMappingLayoutRepoDotWorktrees,
|
|
},
|
|
{
|
|
PathPrefix: filepath.Join(layoutPrefix, "feature"),
|
|
Layout: WorktreeMappingLayoutExplicit,
|
|
Project: "feature-service",
|
|
},
|
|
}, cwd, "leaf")
|
|
assert.True(t, ok, "unsorted resolve")
|
|
assert.Equal(t, "feature-service", project, "unsorted resolve")
|
|
|
|
project, ok = ResolveWorktreeProjectFromSortedMappings([]WorktreeProjectMapping{
|
|
{
|
|
PathPrefix: layoutRoot,
|
|
Layout: WorktreeMappingLayoutRepoDotWorktrees,
|
|
},
|
|
}, filepath.Join(layoutRoot, "service.worktrees", "main"), "leaf")
|
|
assert.True(t, ok, "layout resolve")
|
|
assert.Equal(t, "service", project, "layout resolve")
|
|
|
|
project, ok = ResolveWorktreeProjectFromSortedMappings([]WorktreeProjectMapping{
|
|
{
|
|
PathPrefix: layoutRoot,
|
|
Layout: WorktreeMappingLayoutRepoDotWorktrees,
|
|
},
|
|
}, filepath.Join(layoutRoot, "service.worktrees"), "leaf")
|
|
assert.False(t, ok, "branchless layout root should not resolve")
|
|
assert.Equal(t, "leaf", project, "branchless layout root project")
|
|
|
|
project, ok = ResolveWorktreeProjectFromSortedMappings([]WorktreeProjectMapping{
|
|
{
|
|
PathPrefix: layoutRoot,
|
|
Layout: WorktreeMappingLayoutRepoDotWorktrees,
|
|
},
|
|
{
|
|
PathPrefix: root,
|
|
Layout: WorktreeMappingLayoutExplicit,
|
|
Project: "root-fallback",
|
|
},
|
|
}, filepath.Join(layoutRoot, "plain-worktree", "src"), "leaf")
|
|
assert.True(t, ok, "fallback resolve")
|
|
assert.Equal(t, "root-fallback", project, "fallback resolve")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappingsLayout(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
layoutRoot := filepath.Join(root, "service")
|
|
layoutPrefix := filepath.Join(layoutRoot, "service.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop",
|
|
PathPrefix: layoutRoot,
|
|
Layout: WorktreeMappingLayoutRepoDotWorktrees,
|
|
Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create layout mapping")
|
|
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "bulk",
|
|
Project: "leaf",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(layoutPrefix, "feature"),
|
|
}), "insert bulk session")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "apply mappings")
|
|
assert.Equal(t, 1, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 1, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "bulk", "service")
|
|
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "single",
|
|
Project: "leaf",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(layoutPrefix, "single"),
|
|
}), "insert single session")
|
|
updated, err := d.ApplyWorktreeProjectMappingToSession(
|
|
ctx, "laptop", "single", filepath.Join(layoutPrefix, "single"), "leaf",
|
|
)
|
|
require.NoError(t, err, "ApplyWorktreeProjectMappingToSession")
|
|
assert.True(t, updated, "single session updated")
|
|
assertSessionProject(t, d, "single", "service")
|
|
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "path",
|
|
Project: "leaf",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(layoutPrefix, "path"),
|
|
FilePath: &filePath,
|
|
}), "insert path session")
|
|
result, err = d.ApplyWorktreeProjectMappingsToSessionsByPath(ctx, filePath)
|
|
require.NoError(t, err, "apply mappings by path")
|
|
assert.Equal(t, 1, result.MatchedSessions, "matched sessions by path")
|
|
assert.Equal(t, 1, result.UpdatedSessions, "updated sessions by path")
|
|
assertSessionProject(t, d, "path", "service")
|
|
}
|
|
|
|
func TestWorktreeMappingEmptyCWD(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
layoutRoot := filepath.Join(root, "service")
|
|
layoutPrefix := filepath.Join(layoutRoot, "service.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop",
|
|
PathPrefix: layoutRoot,
|
|
Layout: WorktreeMappingLayoutRepoDotWorktrees,
|
|
Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create layout mapping")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd row")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(layoutPrefix, "feature"),
|
|
FilePath: &filePath,
|
|
}), "insert reference row")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "apply mappings")
|
|
assert.Equal(t, 2, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 2, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd", "service")
|
|
assertSessionProject(t, d, "reference", "service")
|
|
}
|
|
|
|
func TestResolveWorktreeProjectMappingUsesLongestPrefixAndBoundaries(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
broad := filepath.Join(root, "repo.worktrees")
|
|
nested := filepath.Join(broad, "special")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: broad, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create broad mapping")
|
|
_, err = d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: nested, Project: "special-repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create nested mapping")
|
|
|
|
project, ok, err := d.ResolveWorktreeProjectMapping(ctx, "laptop",
|
|
filepath.Join(nested, "feat", "thing"), "leaf")
|
|
require.NoError(t, err, "resolve nested")
|
|
assert.True(t, ok, "nested resolve")
|
|
assert.Equal(t, "special_repo", project, "nested resolve")
|
|
|
|
project, ok, err = d.ResolveWorktreeProjectMapping(ctx, "laptop",
|
|
filepath.Join(broad, "feat", "thing"), "leaf")
|
|
require.NoError(t, err, "resolve broad")
|
|
assert.True(t, ok, "broad resolve")
|
|
assert.Equal(t, "repo", project, "broad resolve")
|
|
|
|
_, ok, err = d.ResolveWorktreeProjectMapping(ctx, "laptop", broad+"-other", "leaf")
|
|
require.NoError(t, err, "resolve boundary miss")
|
|
assert.False(t, ok,
|
|
"path with shared string prefix matched across component boundary")
|
|
|
|
project, ok = ResolveWorktreeProjectFromMappings(
|
|
[]WorktreeProjectMapping{
|
|
{PathPrefix: broad, Project: "repo"},
|
|
{PathPrefix: nested, Project: "special_repo"},
|
|
},
|
|
filepath.Join(nested, "feat", "thing"),
|
|
"leaf",
|
|
)
|
|
assert.True(t, ok, "unsorted resolve")
|
|
assert.Equal(t, "special_repo", project, "unsorted resolve")
|
|
}
|
|
|
|
func TestResolveWorktreeProjectMappingMatchesRootPrefix(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop",
|
|
PathPrefix: string(filepath.Separator),
|
|
Project: "root-project",
|
|
Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create root mapping")
|
|
|
|
project, ok, err := d.ResolveWorktreeProjectMapping(ctx, "laptop",
|
|
filepath.Join(string(filepath.Separator), "tmp", "worktree"), "leaf")
|
|
require.NoError(t, err, "resolve root")
|
|
assert.True(t, ok, "root resolve")
|
|
assert.Equal(t, "root_project", project, "root resolve")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappingsUpdatesOnlyCurrentMachineAndEnabledRows(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
prefix := filepath.Join(root, "repo.worktrees")
|
|
disabledPrefix := filepath.Join(root, "disabled.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create enabled mapping")
|
|
_, err = d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: disabledPrefix, Project: "disabled", Enabled: false,
|
|
})
|
|
require.NoError(t, err, "create disabled mapping")
|
|
|
|
insert := func(id, machine, project, cwd string) {
|
|
t.Helper()
|
|
err := d.UpsertSession(Session{
|
|
ID: id, Project: project, Machine: machine, Agent: "claude", Cwd: cwd,
|
|
})
|
|
require.NoError(t, err, "insert %s", id)
|
|
}
|
|
insert("match", "laptop", "leaf", filepath.Join(prefix, "feat", "thing"))
|
|
insert("same-project", "laptop", "repo", filepath.Join(prefix, "bugfix"))
|
|
insert("other-machine", "server", "leaf", filepath.Join(prefix, "feat", "thing"))
|
|
insert("disabled", "laptop", "leaf", filepath.Join(disabledPrefix, "feat"))
|
|
insert("trashed", "laptop", "leaf", filepath.Join(prefix, "trashed"))
|
|
require.NoError(t, d.SoftDeleteSession("trashed"), "trash session")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "apply mappings")
|
|
assert.Equal(t, 2, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 1, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "match", "repo")
|
|
assertSessionProject(t, d, "same-project", "repo")
|
|
assertSessionProject(t, d, "other-machine", "leaf")
|
|
assertSessionProject(t, d, "disabled", "leaf")
|
|
assertFullSessionProject(t, d, "trashed", "leaf")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappingsBumpsLocalModifiedAt(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
prefix := filepath.Join(t.TempDir(), "repo.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "match", Project: "leaf", Machine: "laptop", Agent: "claude",
|
|
Cwd: filepath.Join(prefix, "feat"),
|
|
}), "insert match")
|
|
|
|
before, err := d.GetSessionFull(ctx, "match")
|
|
require.NoError(t, err, "GetSessionFull before")
|
|
require.Nil(t, before.LocalModifiedAt, "local_modified_at before")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "apply mappings")
|
|
require.Equal(t, 1, result.UpdatedSessions, "updated sessions")
|
|
|
|
after, err := d.GetSessionFull(ctx, "match")
|
|
require.NoError(t, err, "GetSessionFull after")
|
|
assert.Equal(t, "repo", after.Project, "project")
|
|
require.NotNil(t, after.LocalModifiedAt, "local_modified_at after")
|
|
assert.NotEmpty(t, *after.LocalModifiedAt, "local_modified_at after")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappings_EmptyCwdSiblingFallback(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
prefix := filepath.Join(root, "repo.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd row")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefix, "feat", "task"),
|
|
FilePath: &filePath,
|
|
}), "insert fallback reference row")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "apply mappings")
|
|
assert.Equal(t, 2, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 2, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd", "repo")
|
|
assertSessionProject(t, d, "reference", "repo")
|
|
|
|
emptyRow, err := d.GetSession(ctx, "empty-cwd")
|
|
require.NoError(t, err, "GetSession empty-cwd")
|
|
require.NotNil(t, emptyRow, "empty-cwd session")
|
|
assert.Equal(t, "", emptyRow.Cwd, "stored cwd unchanged")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappings_EmptyCwdConflictingSiblingsNoFallback(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
prefixA := filepath.Join(root, "repo-a.worktrees")
|
|
prefixB := filepath.Join(root, "repo-b.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefixA, Project: "repo-a", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping A")
|
|
_, err = d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefixB, Project: "repo-b", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping B")
|
|
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd row")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference-a",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefixA, "feat"),
|
|
FilePath: &filePath,
|
|
}), "insert reference row resolving to mapping A")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference-b",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefixB, "feat"),
|
|
FilePath: &filePath,
|
|
}), "insert reference row resolving to mapping B")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "apply mappings")
|
|
assert.Equal(t, 2, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 2, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd", "stale")
|
|
assertSessionProject(t, d, "reference-a", "repo_a")
|
|
assertSessionProject(t, d, "reference-b", "repo_b")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappings_EmptyCwdUnmappedSiblingNoFallback(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
prefixA := filepath.Join(root, "repo-a.worktrees")
|
|
unmappedCwd := filepath.Join(root, "unrelated", "dir")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefixA, Project: "repo-a", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping A")
|
|
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd row")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference-mapped",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefixA, "feat"),
|
|
FilePath: &filePath,
|
|
}), "insert reference row resolving to mapping A")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference-unmapped",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: unmappedCwd,
|
|
FilePath: &filePath,
|
|
}), "insert reference row with no matching mapping")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "apply mappings")
|
|
assert.Equal(t, 1, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 1, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd", "stale")
|
|
assertSessionProject(t, d, "reference-mapped", "repo_a")
|
|
assertSessionProject(t, d, "reference-unmapped", "stale")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappings_EmptyCwdNoSiblingNoUpdate(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
prefix := filepath.Join(root, "repo.worktrees")
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
otherFilePath := filepath.Join(root, "other-session.jsonl")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd-no-sibling",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd row")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference-unrelated",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefix, "feat"),
|
|
FilePath: &otherFilePath,
|
|
}), "insert unrelated non-empty row")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "apply mappings")
|
|
assert.Equal(t, 1, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 1, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd-no-sibling", "stale")
|
|
assertSessionProject(t, d, "reference-unrelated", "repo")
|
|
|
|
emptyRow, err := d.GetSession(ctx, "empty-cwd-no-sibling")
|
|
require.NoError(t, err, "GetSession empty-cwd-no-sibling")
|
|
require.NotNil(t, emptyRow, "empty-cwd-no-sibling session")
|
|
assert.Equal(t, "", emptyRow.Cwd, "stored cwd unchanged")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappings_EmptyCwdSameProjectNoUpdate(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
prefix := filepath.Join(root, "repo.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd-already-matched",
|
|
Project: "repo",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd same project row")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefix, "feat"),
|
|
FilePath: &filePath,
|
|
}), "insert reference row")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "apply mappings")
|
|
assert.Equal(t, 2, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 1, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd-already-matched", "repo")
|
|
assertSessionProject(t, d, "reference", "repo")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappingsToSessionsByPath_EmptyCwdSiblingFallback(
|
|
t *testing.T,
|
|
) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
prefix := filepath.Join(root, "repo.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd row")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefix, "feat"),
|
|
FilePath: &filePath,
|
|
}), "insert reference row")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappingsToSessionsByPath(ctx, filePath)
|
|
require.NoError(t, err, "apply mappings by path")
|
|
assert.Equal(t, 2, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 2, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd", "repo")
|
|
assertSessionProject(t, d, "reference", "repo")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappingsToSessionsByPath_EmptyCwdConflictingSiblingsNoFallback(
|
|
t *testing.T,
|
|
) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
prefixA := filepath.Join(root, "repo-a.worktrees")
|
|
prefixB := filepath.Join(root, "repo-b.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefixA, Project: "repo-a", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping A")
|
|
_, err = d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefixB, Project: "repo-b", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping B")
|
|
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd row")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference-a",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefixA, "feat"),
|
|
FilePath: &filePath,
|
|
}), "insert reference row resolving to mapping A")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference-b",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefixB, "feat"),
|
|
FilePath: &filePath,
|
|
}), "insert reference row resolving to mapping B")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappingsToSessionsByPath(ctx, filePath)
|
|
require.NoError(t, err, "apply mappings by path")
|
|
assert.Equal(t, 2, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 2, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd", "stale")
|
|
assertSessionProject(t, d, "reference-a", "repo_a")
|
|
assertSessionProject(t, d, "reference-b", "repo_b")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappingsToSessionsByPath_EmptyCwdUnmappedSiblingNoFallback(
|
|
t *testing.T,
|
|
) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
prefixA := filepath.Join(root, "repo-a.worktrees")
|
|
unmappedCwd := filepath.Join(root, "unrelated", "dir")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefixA, Project: "repo-a", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping A")
|
|
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd row")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference-mapped",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: filepath.Join(prefixA, "feat"),
|
|
FilePath: &filePath,
|
|
}), "insert reference row resolving to mapping A")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "reference-unmapped",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: unmappedCwd,
|
|
FilePath: &filePath,
|
|
}), "insert reference row with no matching mapping")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappingsToSessionsByPath(ctx, filePath)
|
|
require.NoError(t, err, "apply mappings by path")
|
|
assert.Equal(t, 1, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 1, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd", "stale")
|
|
assertSessionProject(t, d, "reference-mapped", "repo_a")
|
|
assertSessionProject(t, d, "reference-unmapped", "stale")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappingsToSessionsByPath_EmptyCwdOnlyEmptySiblingsNoMatch(
|
|
t *testing.T,
|
|
) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
filePath := filepath.Join(root, "session.jsonl")
|
|
prefix := filepath.Join(root, "repo.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd-a",
|
|
Project: "stale",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd sibling A")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "empty-cwd-b",
|
|
Project: "other",
|
|
Machine: "laptop",
|
|
Agent: "claude",
|
|
Cwd: "",
|
|
FilePath: &filePath,
|
|
}), "insert empty cwd sibling B")
|
|
|
|
result, err := d.ApplyWorktreeProjectMappingsToSessionsByPath(ctx, filePath)
|
|
require.NoError(t, err, "apply mappings by path")
|
|
assert.Equal(t, 0, result.MatchedSessions, "matched sessions")
|
|
assert.Equal(t, 0, result.UpdatedSessions, "updated sessions")
|
|
assertSessionProject(t, d, "empty-cwd-a", "stale")
|
|
assertSessionProject(t, d, "empty-cwd-b", "other")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappingsToSessionUsesCurrentSessionState(
|
|
t *testing.T,
|
|
) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
root := t.TempDir()
|
|
stalePrefix := filepath.Join(root, "stale.worktrees")
|
|
currentPrefix := filepath.Join(root, "current.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: stalePrefix, Project: "stale-repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create stale mapping")
|
|
_, err = d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: currentPrefix, Project: "current-repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create current mapping")
|
|
|
|
staleCwd := filepath.Join(stalePrefix, "feat")
|
|
currentCwd := filepath.Join(currentPrefix, "feat")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "match", Project: "leaf", Machine: "laptop", Agent: "claude",
|
|
Cwd: staleCwd,
|
|
}), "insert stale match")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "match", Project: "other_leaf", Machine: "laptop", Agent: "claude",
|
|
Cwd: currentCwd,
|
|
}), "move session before apply")
|
|
|
|
updated, err := d.ApplyWorktreeProjectMappingToSession(
|
|
ctx, "laptop", "match", staleCwd, "leaf",
|
|
)
|
|
require.NoError(t, err, "ApplyWorktreeProjectMappingToSession")
|
|
require.True(t, updated, "updated")
|
|
assertSessionProject(t, d, "match", "current_repo")
|
|
}
|
|
|
|
func TestApplyWorktreeProjectMappingToSessionFromSyncDoesNotBumpLocalModifiedAt(
|
|
t *testing.T,
|
|
) {
|
|
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
prefix := filepath.Join(t.TempDir(), "repo.worktrees")
|
|
|
|
_, err := d.CreateWorktreeProjectMapping(ctx, WorktreeProjectMapping{
|
|
Machine: "laptop", PathPrefix: prefix, Project: "repo", Enabled: true,
|
|
})
|
|
require.NoError(t, err, "create mapping")
|
|
require.NoError(t, d.UpsertSession(Session{
|
|
ID: "match", Project: "leaf", Machine: "laptop", Agent: "claude",
|
|
Cwd: filepath.Join(prefix, "feat"),
|
|
}), "insert match")
|
|
|
|
before, err := d.GetSessionFull(ctx, "match")
|
|
require.NoError(t, err, "GetSessionFull before")
|
|
require.Nil(t, before.LocalModifiedAt, "local_modified_at before")
|
|
|
|
updated, err := d.ApplyWorktreeProjectMappingToSessionFromSync(
|
|
ctx, "laptop", "match", before.Cwd, before.Project,
|
|
)
|
|
require.NoError(t, err, "ApplyWorktreeProjectMappingToSessionFromSync")
|
|
require.True(t, updated, "updated")
|
|
|
|
after, err := d.GetSessionFull(ctx, "match")
|
|
require.NoError(t, err, "GetSessionFull after")
|
|
assert.Equal(t, "repo", after.Project, "project")
|
|
assert.Nil(t, after.LocalModifiedAt, "local_modified_at after")
|
|
}
|
|
|
|
func assertSessionProject(t *testing.T, d *DB, id, want string) {
|
|
t.Helper()
|
|
got, err := d.GetSession(context.Background(), id)
|
|
require.NoError(t, err, "GetSession %s", id)
|
|
assert.Equal(t, want, got.Project, "session %s project", id)
|
|
}
|
|
|
|
func TestWorktreeProjectMappingsFinalMetadataCopyRefreshesStalePrecopy(
|
|
t *testing.T,
|
|
) {
|
|
dir := t.TempDir()
|
|
ctx := context.Background()
|
|
|
|
srcPath := filepath.Join(dir, "src.db")
|
|
srcDB, err := Open(srcPath)
|
|
require.NoError(t, err, "Open src")
|
|
defer srcDB.Close()
|
|
|
|
prefix := filepath.Join(dir, "app.worktrees")
|
|
sourceMapping, err := srcDB.CreateWorktreeProjectMapping(
|
|
ctx,
|
|
WorktreeProjectMapping{
|
|
Machine: "laptop",
|
|
PathPrefix: prefix,
|
|
Project: "old-project",
|
|
Enabled: true,
|
|
},
|
|
)
|
|
require.NoError(t, err, "CreateWorktreeProjectMapping src")
|
|
|
|
dstPath := filepath.Join(dir, "dst.db")
|
|
dstDB, err := Open(dstPath)
|
|
require.NoError(t, err, "Open dst")
|
|
defer dstDB.Close()
|
|
|
|
require.NoError(
|
|
t,
|
|
dstDB.CopyWorktreeProjectMappingsFrom(srcPath),
|
|
"CopyWorktreeProjectMappingsFrom",
|
|
)
|
|
|
|
_, err = srcDB.UpdateWorktreeProjectMapping(
|
|
ctx,
|
|
"laptop",
|
|
sourceMapping.ID,
|
|
WorktreeProjectMapping{
|
|
PathPrefix: prefix,
|
|
Project: "new-project",
|
|
Enabled: false,
|
|
},
|
|
)
|
|
require.NoError(t, err, "UpdateWorktreeProjectMapping src")
|
|
require.NoError(t, srcDB.CloseConnections(), "CloseConnections src")
|
|
|
|
_, err = dstDB.getWriter().ExecContext(ctx, `
|
|
UPDATE worktree_project_mappings
|
|
SET updated_at = '9999-12-31T23:59:59.999Z'
|
|
WHERE machine = ? AND path_prefix = ?`,
|
|
"laptop",
|
|
prefix,
|
|
)
|
|
require.NoError(t, err, "force dst updated_at ahead")
|
|
|
|
require.NoError(
|
|
t,
|
|
dstDB.CopySessionMetadataFrom(srcPath),
|
|
"CopySessionMetadataFrom",
|
|
)
|
|
|
|
got, err := dstDB.ListWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "ListWorktreeProjectMappings")
|
|
require.Len(t, got, 1, "mapping count")
|
|
assert.Equal(t, "new_project", got[0].Project, "project")
|
|
assert.False(t, got[0].Enabled, "mapping should reflect disabled source row")
|
|
}
|
|
|
|
func TestWorktreeProjectMappingsFinalMetadataCopyRemovesDeletedPrecopy(
|
|
t *testing.T,
|
|
) {
|
|
dir := t.TempDir()
|
|
ctx := context.Background()
|
|
|
|
srcPath := filepath.Join(dir, "src.db")
|
|
srcDB, err := Open(srcPath)
|
|
require.NoError(t, err, "Open src")
|
|
defer srcDB.Close()
|
|
|
|
prefix := filepath.Join(dir, "app.worktrees")
|
|
sourceMapping, err := srcDB.CreateWorktreeProjectMapping(
|
|
ctx,
|
|
WorktreeProjectMapping{
|
|
Machine: "laptop",
|
|
PathPrefix: prefix,
|
|
Project: "old-project",
|
|
Enabled: true,
|
|
},
|
|
)
|
|
require.NoError(t, err, "CreateWorktreeProjectMapping src")
|
|
|
|
dstPath := filepath.Join(dir, "dst.db")
|
|
dstDB, err := Open(dstPath)
|
|
require.NoError(t, err, "Open dst")
|
|
defer dstDB.Close()
|
|
|
|
require.NoError(
|
|
t,
|
|
dstDB.CopyWorktreeProjectMappingsFrom(srcPath),
|
|
"CopyWorktreeProjectMappingsFrom",
|
|
)
|
|
|
|
require.NoError(
|
|
t,
|
|
srcDB.DeleteWorktreeProjectMapping(
|
|
ctx, "laptop", sourceMapping.ID,
|
|
),
|
|
"DeleteWorktreeProjectMapping src",
|
|
)
|
|
require.NoError(t, srcDB.CloseConnections(), "CloseConnections src")
|
|
|
|
require.NoError(
|
|
t,
|
|
dstDB.CopySessionMetadataFrom(srcPath),
|
|
"CopySessionMetadataFrom",
|
|
)
|
|
|
|
got, err := dstDB.ListWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "ListWorktreeProjectMappings")
|
|
assert.Empty(t, got, "mapping count")
|
|
}
|
|
|
|
func TestCopyWorktreeProjectMappingsFromOldSchemaDefaultsLayout(t *testing.T) {
|
|
dir := t.TempDir()
|
|
ctx := context.Background()
|
|
srcPath := filepath.Join(dir, "old-src.db")
|
|
prefix := filepath.Join(dir, "app.worktrees")
|
|
createOldWorktreeMappingDB(t, srcPath, prefix)
|
|
|
|
dstPath := filepath.Join(dir, "dst.db")
|
|
dstDB, err := Open(dstPath)
|
|
require.NoError(t, err, "Open dst")
|
|
defer dstDB.Close()
|
|
|
|
require.NoError(
|
|
t,
|
|
dstDB.CopyWorktreeProjectMappingsFrom(srcPath),
|
|
"CopyWorktreeProjectMappingsFrom",
|
|
)
|
|
|
|
got, err := dstDB.ListWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "ListWorktreeProjectMappings")
|
|
require.Len(t, got, 1, "mapping count")
|
|
assert.Equal(t, WorktreeMappingLayoutExplicit, got[0].Layout, "layout")
|
|
assert.Equal(t, "old_project", got[0].Project, "project")
|
|
assert.True(t, got[0].Enabled, "enabled")
|
|
}
|
|
|
|
func TestCopySessionMetadataFromOldWorktreeMappingSchemaDefaultsLayout(t *testing.T) {
|
|
dir := t.TempDir()
|
|
ctx := context.Background()
|
|
srcPath := filepath.Join(dir, "old-src.db")
|
|
prefix := filepath.Join(dir, "app.worktrees")
|
|
createOldWorktreeMappingDB(t, srcPath, prefix)
|
|
|
|
dstPath := filepath.Join(dir, "dst.db")
|
|
dstDB, err := Open(dstPath)
|
|
require.NoError(t, err, "Open dst")
|
|
defer dstDB.Close()
|
|
|
|
require.NoError(
|
|
t,
|
|
dstDB.CopySessionMetadataFrom(srcPath),
|
|
"CopySessionMetadataFrom",
|
|
)
|
|
|
|
got, err := dstDB.ListWorktreeProjectMappings(ctx, "laptop")
|
|
require.NoError(t, err, "ListWorktreeProjectMappings")
|
|
require.Len(t, got, 1, "mapping count")
|
|
assert.Equal(t, WorktreeMappingLayoutExplicit, got[0].Layout, "layout")
|
|
assert.Equal(t, "old_project", got[0].Project, "project")
|
|
assert.True(t, got[0].Enabled, "enabled")
|
|
}
|
|
|
|
func createOldWorktreeMappingDB(t *testing.T, path string, prefix string) {
|
|
t.Helper()
|
|
conn, err := sql.Open("sqlite3", path)
|
|
require.NoError(t, err, "open old sqlite")
|
|
defer conn.Close()
|
|
_, err = conn.Exec(`
|
|
CREATE TABLE worktree_project_mappings (
|
|
id INTEGER PRIMARY KEY,
|
|
machine TEXT NOT NULL,
|
|
path_prefix TEXT NOT NULL,
|
|
project TEXT NOT NULL,
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
UNIQUE(machine, path_prefix)
|
|
);
|
|
INSERT INTO worktree_project_mappings (
|
|
machine, path_prefix, project, enabled
|
|
) VALUES (
|
|
'laptop', ?, 'old_project', 1
|
|
);`,
|
|
prefix,
|
|
)
|
|
require.NoError(t, err, "seed old worktree mapping db")
|
|
}
|
|
|
|
func assertFullSessionProject(t *testing.T, d *DB, id, want string) {
|
|
t.Helper()
|
|
got, err := d.GetSessionFull(context.Background(), id)
|
|
require.NoError(t, err, "GetSessionFull %s", id)
|
|
assert.Equal(t, want, got.Project, "session %s project", id)
|
|
}
|