mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-08 17:36:45 +08:00
* fix(runtimes): revoke access when making runtime private * fix(runtimes): regenerate queries for workflow step --------- Co-authored-by: m2keloop <[email protected]>
97 lines
3.3 KiB
Go
97 lines
3.3 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/multica-ai/multica/server/internal/testutil"
|
|
db "github.com/multica-ai/multica/server/pkg/db/generated"
|
|
)
|
|
|
|
func TestRuntimeBindingLocksWorkspaceBeforeRuntime(t *testing.T) {
|
|
if testHandler == nil {
|
|
t.Fatal("database-backed handler fixture is required")
|
|
}
|
|
for _, tt := range []struct {
|
|
entry string
|
|
wantStatus int
|
|
}{
|
|
{entry: "user create", wantStatus: http.StatusCreated},
|
|
{entry: "user rebind", wantStatus: http.StatusOK},
|
|
{entry: "mika create", wantStatus: http.StatusCreated},
|
|
{entry: "builder create", wantStatus: http.StatusCreated},
|
|
{entry: "builder switch", wantStatus: http.StatusOK},
|
|
} {
|
|
t.Run(tt.entry, func(t *testing.T) {
|
|
h := *testHandler
|
|
fx, runtimeID, handler, req := runtimeBindingVisibilityRequest(t, &h, tt.entry)
|
|
fx.Cleanup(t, `DELETE FROM agent WHERE workspace_id = $1`, fx.WorkspaceID)
|
|
fx.Cleanup(t, `DELETE FROM chat_session WHERE workspace_id = $1`, fx.WorkspaceID)
|
|
ctx, cancel := context.WithTimeout(req.Context(), 20*time.Second)
|
|
defer cancel()
|
|
req = req.WithContext(ctx)
|
|
holder, err := testPool.Begin(ctx)
|
|
if err != nil {
|
|
t.Fatalf("begin workspace delete lock: %v", err)
|
|
}
|
|
defer holder.Rollback(context.Background())
|
|
if _, err := db.New(holder).LockWorkspaceForDelete(ctx, parseUUID(fx.WorkspaceID)); err != nil {
|
|
t.Fatalf("lock workspace as deletion would: %v", err)
|
|
}
|
|
holderPID := holderBackendPID(t, ctx, holder)
|
|
responses := make(chan *testutil.Response, 1)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
responses <- testutil.Call(t, handler, req)
|
|
}()
|
|
defer func() {
|
|
cancel()
|
|
_ = holder.Rollback(context.Background())
|
|
select {
|
|
case <-done:
|
|
case <-time.After(5 * time.Second):
|
|
t.Error("binding request did not exit after cancellation")
|
|
}
|
|
}()
|
|
if !waitForWaiterBlockedBy(t, holderPID, 10*time.Second) {
|
|
select {
|
|
case response := <-responses:
|
|
t.Fatalf("binding returned %d while workspace was locked: %s", response.Code, response.Text())
|
|
default:
|
|
t.Fatal("binding did not wait for the workspace lock")
|
|
}
|
|
}
|
|
|
|
// While the binding waits for the workspace, another transaction
|
|
// must still be able to lock the runtime. Otherwise deletion can
|
|
// hold workspace -> wait runtime while binding does the reverse.
|
|
probe, err := testPool.Begin(ctx)
|
|
if err != nil {
|
|
t.Fatalf("begin runtime lock probe: %v", err)
|
|
}
|
|
defer probe.Rollback(context.Background())
|
|
if _, err := probe.Exec(ctx, `SELECT id FROM agent_runtime WHERE id = $1 FOR NO KEY UPDATE NOWAIT`, runtimeID); err != nil {
|
|
t.Fatalf("runtime was locked before binding acquired workspace: %v", err)
|
|
}
|
|
if err := probe.Rollback(ctx); err != nil {
|
|
t.Fatalf("release runtime lock probe: %v", err)
|
|
}
|
|
if err := holder.Commit(ctx); err != nil {
|
|
t.Fatalf("release workspace lock: %v", err)
|
|
}
|
|
select {
|
|
case response := <-responses:
|
|
response.Want(tt.wantStatus)
|
|
case <-ctx.Done():
|
|
t.Fatal("binding did not finish after workspace lock was released")
|
|
}
|
|
if got := fx.Count(t, `SELECT count(*) FROM agent WHERE runtime_id = $1`, runtimeID); got != 1 {
|
|
t.Fatalf("successful binding left %d agents on the runtime, want 1", got)
|
|
}
|
|
})
|
|
}
|
|
}
|