Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

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())
}