Merge branch 'fix/m2-pr-e-agent-service'

PR-E of 6: AgentService ctx-first collapse.

Collapses the HeartbeatWithContext wrapper into a single Heartbeat
method. Handler-facing method name is preserved (D-4); the handler
service interface and mock already expected ctx-first, so this PR
touches only the service layer and its tests (4 files, 9+/15-).

Verification on the feature branch: build, vet, test (-short),
test -race, full-module test -short, and golangci-lint all clean.

Audit complete. Commit: 1f6cf0eafa. Sections: 12. Findings: 2/7/10/4/6.
This commit is contained in:
shankar0123
2026-04-18 01:25:30 +00:00
4 changed files with 9 additions and 15 deletions
+2 -8
View File
@@ -91,8 +91,8 @@ func (s *AgentService) Register(ctx context.Context, name string, hostname strin
return agent, apiKey, nil return agent, apiKey, nil
} }
// HeartbeatWithContext updates an agent's last seen time, status, and metadata. // Heartbeat updates an agent's last seen time, status, and metadata.
func (s *AgentService) HeartbeatWithContext(ctx context.Context, agentID string, metadata *domain.AgentMetadata) error { func (s *AgentService) Heartbeat(ctx context.Context, agentID string, metadata *domain.AgentMetadata) error {
agent, err := s.agentRepo.Get(ctx, agentID) agent, err := s.agentRepo.Get(ctx, agentID)
if err != nil { if err != nil {
return fmt.Errorf("failed to fetch agent: %w", err) return fmt.Errorf("failed to fetch agent: %w", err)
@@ -114,12 +114,6 @@ func (s *AgentService) HeartbeatWithContext(ctx context.Context, agentID string,
return nil return nil
} }
// Heartbeat updates agent heartbeat (handler interface method).
// Note: This method is called from handlers which have a context; callers should prefer HeartbeatWithContext.
func (s *AgentService) Heartbeat(ctx context.Context, agentID string, metadata *domain.AgentMetadata) error {
return s.HeartbeatWithContext(ctx, agentID, metadata)
}
// SubmitCSR validates and processes a Certificate Signing Request from an agent. // SubmitCSR validates and processes a Certificate Signing Request from an agent.
// In agent keygen mode, this completes an AwaitingCSR renewal job by signing the CSR // In agent keygen mode, this completes an AwaitingCSR renewal job by signing the CSR
// and storing the cert version. The private key stays on the agent — only the CSR // and storing the cert version. The private key stays on the agent — only the CSR
+2 -2
View File
@@ -92,7 +92,7 @@ func TestHeartbeat(t *testing.T) {
agentService := NewAgentService(agentRepo, certRepo, jobRepo, targetRepo, auditService, issuerRegistry, nil) agentService := NewAgentService(agentRepo, certRepo, jobRepo, targetRepo, auditService, issuerRegistry, nil)
err := agentService.HeartbeatWithContext(ctx, "agent-001", nil) err := agentService.Heartbeat(ctx, "agent-001", nil)
if err != nil { if err != nil {
t.Fatalf("Heartbeat failed: %v", err) t.Fatalf("Heartbeat failed: %v", err)
} }
@@ -125,7 +125,7 @@ func TestHeartbeat_NotFound(t *testing.T) {
agentService := NewAgentService(agentRepo, certRepo, jobRepo, targetRepo, auditService, issuerRegistry, nil) agentService := NewAgentService(agentRepo, certRepo, jobRepo, targetRepo, auditService, issuerRegistry, nil)
err := agentService.HeartbeatWithContext(ctx, "nonexistent", nil) err := agentService.Heartbeat(ctx, "nonexistent", nil)
if err == nil { if err == nil {
t.Fatal("expected error for nonexistent agent") t.Fatal("expected error for nonexistent agent")
} }
+1 -1
View File
@@ -159,7 +159,7 @@ func TestConcurrentAgentHeartbeats(t *testing.T) {
Architecture: "x86_64", Architecture: "x86_64",
} }
err := agentSvc.HeartbeatWithContext(ctx, agentID, metadata) err := agentSvc.Heartbeat(ctx, agentID, metadata)
if err != nil { if err != nil {
errChan <- fmt.Errorf("goroutine %d: failed heartbeat for agent %s: %w", idx, agentID, err) errChan <- fmt.Errorf("goroutine %d: failed heartbeat for agent %s: %w", idx, agentID, err)
return return
+4 -4
View File
@@ -176,13 +176,13 @@ func TestAgentService_HeartbeatWithCancelledContext(t *testing.T) {
nil, // renewalService nil, // renewalService
) )
err := agentSvc.HeartbeatWithContext(ctx, "agent-1", &domain.AgentMetadata{}) err := agentSvc.Heartbeat(ctx, "agent-1", &domain.AgentMetadata{})
// Service should handle cancelled context // Service should handle cancelled context
if err == nil || ctx.Err() == context.Canceled { if err == nil || ctx.Err() == context.Canceled {
return return
} }
t.Logf("HeartbeatWithContext with cancelled context returned: %v", err) t.Logf("Heartbeat with cancelled context returned: %v", err)
} }
// Test with timeout context (should trigger deadline exceeded) // Test with timeout context (should trigger deadline exceeded)
@@ -229,11 +229,11 @@ func TestAgentService_HeartbeatWithDeadlineExceeded(t *testing.T) {
time.Sleep(10 * time.Millisecond) // Ensure deadline is exceeded time.Sleep(10 * time.Millisecond) // Ensure deadline is exceeded
err := agentSvc.HeartbeatWithContext(ctx, "agent-1", &domain.AgentMetadata{}) err := agentSvc.Heartbeat(ctx, "agent-1", &domain.AgentMetadata{})
// Service should handle deadline exceeded // Service should handle deadline exceeded
if err == nil || ctx.Err() == context.DeadlineExceeded { if err == nil || ctx.Err() == context.DeadlineExceeded {
return return
} }
t.Logf("HeartbeatWithContext with deadline exceeded returned: %v", err) t.Logf("Heartbeat with deadline exceeded returned: %v", err)
} }