fix(ci): repair handler test compile drift #884

Merged
devops-engineer merged 2 commits from fix/main-handler-test-compile into main 2026-05-13 20:47:38 +00:00
2 changed files with 59 additions and 54 deletions

View File

@ -471,11 +471,11 @@ func TestDelegationRecord_InsertsActivityLogRow(t *testing.T) {
mock.ExpectExec("INSERT INTO activity_logs").
WithArgs(
"550e8400-e29b-41d4-a716-446655440000", // workspace_id
"550e8400-e29b-41d4-a716-446655440000", // source_id
"550e8400-e29b-41d4-a716-446655440001", // target_id
"Delegating to 550e8400-e29b-41d4-a716-446655440001", // summary
sqlmock.AnyArg(), // request_body (jsonb)
"550e8400-e29b-41d4-a716-446655440000", // workspace_id
"550e8400-e29b-41d4-a716-446655440000", // source_id
"550e8400-e29b-41d4-a716-446655440001", // target_id
"Delegating to 550e8400-e29b-41d4-a716-446655440001", // summary
sqlmock.AnyArg(), // request_body (jsonb)
).
WillReturnResult(sqlmock.NewResult(0, 1))
// RecordAndBroadcast INSERT for DELEGATION_SENT
@ -970,9 +970,9 @@ func TestInsertDelegationOutcome_ZeroValueIsUnknown(t *testing.T) {
// Test strategy: spin up a mock A2A agent server, set up the source/target DB rows, call
// executeDelegation directly, and verify the activity_logs status and delegation status.
const testDelegationID = "del-159-test"
const testSourceID = "ws-source-159"
const testTargetID = "ws-target-159"
const testDeliveryDelegationID = "del-159-test"
const testDeliverySourceID = "ws-source-159"
const testDeliveryTargetID = "ws-target-159"
// expectExecuteDelegationBase sets up sqlmock expectations for the DB queries that
// executeDelegation always makes, regardless of outcome.
@ -980,17 +980,17 @@ func expectExecuteDelegationBase(mock sqlmock.Sqlmock) {
// updateDelegationStatus: dispatched
// Uses prefix match — sqlmock regexes match the full query string.
mock.ExpectExec("UPDATE activity_logs SET status").
WithArgs("dispatched", "", testSourceID, testDelegationID).
WithArgs("dispatched", "", testDeliverySourceID, testDeliveryDelegationID).
WillReturnResult(sqlmock.NewResult(0, 1))
// CanCommunicate: getWorkspaceRef(source) + getWorkspaceRef(target).
// Both are root-level workspaces (parent_id=NULL) → root-level siblings → allowed.
mock.ExpectQuery("SELECT id, parent_id FROM workspaces WHERE id = ").
WithArgs(testSourceID).
WillReturnRows(sqlmock.NewRows([]string{"id", "parent_id"}).AddRow(testSourceID, nil))
WithArgs(testDeliverySourceID).
WillReturnRows(sqlmock.NewRows([]string{"id", "parent_id"}).AddRow(testDeliverySourceID, nil))
mock.ExpectQuery("SELECT id, parent_id FROM workspaces WHERE id = ").
WithArgs(testTargetID).
WillReturnRows(sqlmock.NewRows([]string{"id", "parent_id"}).AddRow(testTargetID, nil))
WithArgs(testDeliveryTargetID).
WillReturnRows(sqlmock.NewRows([]string{"id", "parent_id"}).AddRow(testDeliveryTargetID, nil))
// resolveAgentURL: test callers always set the URL in Redis (mr.Set ws:{id}:url),
// so resolveAgentURL gets a cache hit and never falls back to DB.
@ -1009,7 +1009,7 @@ func expectExecuteDelegationSuccess(mock sqlmock.Sqlmock, respBody string) {
// updateDelegationStatus: completed
mock.ExpectExec("UPDATE activity_logs SET status").
WithArgs("completed", "", testSourceID, testDelegationID).
WithArgs("completed", "", testDeliverySourceID, testDeliveryDelegationID).
WillReturnResult(sqlmock.NewResult(0, 1))
}
@ -1018,7 +1018,7 @@ func expectExecuteDelegationSuccess(mock sqlmock.Sqlmock, respBody string) {
func expectExecuteDelegationFailed(mock sqlmock.Sqlmock) {
// updateDelegationStatus: failed (fires before the INSERT in the failure path)
mock.ExpectExec("UPDATE activity_logs SET status").
WithArgs("failed", sqlmock.AnyArg(), testSourceID, testDelegationID).
WithArgs("failed", sqlmock.AnyArg(), testDeliverySourceID, testDeliveryDelegationID).
WillReturnResult(sqlmock.NewResult(0, 1))
// INSERT activity_logs for delegation failure ('failed' is a SQL literal, not a param)
@ -1085,7 +1085,7 @@ func TestExecuteDelegation_DeliveryConfirmedProxyError_TreatsAsSuccess(t *testin
}()
agentURL := "http://" + ln.Addr().String()
mr.Set(fmt.Sprintf("ws:%s:url", testTargetID), agentURL)
mr.Set(fmt.Sprintf("ws:%s:url", testDeliveryTargetID), agentURL)
allowLoopbackForTest(t)
expectExecuteDelegationBase(mock)
@ -1104,7 +1104,7 @@ func TestExecuteDelegation_DeliveryConfirmedProxyError_TreatsAsSuccess(t *testin
},
},
})
dh.executeDelegation(testSourceID, testTargetID, testDelegationID, a2aBody)
dh.executeDelegation(context.Background(), testDeliverySourceID, testDeliveryTargetID, testDeliveryDelegationID, a2aBody)
time.Sleep(100 * time.Millisecond) // let DB writes settle
@ -1155,7 +1155,7 @@ func TestExecuteDelegation_ProxyErrorNon2xx_RemainsFailed(t *testing.T) {
}()
agentURL := "http://" + ln.Addr().String()
mr.Set(fmt.Sprintf("ws:%s:url", testTargetID), agentURL)
mr.Set(fmt.Sprintf("ws:%s:url", testDeliveryTargetID), agentURL)
allowLoopbackForTest(t)
expectExecuteDelegationBase(mock)
@ -1170,7 +1170,7 @@ func TestExecuteDelegation_ProxyErrorNon2xx_RemainsFailed(t *testing.T) {
},
},
})
dh.executeDelegation(testSourceID, testTargetID, testDelegationID, a2aBody)
dh.executeDelegation(context.Background(), testDeliverySourceID, testDeliveryTargetID, testDeliveryDelegationID, a2aBody)
time.Sleep(100 * time.Millisecond)
@ -1201,7 +1201,7 @@ func TestExecuteDelegation_ProxyErrorEmptyBody_RemainsFailed(t *testing.T) {
}))
defer agentServer.Close()
mr.Set(fmt.Sprintf("ws:%s:url", testTargetID), agentServer.URL)
mr.Set(fmt.Sprintf("ws:%s:url", testDeliveryTargetID), agentServer.URL)
allowLoopbackForTest(t)
// executeDelegationBase: UPDATE dispatched + CanCommunicate SELECTs
@ -1220,7 +1220,7 @@ func TestExecuteDelegation_ProxyErrorEmptyBody_RemainsFailed(t *testing.T) {
},
},
})
dh.executeDelegation(testSourceID, testTargetID, testDelegationID, a2aBody)
dh.executeDelegation(context.Background(), testDeliverySourceID, testDeliveryTargetID, testDeliveryDelegationID, a2aBody)
time.Sleep(100 * time.Millisecond)
@ -1248,7 +1248,7 @@ func TestExecuteDelegation_CleanProxyResponse_Unchanged(t *testing.T) {
}))
defer agentServer.Close()
mr.Set(fmt.Sprintf("ws:%s:url", testTargetID), agentServer.URL)
mr.Set(fmt.Sprintf("ws:%s:url", testDeliveryTargetID), agentServer.URL)
allowLoopbackForTest(t)
expectExecuteDelegationBase(mock)
@ -1263,7 +1263,7 @@ func TestExecuteDelegation_CleanProxyResponse_Unchanged(t *testing.T) {
},
},
})
dh.executeDelegation(testSourceID, testTargetID, testDelegationID, a2aBody)
dh.executeDelegation(context.Background(), testDeliverySourceID, testDeliveryTargetID, testDeliveryDelegationID, a2aBody)
time.Sleep(100 * time.Millisecond)

View File

@ -34,11 +34,16 @@ func setupWorkspaceCrudTest(t *testing.T) (sqlmock.Sqlmock, *gin.Engine) {
return mock, r
}
func newWorkspaceCrudHandler(t *testing.T) *WorkspaceHandler {
t.Helper()
return NewWorkspaceHandler(nil, nil, "", t.TempDir())
}
// ---------- State ----------
func TestState_LegacyWorkspaceNoLiveToken(t *testing.T) {
mock, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
h := newWorkspaceCrudHandler(t)
r.GET("/workspaces/:id/state", h.State)
wsID := "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
@ -76,7 +81,7 @@ func TestState_LegacyWorkspaceNoLiveToken(t *testing.T) {
func TestState_HasLiveTokenMissingAuth(t *testing.T) {
mock, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
h := newWorkspaceCrudHandler(t)
r.GET("/workspaces/:id/state", h.State)
wsID := "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
@ -96,7 +101,7 @@ func TestState_HasLiveTokenMissingAuth(t *testing.T) {
func TestState_WorkspaceNotFound(t *testing.T) {
mock, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
h := newWorkspaceCrudHandler(t)
r.GET("/workspaces/:id/state", h.State)
wsID := "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
@ -126,7 +131,7 @@ func TestState_WorkspaceNotFound(t *testing.T) {
func TestState_WorkspaceSoftDeleted(t *testing.T) {
mock, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
h := newWorkspaceCrudHandler(t)
r.GET("/workspaces/:id/state", h.State)
wsID := "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
@ -159,7 +164,7 @@ func TestState_WorkspaceSoftDeleted(t *testing.T) {
func TestState_QueryError(t *testing.T) {
mock, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
h := newWorkspaceCrudHandler(t)
r.GET("/workspaces/:id/state", h.State)
wsID := "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
@ -182,8 +187,8 @@ func TestState_QueryError(t *testing.T) {
// ---------- Update ----------
func TestUpdate_InvalidUUID(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -200,8 +205,8 @@ func TestUpdate_InvalidUUID(t *testing.T) {
}
func TestUpdate_InvalidBody(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -216,8 +221,8 @@ func TestUpdate_InvalidBody(t *testing.T) {
}
func TestUpdate_WorkspaceNotFound(t *testing.T) {
mock, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
mock, _ := setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -240,8 +245,8 @@ func TestUpdate_WorkspaceNotFound(t *testing.T) {
}
func TestUpdate_NameTooLong(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -262,8 +267,8 @@ func TestUpdate_NameTooLong(t *testing.T) {
}
func TestUpdate_RoleTooLong(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -284,8 +289,8 @@ func TestUpdate_RoleTooLong(t *testing.T) {
}
func TestUpdate_NameWithNewline(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -302,8 +307,8 @@ func TestUpdate_NameWithNewline(t *testing.T) {
}
func TestUpdate_NameWithYAMLSpecialChars(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -320,8 +325,8 @@ func TestUpdate_NameWithYAMLSpecialChars(t *testing.T) {
}
func TestUpdate_WorkspaceDirSystemPath(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -338,8 +343,8 @@ func TestUpdate_WorkspaceDirSystemPath(t *testing.T) {
}
func TestUpdate_WorkspaceDirTraversal(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -356,8 +361,8 @@ func TestUpdate_WorkspaceDirTraversal(t *testing.T) {
}
func TestUpdate_WorkspaceDirRelativePath(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.PATCH("/workspaces/:id", h.Update)
@ -376,8 +381,8 @@ func TestUpdate_WorkspaceDirRelativePath(t *testing.T) {
// ---------- Delete ----------
func TestDelete_InvalidUUID(t *testing.T) {
_, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
_, _ = setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.DELETE("/workspaces/:id", h.Delete)
@ -391,8 +396,8 @@ func TestDelete_InvalidUUID(t *testing.T) {
}
func TestDelete_HasChildrenWithoutConfirm(t *testing.T) {
mock, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
mock, _ := setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.DELETE("/workspaces/:id", h.Delete)
@ -425,8 +430,8 @@ func TestDelete_HasChildrenWithoutConfirm(t *testing.T) {
}
func TestDelete_ChildrenCheckQueryError(t *testing.T) {
mock, r := setupWorkspaceCrudTest(t)
h := NewWorkspaceHandler(nil, nil, nil, nil)
mock, _ := setupWorkspaceCrudTest(t)
h := newWorkspaceCrudHandler(t)
r2 := gin.New()
r2.DELETE("/workspaces/:id", h.Delete)