feat: M11b — ownership tracking, agent groups, interactive renewal approval

Ownership: owners/teams GUI pages, notification email resolution via
resolveRecipient (owner_id → owner.email lookup). Agent groups: dynamic
device grouping by OS/arch/IP CIDR/version with manual include/exclude
membership, migration 000004, full CRUD stack (domain → repo → service →
handler → frontend). Interactive approval: AwaitingApproval job state,
approve/reject API endpoints with reason tracking. Tests: 12 agent group
handler tests, 8 approve/reject job handler tests, integration tests
updated for 13-param RegisterHandlers. Docs updated across architecture,
concepts, and seed data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Shankar
2026-03-20 21:02:35 -04:00
parent 1ef16984eb
commit e445cbef22
27 changed files with 1774 additions and 21 deletions
+29
View File
@@ -68,6 +68,7 @@ func TestCertificateLifecycle(t *testing.T) {
profileHandler := handler.NewProfileHandler(&mockProfileService{})
teamHandler := handler.NewTeamHandler(&mockTeamService{})
ownerHandler := handler.NewOwnerHandler(&mockOwnerService{})
agentGroupHandler := handler.NewAgentGroupHandler(&mockAgentGroupService{})
auditHandler := handler.NewAuditHandler(auditService)
notificationHandler := handler.NewNotificationHandler(notificationService)
healthHandler := handler.NewHealthHandler("none")
@@ -84,6 +85,7 @@ func TestCertificateLifecycle(t *testing.T) {
profileHandler,
teamHandler,
ownerHandler,
agentGroupHandler,
auditHandler,
notificationHandler,
healthHandler,
@@ -1019,3 +1021,30 @@ func (m *mockProfileService) UpdateProfile(id string, profile domain.Certificate
func (m *mockProfileService) DeleteProfile(id string) error {
return nil
}
type mockAgentGroupService struct{}
func (m *mockAgentGroupService) ListAgentGroups(page, perPage int) ([]domain.AgentGroup, int64, error) {
return []domain.AgentGroup{}, 0, nil
}
func (m *mockAgentGroupService) GetAgentGroup(id string) (*domain.AgentGroup, error) {
return nil, fmt.Errorf("agent group not found")
}
func (m *mockAgentGroupService) CreateAgentGroup(group domain.AgentGroup) (*domain.AgentGroup, error) {
return &group, nil
}
func (m *mockAgentGroupService) UpdateAgentGroup(id string, group domain.AgentGroup) (*domain.AgentGroup, error) {
group.ID = id
return &group, nil
}
func (m *mockAgentGroupService) DeleteAgentGroup(id string) error {
return nil
}
func (m *mockAgentGroupService) ListMembers(id string) ([]domain.Agent, int64, error) {
return []domain.Agent{}, 0, nil
}
+2
View File
@@ -58,6 +58,7 @@ func setupTestServer(t *testing.T) (*httptest.Server, *mockCertificateRepository
profileHandler := handler.NewProfileHandler(&mockProfileService{})
teamHandler := handler.NewTeamHandler(&mockTeamService{})
ownerHandler := handler.NewOwnerHandler(&mockOwnerService{})
agentGroupHandler := handler.NewAgentGroupHandler(&mockAgentGroupService{})
auditHandler := handler.NewAuditHandler(auditService)
notificationHandler := handler.NewNotificationHandler(notificationService)
healthHandler := handler.NewHealthHandler("none")
@@ -73,6 +74,7 @@ func setupTestServer(t *testing.T) (*httptest.Server, *mockCertificateRepository
profileHandler,
teamHandler,
ownerHandler,
agentGroupHandler,
auditHandler,
notificationHandler,
healthHandler,