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
237 lines
6.7 KiB
Go
237 lines
6.7 KiB
Go
package server_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.kenn.io/agentsview/internal/db"
|
|
)
|
|
|
|
func TestWorktreeMappingsAPIUsesCurrentMachine(t *testing.T) {
|
|
te := setup(t)
|
|
prefix := filepath.Join(t.TempDir(), "app.worktrees")
|
|
|
|
created := postWorktreeMapping(t, te, map[string]any{
|
|
"path_prefix": prefix,
|
|
"project": "canonical-app",
|
|
"machine": "other-machine",
|
|
})
|
|
require.Equal(t, "test", created.Machine)
|
|
require.Equal(t, db.WorktreeMappingLayoutExplicit, created.Layout)
|
|
require.Equal(t, "canonical_app", created.Project)
|
|
require.True(t, created.Enabled, "created mapping should default enabled")
|
|
|
|
var list struct {
|
|
Machine string `json:"machine"`
|
|
Mappings []db.WorktreeProjectMapping `json:"mappings"`
|
|
}
|
|
w := te.get(t, "/api/v1/settings/worktree-mappings")
|
|
assertStatus(t, w, http.StatusOK)
|
|
decodeInto(t, w, &list)
|
|
require.Equal(t, "test", list.Machine)
|
|
require.Len(t, list.Mappings, 1)
|
|
|
|
updated := putWorktreeMapping(t, te, created.ID, map[string]any{
|
|
"path_prefix": prefix,
|
|
"project": "disabled-app",
|
|
"enabled": false,
|
|
})
|
|
assert.False(t, updated.Enabled, "updated mapping should be disabled")
|
|
assert.Equal(t, db.WorktreeMappingLayoutExplicit, updated.Layout)
|
|
assert.Equal(t, "disabled_app", updated.Project)
|
|
|
|
req := httptest.NewRequest(
|
|
http.MethodDelete,
|
|
"/api/v1/settings/worktree-mappings/"+
|
|
strconv.FormatInt(created.ID, 10),
|
|
nil,
|
|
)
|
|
req.Header.Set("Origin", "http://127.0.0.1:0")
|
|
delW := httptest.NewRecorder()
|
|
te.handler.ServeHTTP(delW, req)
|
|
assertStatus(t, delW, http.StatusNoContent)
|
|
|
|
w = te.get(t, "/api/v1/settings/worktree-mappings")
|
|
assertStatus(t, w, http.StatusOK)
|
|
decodeInto(t, w, &list)
|
|
assert.Empty(t, list.Mappings, "mappings after delete should be empty")
|
|
}
|
|
|
|
func TestWorktreeMappingsAPIHandlesLayouts(t *testing.T) {
|
|
te := setup(t)
|
|
root := t.TempDir()
|
|
layoutPrefix := filepath.Join(root, "service")
|
|
layoutRoot := filepath.Join(layoutPrefix, "service.worktrees")
|
|
|
|
created := postWorktreeMapping(t, te, map[string]any{
|
|
"path_prefix": layoutPrefix,
|
|
"layout": db.WorktreeMappingLayoutRepoDotWorktrees,
|
|
})
|
|
require.Equal(t, db.WorktreeMappingLayoutRepoDotWorktrees, created.Layout)
|
|
require.Empty(t, created.Project)
|
|
require.True(t, created.Enabled, "created mapping should default enabled")
|
|
|
|
var list struct {
|
|
Machine string `json:"machine"`
|
|
Mappings []db.WorktreeProjectMapping `json:"mappings"`
|
|
}
|
|
w := te.get(t, "/api/v1/settings/worktree-mappings")
|
|
assertStatus(t, w, http.StatusOK)
|
|
decodeInto(t, w, &list)
|
|
require.Equal(t, "test", list.Machine)
|
|
require.Len(t, list.Mappings, 1)
|
|
assert.Equal(t, db.WorktreeMappingLayoutRepoDotWorktrees, list.Mappings[0].Layout)
|
|
assert.Empty(t, list.Mappings[0].Project)
|
|
|
|
updated := putWorktreeMapping(t, te, created.ID, map[string]any{
|
|
"path_prefix": layoutPrefix,
|
|
"layout": db.WorktreeMappingLayoutRepoDotWorktrees,
|
|
"enabled": false,
|
|
})
|
|
assert.False(t, updated.Enabled, "updated mapping should be disabled")
|
|
assert.Equal(t, db.WorktreeMappingLayoutRepoDotWorktrees, updated.Layout)
|
|
assert.Empty(t, updated.Project)
|
|
|
|
w = postRawWorktreeMapping(t, te, map[string]any{
|
|
"path_prefix": layoutRoot,
|
|
"layout": "bogus",
|
|
})
|
|
assertStatus(t, w, http.StatusBadRequest)
|
|
|
|
w = postRawWorktreeMapping(t, te, map[string]any{
|
|
"path_prefix": layoutRoot,
|
|
"layout": db.WorktreeMappingLayoutExplicit,
|
|
})
|
|
assertStatus(t, w, http.StatusBadRequest)
|
|
}
|
|
|
|
func TestWorktreeMappingsAPIApply(t *testing.T) {
|
|
te := setup(t)
|
|
prefix := filepath.Join(t.TempDir(), "app.worktrees")
|
|
_ = postWorktreeMapping(t, te, map[string]any{
|
|
"path_prefix": prefix,
|
|
"project": "canonical-app",
|
|
})
|
|
require.NoError(t, te.db.UpsertSession(db.Session{
|
|
ID: "s1",
|
|
Machine: "test",
|
|
Agent: "claude",
|
|
Project: "feature_login",
|
|
Cwd: filepath.Join(prefix, "feature-login"),
|
|
}))
|
|
|
|
w := te.post(t, "/api/v1/settings/worktree-mappings/apply", `{}`)
|
|
assertStatus(t, w, http.StatusOK)
|
|
var resp struct {
|
|
Machine string `json:"machine"`
|
|
MatchedSessions int `json:"matched_sessions"`
|
|
UpdatedSessions int `json:"updated_sessions"`
|
|
}
|
|
decodeInto(t, w, &resp)
|
|
assert.Equal(t, "test", resp.Machine)
|
|
assert.Equal(t, 1, resp.MatchedSessions)
|
|
assert.Equal(t, 1, resp.UpdatedSessions)
|
|
sess, err := te.db.GetSession(context.Background(), "s1")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "canonical_app", sess.Project)
|
|
}
|
|
|
|
func TestWorktreeMappingsAPIRejectsRemoteMode(t *testing.T) {
|
|
te := setupPGMode(t)
|
|
w := te.get(t, "/api/v1/settings/worktree-mappings")
|
|
assertStatus(t, w, http.StatusNotImplemented)
|
|
|
|
w = te.post(t, "/api/v1/settings/worktree-mappings", `{
|
|
"path_prefix": "/tmp/app.worktrees",
|
|
"project": "app"
|
|
}`)
|
|
assertStatus(t, w, http.StatusNotImplemented)
|
|
|
|
w = te.post(t, "/api/v1/settings/worktree-mappings/apply", `{}`)
|
|
assertStatus(t, w, http.StatusNotImplemented)
|
|
}
|
|
|
|
func TestWorktreeMappingsAPIMalformedIDIsNotFound(t *testing.T) {
|
|
te := setup(t)
|
|
req := httptest.NewRequest(
|
|
http.MethodPut,
|
|
"/api/v1/settings/worktree-mappings/apply",
|
|
bytes.NewReader([]byte(`{}`)),
|
|
)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Origin", "http://127.0.0.1:0")
|
|
w := httptest.NewRecorder()
|
|
te.handler.ServeHTTP(w, req)
|
|
assertStatus(t, w, http.StatusNotFound)
|
|
}
|
|
|
|
func postWorktreeMapping(
|
|
t *testing.T,
|
|
te *testEnv,
|
|
body map[string]any,
|
|
) db.WorktreeProjectMapping {
|
|
t.Helper()
|
|
data, err := json.Marshal(body)
|
|
require.NoError(t, err)
|
|
w := te.post(
|
|
t,
|
|
"/api/v1/settings/worktree-mappings",
|
|
string(data),
|
|
)
|
|
assertStatus(t, w, http.StatusCreated)
|
|
return decode[db.WorktreeProjectMapping](t, w)
|
|
}
|
|
|
|
func putWorktreeMapping(
|
|
t *testing.T,
|
|
te *testEnv,
|
|
id int64,
|
|
body map[string]any,
|
|
) db.WorktreeProjectMapping {
|
|
t.Helper()
|
|
data, err := json.Marshal(body)
|
|
require.NoError(t, err)
|
|
req := httptest.NewRequest(
|
|
http.MethodPut,
|
|
"/api/v1/settings/worktree-mappings/"+
|
|
strconv.FormatInt(id, 10),
|
|
bytes.NewReader(data),
|
|
)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Origin", "http://127.0.0.1:0")
|
|
w := httptest.NewRecorder()
|
|
te.handler.ServeHTTP(w, req)
|
|
assertStatus(t, w, http.StatusOK)
|
|
return decode[db.WorktreeProjectMapping](t, w)
|
|
}
|
|
|
|
func postRawWorktreeMapping(
|
|
t *testing.T,
|
|
te *testEnv,
|
|
body map[string]any,
|
|
) *httptest.ResponseRecorder {
|
|
t.Helper()
|
|
data, err := json.Marshal(body)
|
|
require.NoError(t, err)
|
|
return te.post(t, "/api/v1/settings/worktree-mappings", string(data))
|
|
}
|
|
|
|
func decodeInto(
|
|
t *testing.T,
|
|
w *httptest.ResponseRecorder,
|
|
target any,
|
|
) {
|
|
t.Helper()
|
|
require.NoError(t, json.Unmarshal(w.Body.Bytes(), target),
|
|
"decoding JSON; body: %s", w.Body.String())
|
|
}
|