Replace implemented column with CMMI maturity level
Drop the boolean implemented/not-implemented state in favor of a mandatory CMMI maturity level enum (NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING) stored as a Postgres enum type. The migration backfills existing rows (NOT_IMPLEMENTED → NONE, IMPLEMENTED → INITIAL), makes the column NOT NULL, and drops the old implemented column and its enum type. - maturityLevel is required on CreateControlInput and non-nullable (!) in the GraphQL schema - CLI displays human-readable CMMI labels instead of raw enum tokens - SOA table and published document use a single Maturity column in place of the old Implemented + Maturity columns - Remove ControlImplementationState type and all implemented references across backend, frontend, CLI, MCP, n8n, and E2E tests Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -59,12 +59,12 @@ func TestControl_Create(t *testing.T) {
|
||||
|
||||
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,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"sectionTitle": "A.5",
|
||||
"name": "Information Security Policies",
|
||||
"description": "Policies for information security",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
@@ -266,11 +266,11 @@ func TestControl_RequiredFields(t *testing.T) {
|
||||
name: "Missing frameworkId should fail",
|
||||
variables: map[string]any{
|
||||
"input": map[string]any{
|
||||
"name": "Test Control",
|
||||
"description": "Test",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"name": "Test Control",
|
||||
"description": "Test",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
},
|
||||
wantError: true,
|
||||
@@ -279,11 +279,11 @@ func TestControl_RequiredFields(t *testing.T) {
|
||||
name: "Missing name should fail",
|
||||
variables: map[string]any{
|
||||
"input": map[string]any{
|
||||
"frameworkId": frameworkID,
|
||||
"description": "Test",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"description": "Test",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
},
|
||||
wantError: true,
|
||||
@@ -292,11 +292,11 @@ func TestControl_RequiredFields(t *testing.T) {
|
||||
name: "Missing sectionTitle should fail",
|
||||
variables: map[string]any{
|
||||
"input": map[string]any{
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Test Control",
|
||||
"description": "Test",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Test Control",
|
||||
"description": "Test",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
},
|
||||
wantError: true,
|
||||
@@ -305,11 +305,11 @@ func TestControl_RequiredFields(t *testing.T) {
|
||||
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,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Test Control",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
},
|
||||
wantError: true,
|
||||
@@ -318,17 +318,17 @@ func TestControl_RequiredFields(t *testing.T) {
|
||||
name: "Missing bestPractice should fail",
|
||||
variables: map[string]any{
|
||||
"input": map[string]any{
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Test Control",
|
||||
"description": "Test",
|
||||
"sectionTitle": "Section 1",
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Test Control",
|
||||
"description": "Test",
|
||||
"sectionTitle": "Section 1",
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
},
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "Missing implemented should fail",
|
||||
name: "Missing maturityLevel should fail",
|
||||
variables: map[string]any{
|
||||
"input": map[string]any{
|
||||
"frameworkId": frameworkID,
|
||||
@@ -418,12 +418,12 @@ func TestControl_OmittableDescription(t *testing.T) {
|
||||
|
||||
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,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Omittable Test Control",
|
||||
"description": "Initial description",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
}, &createResult)
|
||||
require.NoError(t, err)
|
||||
@@ -555,8 +555,8 @@ func TestControl_MaturityLevel(t *testing.T) {
|
||||
CreateControl struct {
|
||||
ControlEdge struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
MaturityLevel *string `json:"maturityLevel"`
|
||||
ID string `json:"id"`
|
||||
MaturityLevel string `json:"maturityLevel"`
|
||||
} `json:"node"`
|
||||
} `json:"controlEdge"`
|
||||
} `json:"createControl"`
|
||||
@@ -565,26 +565,26 @@ func TestControl_MaturityLevel(t *testing.T) {
|
||||
type updateResult struct {
|
||||
UpdateControl struct {
|
||||
Control struct {
|
||||
ID string `json:"id"`
|
||||
MaturityLevel *string `json:"maturityLevel"`
|
||||
ID string `json:"id"`
|
||||
MaturityLevel string `json:"maturityLevel"`
|
||||
} `json:"control"`
|
||||
} `json:"updateControl"`
|
||||
}
|
||||
|
||||
t.Run("create without maturityLevel returns null", func(t *testing.T) {
|
||||
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 without maturity",
|
||||
"description": "control without maturity description",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"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.Nil(t, res.CreateControl.ControlEdge.Node.MaturityLevel)
|
||||
assert.Equal(t, "INITIAL", res.CreateControl.ControlEdge.Node.MaturityLevel)
|
||||
})
|
||||
|
||||
t.Run("create with maturityLevel persists value", func(t *testing.T) {
|
||||
@@ -596,25 +596,23 @@ func TestControl_MaturityLevel(t *testing.T) {
|
||||
"name": "Control with maturity",
|
||||
"description": "control with maturity description",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"maturityLevel": "DEFINED",
|
||||
},
|
||||
}, &res)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.CreateControl.ControlEdge.Node.MaturityLevel)
|
||||
assert.Equal(t, "DEFINED", *res.CreateControl.ControlEdge.Node.MaturityLevel)
|
||||
assert.Equal(t, "DEFINED", res.CreateControl.ControlEdge.Node.MaturityLevel)
|
||||
})
|
||||
|
||||
t.Run("update lifecycle: set, change, clear, omit", func(t *testing.T) {
|
||||
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,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"sectionTitle": "M.3",
|
||||
"name": "Lifecycle control",
|
||||
"description": "lifecycle control description",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
}, &created)
|
||||
require.NoError(t, err)
|
||||
@@ -629,8 +627,7 @@ func TestControl_MaturityLevel(t *testing.T) {
|
||||
},
|
||||
}, &setRes)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, setRes.UpdateControl.Control.MaturityLevel)
|
||||
assert.Equal(t, "INITIAL", *setRes.UpdateControl.Control.MaturityLevel)
|
||||
assert.Equal(t, "INITIAL", setRes.UpdateControl.Control.MaturityLevel)
|
||||
|
||||
// change
|
||||
var changeRes updateResult
|
||||
@@ -641,30 +638,9 @@ func TestControl_MaturityLevel(t *testing.T) {
|
||||
},
|
||||
}, &changeRes)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, changeRes.UpdateControl.Control.MaturityLevel)
|
||||
assert.Equal(t, "OPTIMIZING", *changeRes.UpdateControl.Control.MaturityLevel)
|
||||
|
||||
// clear (explicit null)
|
||||
var clearRes updateResult
|
||||
err = owner.Execute(updateControlQuery, map[string]any{
|
||||
"input": map[string]any{
|
||||
"id": controlID,
|
||||
"maturityLevel": nil,
|
||||
},
|
||||
}, &clearRes)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, clearRes.UpdateControl.Control.MaturityLevel)
|
||||
|
||||
// set again, then omit field on next update -> stays unchanged
|
||||
var setAgain updateResult
|
||||
err = owner.Execute(updateControlQuery, map[string]any{
|
||||
"input": map[string]any{
|
||||
"id": controlID,
|
||||
"maturityLevel": "MANAGED",
|
||||
},
|
||||
}, &setAgain)
|
||||
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{
|
||||
@@ -673,8 +649,7 @@ func TestControl_MaturityLevel(t *testing.T) {
|
||||
},
|
||||
}, &omitRes)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, omitRes.UpdateControl.Control.MaturityLevel)
|
||||
assert.Equal(t, "MANAGED", *omitRes.UpdateControl.Control.MaturityLevel)
|
||||
assert.Equal(t, "OPTIMIZING", omitRes.UpdateControl.Control.MaturityLevel)
|
||||
})
|
||||
|
||||
t.Run("invalid maturityLevel is rejected", func(t *testing.T) {
|
||||
@@ -686,7 +661,6 @@ func TestControl_MaturityLevel(t *testing.T) {
|
||||
"name": "Bad maturity",
|
||||
"description": "bad maturity description",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"maturityLevel": "BOGUS",
|
||||
},
|
||||
}, &res)
|
||||
@@ -755,12 +729,12 @@ func TestControl_SubResolvers(t *testing.T) {
|
||||
|
||||
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,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"name": "SubResolver Test Control",
|
||||
"description": "Test description",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
}, &controlResult)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -78,12 +78,12 @@ func TestControlMeasureMapping_CreateDelete(t *testing.T) {
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Control for Mapping",
|
||||
"description": "Test control for mapping",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Control for Mapping",
|
||||
"description": "Test control for mapping",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
}, &createControlResult)
|
||||
require.NoError(t, err)
|
||||
@@ -358,12 +358,12 @@ func TestControlDocumentMapping_CreateDelete(t *testing.T) {
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Control for Document Mapping",
|
||||
"description": "Test control",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Control for Document Mapping",
|
||||
"description": "Test control",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
}, &createControlResult)
|
||||
require.NoError(t, err)
|
||||
@@ -498,12 +498,12 @@ func TestControlAuditMapping_CreateDelete(t *testing.T) {
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Control for Audit Mapping",
|
||||
"description": "Test control",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Control for Audit Mapping",
|
||||
"description": "Test control",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
}, &createControlResult)
|
||||
require.NoError(t, err)
|
||||
@@ -636,12 +636,12 @@ func TestControlSnapshotMapping_CreateDelete(t *testing.T) {
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Control for Snapshot Mapping",
|
||||
"description": "Test control",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"implemented": "IMPLEMENTED",
|
||||
"frameworkId": frameworkID,
|
||||
"name": "Control for Snapshot Mapping",
|
||||
"description": "Test control",
|
||||
"sectionTitle": "Section 1",
|
||||
"bestPractice": true,
|
||||
"maturityLevel": "INITIAL",
|
||||
},
|
||||
}, &createControlResult)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -446,7 +446,7 @@ func TestRBAC(t *testing.T) {
|
||||
client: owner,
|
||||
query: createControlMutation,
|
||||
variables: func() map[string]any {
|
||||
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Owner"), "bestPractice": true, "implemented": "IMPLEMENTED"}}
|
||||
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Owner"), "bestPractice": true, "maturityLevel": "INITIAL"}}
|
||||
},
|
||||
shouldAllow: true,
|
||||
},
|
||||
@@ -456,7 +456,7 @@ func TestRBAC(t *testing.T) {
|
||||
client: admin,
|
||||
query: createControlMutation,
|
||||
variables: func() map[string]any {
|
||||
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Admin"), "bestPractice": true, "implemented": "IMPLEMENTED"}}
|
||||
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Admin"), "bestPractice": true, "maturityLevel": "INITIAL"}}
|
||||
},
|
||||
shouldAllow: true,
|
||||
},
|
||||
@@ -466,7 +466,7 @@ func TestRBAC(t *testing.T) {
|
||||
client: viewer,
|
||||
query: createControlMutation,
|
||||
variables: func() map[string]any {
|
||||
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Viewer"), "bestPractice": true, "implemented": "IMPLEMENTED"}}
|
||||
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Viewer"), "bestPractice": true, "maturityLevel": "INITIAL"}}
|
||||
},
|
||||
shouldAllow: false,
|
||||
},
|
||||
|
||||
@@ -248,12 +248,12 @@ func CreateControl(c *testutil.Client, frameworkID string, attrs ...Attrs) strin
|
||||
`
|
||||
|
||||
input := map[string]any{
|
||||
"frameworkId": frameworkID,
|
||||
"name": a.getString("name", SafeName("Control")),
|
||||
"description": a.getString("description", "Test control description"),
|
||||
"sectionTitle": a.getString("sectionTitle", fmt.Sprintf("Section %s", gofakeit.LetterN(3))),
|
||||
"bestPractice": a.getBool("bestPractice", true),
|
||||
"implemented": a.getString("implemented", "IMPLEMENTED"),
|
||||
"frameworkId": frameworkID,
|
||||
"name": a.getString("name", SafeName("Control")),
|
||||
"description": a.getString("description", "Test control description"),
|
||||
"sectionTitle": a.getString("sectionTitle", fmt.Sprintf("Section %s", gofakeit.LetterN(3))),
|
||||
"bestPractice": a.getBool("bestPractice", true),
|
||||
"maturityLevel": a.getString("maturityLevel", "INITIAL"),
|
||||
}
|
||||
|
||||
if justification := a.getStringPtr("notImplementedJustification"); justification != nil {
|
||||
@@ -502,8 +502,8 @@ func (b *ControlBuilder) WithBestPractice(bestPractice bool) *ControlBuilder {
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *ControlBuilder) WithImplemented(implemented string) *ControlBuilder {
|
||||
b.attrs["implemented"] = implemented
|
||||
func (b *ControlBuilder) WithMaturityLevel(maturityLevel string) *ControlBuilder {
|
||||
b.attrs["maturityLevel"] = maturityLevel
|
||||
return b
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user