Files
probo/e2e/console/control_test.go
Sacha Al Himdani 9ac71f948f Update contact email to hello@probo.com
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-06-09 16:45:23 +02:00

892 lines
21 KiB
Go

// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package console_test
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.probo.inc/probo/e2e/internal/factory"
"go.probo.inc/probo/e2e/internal/testutil"
)
func TestControl_Create(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
frameworkID := factory.CreateFramework(owner, factory.Attrs{"name": "Framework for Control Tests"})
t.Run("with full details", func(t *testing.T) {
query := `
mutation CreateControl($input: CreateControlInput!) {
createControl(input: $input) {
controlEdge {
node {
id
name
sectionTitle
}
}
}
}
`
var result struct {
CreateControl struct {
ControlEdge struct {
Node struct {
ID string `json:"id"`
Name string `json:"name"`
SectionTitle string `json:"sectionTitle"`
} `json:"node"`
} `json:"controlEdge"`
} `json:"createControl"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"sectionTitle": "A.5",
"name": "Information Security Policies",
"description": "Policies for information security",
"bestPractice": true,
"maturityLevel": "INITIAL",
},
}, &result)
require.NoError(t, err)
control := result.CreateControl.ControlEdge.Node
assert.NotEmpty(t, control.ID)
assert.Equal(t, "Information Security Policies", control.Name)
assert.Equal(t, "A.5", control.SectionTitle)
})
}
func TestControl_Update(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
frameworkID := factory.CreateFramework(owner, factory.Attrs{"name": "Framework for Control Update"})
controlID := factory.CreateControl(owner, frameworkID, factory.Attrs{
"name": "Control to Update",
"description": "Original description",
})
t.Run("updates name and description", func(t *testing.T) {
query := `
mutation UpdateControl($input: UpdateControlInput!) {
updateControl(input: $input) {
control {
id
name
}
}
}
`
var result struct {
UpdateControl struct {
Control struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"control"`
} `json:"updateControl"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"id": controlID,
"name": "Updated Control Name",
"description": "Updated description",
},
}, &result)
require.NoError(t, err)
assert.Equal(t, controlID, result.UpdateControl.Control.ID)
assert.Equal(t, "Updated Control Name", result.UpdateControl.Control.Name)
})
}
func TestControl_Delete(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
frameworkID := factory.CreateFramework(owner, factory.Attrs{
"name": "Framework for Delete",
})
controlID := factory.CreateControl(owner, frameworkID, factory.Attrs{
"name": "Control to Delete",
})
query := `
mutation DeleteControl($input: DeleteControlInput!) {
deleteControl(input: $input) {
deletedControlId
}
}
`
var result struct {
DeleteControl struct {
DeletedControlID string `json:"deletedControlId"`
} `json:"deleteControl"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"controlId": controlID,
},
}, &result)
require.NoError(t, err)
assert.Equal(t, controlID, result.DeleteControl.DeletedControlID)
}
func TestControl_List(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
frameworkID := factory.CreateFramework(owner, factory.Attrs{"name": "Framework for List Test"})
// Create multiple controls
controlNames := []string{"Control A", "Control B", "Control C"}
for i, name := range controlNames {
factory.CreateControl(owner, frameworkID, factory.Attrs{
"name": name,
"sectionTitle": fmt.Sprintf("A.%d", 5+i),
})
}
query := `
query GetFrameworkControls($id: ID!) {
node(id: $id) {
... on Framework {
id
name
controls(first: 10) {
edges {
node {
id
name
sectionTitle
}
}
}
}
}
}
`
var result struct {
Node struct {
ID string `json:"id"`
Name string `json:"name"`
Controls struct {
Edges []struct {
Node struct {
ID string `json:"id"`
Name string `json:"name"`
SectionTitle string `json:"sectionTitle"`
} `json:"node"`
} `json:"edges"`
} `json:"controls"`
} `json:"node"`
}
err := owner.Execute(query, map[string]any{"id": frameworkID}, &result)
require.NoError(t, err)
assert.GreaterOrEqual(t, len(result.Node.Controls.Edges), 3)
}
func TestControl_RequiredFields(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
// Create a framework first
createFrameworkQuery := `
mutation CreateFramework($input: CreateFrameworkInput!) {
createFramework(input: $input) {
frameworkEdge {
node {
id
}
}
}
}
`
var frameworkResult struct {
CreateFramework struct {
FrameworkEdge struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"frameworkEdge"`
} `json:"createFramework"`
}
err := owner.Execute(createFrameworkQuery, map[string]any{
"input": map[string]any{
"organizationId": owner.GetOrganizationID().String(),
"name": fmt.Sprintf("Control Required Fields Test %d", time.Now().UnixNano()),
},
}, &frameworkResult)
require.NoError(t, err)
frameworkID := frameworkResult.CreateFramework.FrameworkEdge.Node.ID
createControlQuery := `
mutation CreateControl($input: CreateControlInput!) {
createControl(input: $input) {
controlEdge {
node {
id
}
}
}
}
`
tests := []struct {
name string
variables map[string]any
wantError bool
}{
{
name: "Missing frameworkId should fail",
variables: map[string]any{
"input": map[string]any{
"name": "Test Control",
"description": "Test",
"sectionTitle": "Section 1",
"bestPractice": true,
"maturityLevel": "INITIAL",
},
},
wantError: true,
},
{
name: "Missing name should fail",
variables: map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"description": "Test",
"sectionTitle": "Section 1",
"bestPractice": true,
"maturityLevel": "INITIAL",
},
},
wantError: true,
},
{
name: "Missing sectionTitle should fail",
variables: map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "Test Control",
"description": "Test",
"bestPractice": true,
"maturityLevel": "INITIAL",
},
},
wantError: true,
},
{
name: "Missing description should fail (required field)",
variables: map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "Test Control",
"sectionTitle": "Section 1",
"bestPractice": true,
"maturityLevel": "INITIAL",
},
},
wantError: true,
},
{
name: "Missing bestPractice should fail",
variables: map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "Test Control",
"description": "Test",
"sectionTitle": "Section 1",
"maturityLevel": "INITIAL",
},
},
wantError: true,
},
{
name: "Missing maturityLevel should fail",
variables: map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "Test Control",
"description": "Test",
"sectionTitle": "Section 1",
"bestPractice": true,
},
},
wantError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := owner.Do(createControlQuery, tt.variables)
if tt.wantError {
require.Error(t, err, "Expected validation error")
} else {
require.NoError(t, err)
}
})
}
}
func TestControl_OmittableDescription(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
// Create framework
createFrameworkQuery := `
mutation CreateFramework($input: CreateFrameworkInput!) {
createFramework(input: $input) {
frameworkEdge {
node {
id
}
}
}
}
`
var frameworkResult struct {
CreateFramework struct {
FrameworkEdge struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"frameworkEdge"`
} `json:"createFramework"`
}
err := owner.Execute(createFrameworkQuery, map[string]any{
"input": map[string]any{
"organizationId": owner.GetOrganizationID().String(),
"name": fmt.Sprintf("Control Omittable Test %d", time.Now().UnixNano()),
},
}, &frameworkResult)
require.NoError(t, err)
frameworkID := frameworkResult.CreateFramework.FrameworkEdge.Node.ID
// Create control with description
createControlQuery := `
mutation CreateControl($input: CreateControlInput!) {
createControl(input: $input) {
controlEdge {
node {
id
description
}
}
}
}
`
var createResult struct {
CreateControl struct {
ControlEdge struct {
Node struct {
ID string `json:"id"`
Description string `json:"description"`
} `json:"node"`
} `json:"controlEdge"`
} `json:"createControl"`
}
err = owner.Execute(createControlQuery, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "Omittable Test Control",
"description": "Initial description",
"sectionTitle": "Section 1",
"bestPractice": true,
"maturityLevel": "INITIAL",
},
}, &createResult)
require.NoError(t, err)
controlID := createResult.CreateControl.ControlEdge.Node.ID
t.Run("Update with null description should clear it", func(t *testing.T) {
updateControlQuery := `
mutation UpdateControl($input: UpdateControlInput!) {
updateControl(input: $input) {
control {
id
description
}
}
}
`
var updateResult struct {
UpdateControl struct {
Control struct {
ID string `json:"id"`
Description *string `json:"description"`
} `json:"control"`
} `json:"updateControl"`
}
err := owner.Execute(updateControlQuery, map[string]any{
"input": map[string]any{
"id": controlID,
"description": nil,
},
}, &updateResult)
require.NoError(t, err)
assert.Nil(t, updateResult.UpdateControl.Control.Description)
})
t.Run("Update without description should not change it", func(t *testing.T) {
// Set description first
setDescQuery := `
mutation UpdateControl($input: UpdateControlInput!) {
updateControl(input: $input) {
control {
id
}
}
}
`
var setDescResult struct {
UpdateControl struct {
Control struct {
ID string `json:"id"`
} `json:"control"`
} `json:"updateControl"`
}
err := owner.Execute(setDescQuery, map[string]any{
"input": map[string]any{
"id": controlID,
"description": "Should persist",
},
}, &setDescResult)
require.NoError(t, err)
// Update only name
updateNameQuery := `
mutation UpdateControl($input: UpdateControlInput!) {
updateControl(input: $input) {
control {
id
name
description
}
}
}
`
var updateResult struct {
UpdateControl struct {
Control struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
} `json:"control"`
} `json:"updateControl"`
}
err = owner.Execute(updateNameQuery, map[string]any{
"input": map[string]any{
"id": controlID,
"name": "Updated Name",
},
}, &updateResult)
require.NoError(t, err)
assert.Equal(t, "Should persist", updateResult.UpdateControl.Control.Description)
})
}
func TestControl_MaturityLevel(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
frameworkID := factory.CreateFramework(owner, factory.Attrs{"name": "Framework for Maturity Tests"})
createControlQuery := `
mutation CreateControl($input: CreateControlInput!) {
createControl(input: $input) {
controlEdge {
node {
id
maturityLevel
}
}
}
}
`
updateControlQuery := `
mutation UpdateControl($input: UpdateControlInput!) {
updateControl(input: $input) {
control {
id
maturityLevel
}
}
}
`
type createResult struct {
CreateControl struct {
ControlEdge struct {
Node struct {
ID string `json:"id"`
MaturityLevel string `json:"maturityLevel"`
} `json:"node"`
} `json:"controlEdge"`
} `json:"createControl"`
}
type updateResult struct {
UpdateControl struct {
Control struct {
ID string `json:"id"`
MaturityLevel string `json:"maturityLevel"`
} `json:"control"`
} `json:"updateControl"`
}
t.Run("create with INITIAL maturityLevel", func(t *testing.T) {
var res createResult
err := owner.Execute(createControlQuery, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"sectionTitle": "M.1",
"name": "Control with initial maturity",
"description": "control with initial maturity description",
"bestPractice": true,
"maturityLevel": "INITIAL",
},
}, &res)
require.NoError(t, err)
assert.Equal(t, "INITIAL", res.CreateControl.ControlEdge.Node.MaturityLevel)
})
t.Run("create with maturityLevel persists value", func(t *testing.T) {
var res createResult
err := owner.Execute(createControlQuery, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"sectionTitle": "M.2",
"name": "Control with maturity",
"description": "control with maturity description",
"bestPractice": true,
"maturityLevel": "DEFINED",
},
}, &res)
require.NoError(t, err)
assert.Equal(t, "DEFINED", res.CreateControl.ControlEdge.Node.MaturityLevel)
})
t.Run("update lifecycle: set, change, omit", func(t *testing.T) {
var created createResult
err := owner.Execute(createControlQuery, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"sectionTitle": "M.3",
"name": "Lifecycle control",
"description": "lifecycle control description",
"bestPractice": true,
"maturityLevel": "INITIAL",
},
}, &created)
require.NoError(t, err)
controlID := created.CreateControl.ControlEdge.Node.ID
// set
var setRes updateResult
err = owner.Execute(updateControlQuery, map[string]any{
"input": map[string]any{
"id": controlID,
"maturityLevel": "INITIAL",
},
}, &setRes)
require.NoError(t, err)
assert.Equal(t, "INITIAL", setRes.UpdateControl.Control.MaturityLevel)
// change
var changeRes updateResult
err = owner.Execute(updateControlQuery, map[string]any{
"input": map[string]any{
"id": controlID,
"maturityLevel": "OPTIMIZING",
},
}, &changeRes)
require.NoError(t, err)
assert.Equal(t, "OPTIMIZING", changeRes.UpdateControl.Control.MaturityLevel)
// omit field on next update -> stays unchanged
var omitRes updateResult
err = owner.Execute(updateControlQuery, map[string]any{
"input": map[string]any{
"id": controlID,
"name": "Renamed without touching maturity",
},
}, &omitRes)
require.NoError(t, err)
assert.Equal(t, "OPTIMIZING", omitRes.UpdateControl.Control.MaturityLevel)
})
t.Run("invalid maturityLevel is rejected", func(t *testing.T) {
var res createResult
err := owner.Execute(createControlQuery, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"sectionTitle": "M.4",
"name": "Bad maturity",
"description": "bad maturity description",
"bestPractice": true,
"maturityLevel": "BOGUS",
},
}, &res)
assert.Error(t, err)
})
}
func TestControl_SubResolvers(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
// Create framework
createFrameworkQuery := `
mutation CreateFramework($input: CreateFrameworkInput!) {
createFramework(input: $input) {
frameworkEdge {
node {
id
}
}
}
}
`
var frameworkResult struct {
CreateFramework struct {
FrameworkEdge struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"frameworkEdge"`
} `json:"createFramework"`
}
err := owner.Execute(createFrameworkQuery, map[string]any{
"input": map[string]any{
"organizationId": owner.GetOrganizationID().String(),
"name": fmt.Sprintf("Control SubResolver Test %d", time.Now().UnixNano()),
},
}, &frameworkResult)
require.NoError(t, err)
frameworkID := frameworkResult.CreateFramework.FrameworkEdge.Node.ID
// Create control
createControlQuery := `
mutation CreateControl($input: CreateControlInput!) {
createControl(input: $input) {
controlEdge {
node {
id
}
}
}
}
`
var controlResult struct {
CreateControl struct {
ControlEdge struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"controlEdge"`
} `json:"createControl"`
}
err = owner.Execute(createControlQuery, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "SubResolver Test Control",
"description": "Test description",
"sectionTitle": "Section 1",
"bestPractice": true,
"maturityLevel": "INITIAL",
},
}, &controlResult)
require.NoError(t, err)
controlID := controlResult.CreateControl.ControlEdge.Node.ID
// Create a measure and link it
createMeasureQuery := `
mutation CreateMeasure($input: CreateMeasureInput!) {
createMeasure(input: $input) {
measureEdge {
node {
id
}
}
}
}
`
var measureResult struct {
CreateMeasure struct {
MeasureEdge struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"measureEdge"`
} `json:"createMeasure"`
}
err = owner.Execute(createMeasureQuery, map[string]any{
"input": map[string]any{
"organizationId": owner.GetOrganizationID().String(),
"name": "Test Measure for Control",
"category": "POLICY",
},
}, &measureResult)
require.NoError(t, err)
measureID := measureResult.CreateMeasure.MeasureEdge.Node.ID
// Create mapping
createMappingQuery := `
mutation CreateControlMeasureMapping($input: CreateControlMeasureMappingInput!) {
createControlMeasureMapping(input: $input) {
controlEdge {
node {
id
}
}
}
}
`
var mappingResult struct {
CreateControlMeasureMapping struct {
ControlEdge struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"controlEdge"`
} `json:"createControlMeasureMapping"`
}
err = owner.Execute(createMappingQuery, map[string]any{
"input": map[string]any{
"controlId": controlID,
"measureId": measureID,
},
}, &mappingResult)
require.NoError(t, err)
t.Run("Control framework sub-resolver", func(t *testing.T) {
query := `
query GetControlFramework($id: ID!) {
node(id: $id) {
... on Control {
id
framework {
id
name
}
}
}
}
`
var result struct {
Node struct {
ID string `json:"id"`
Framework struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"framework"`
} `json:"node"`
}
err := owner.Execute(query, map[string]any{
"id": controlID,
}, &result)
require.NoError(t, err)
assert.Equal(t, frameworkID, result.Node.Framework.ID)
})
t.Run("Control measures sub-resolver", func(t *testing.T) {
query := `
query GetControlMeasures($id: ID!) {
node(id: $id) {
... on Control {
id
measures(first: 10) {
edges {
node {
id
name
}
}
}
}
}
}
`
var result struct {
Node struct {
ID string `json:"id"`
Measures struct {
Edges []struct {
Node struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"node"`
} `json:"edges"`
} `json:"measures"`
} `json:"node"`
}
err := owner.Execute(query, map[string]any{
"id": controlID,
}, &result)
require.NoError(t, err)
assert.GreaterOrEqual(t, len(result.Node.Measures.Edges), 1)
})
}