From f3e0fbe057c4a1803569df97e13f74d1fae8c347 Mon Sep 17 00:00:00 2001 From: Lance Bragstad Date: Thu, 21 Jul 2022 15:45:54 -0500 Subject: [PATCH] Add a table for compliance results This commit adds the basic schema we've outlined in https://github.com/rhmdnd/compserv/issues/84#issuecomment-1182304033 Future patches might include additional columns we need, depending on how this is used. Fixes #31 --- .../000009_create_results_table.down.sql | 1 + migrations/000009_create_results_table.up.sql | 14 ++ tests/integration_test.go | 122 +++++++++++++++- tests/utils.go | 132 +++++++++++++++++- 4 files changed, 263 insertions(+), 6 deletions(-) create mode 100644 migrations/000009_create_results_table.down.sql create mode 100644 migrations/000009_create_results_table.up.sql diff --git a/migrations/000009_create_results_table.down.sql b/migrations/000009_create_results_table.down.sql new file mode 100644 index 00000000..e2882ffc --- /dev/null +++ b/migrations/000009_create_results_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS results; diff --git a/migrations/000009_create_results_table.up.sql b/migrations/000009_create_results_table.up.sql new file mode 100644 index 00000000..d187aecc --- /dev/null +++ b/migrations/000009_create_results_table.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS results ( + id uuid PRIMARY KEY, + name VARCHAR(255), + outcome VARCHAR(255), + instruction TEXT, + control_id UUID, + metadata_id UUID, + subject_id UUID, + assessment_id UUID, + CONSTRAINT fk_results_control_id FOREIGN KEY (control_id) REFERENCES controls (id), + CONSTRAINT fk_results_metadata_id FOREIGN KEY (metadata_id) REFERENCES metadata (id), + CONSTRAINT fk_results_subject_id FOREIGN KEY (subject_id) REFERENCES subjects (id), + CONSTRAINT fk_results_assessment_id FOREIGN KEY (assessment_id) REFERENCES assessments (id) +); diff --git a/tests/integration_test.go b/tests/integration_test.go index 98d3f723..8a1d3c00 100644 --- a/tests/integration_test.go +++ b/tests/integration_test.go @@ -303,7 +303,7 @@ func TestMigration(t *testing.T) { // nolint:paralleltest // database tests shou // Upgrade the database and make sure all upgrades apply cleanly. err = m.Up() version, dirty, _ = m.Version() - expectedVersion = uint(8) + expectedVersion = uint(9) assert.Equal(t, expectedVersion, version, "Database version mismatch: want %d but got %d", expectedVersion, version) assert.Equal(t, false, dirty, "Database state mismatch: want %t but got %t", false, dirty) assert.Equal(t, err, nil, "Error upgrading the database: %s", err) @@ -456,6 +456,59 @@ func TestAssessmentMigration(t *testing.T) { // nolint:paralleltest // database assert.False(t, result, "Table exist: %s", tableName) } +func TestResultMigration(t *testing.T) { // nolint:paralleltest // database tests should run serially + m := getMigrationHelper(t) + gormDB := getGormHelper() + tableName := "results" + + if err := m.Migrate(8); err != nil { + t.Fatalf("Unable to upgrade database: %s", err) + } + + // Ensure the metadata table doesn't exist before the upgrade + result := gormDB.Migrator().HasTable(tableName) + assert.False(t, result, "Table exists prior to migration: %s", tableName) + + // Ensure the table exists after running the migration + if err := m.Migrate(9); err != nil { + t.Fatalf("Unable to upgrade database: %s", err) + } + + result = gormDB.Migrator().HasTable(tableName) + assert.True(t, result, "Table doesn't exist: %s", tableName) + + // Check to make sure it has the expected columns + type results struct{} + columns := []string{"id", "name", "outcome", "instruction", "control_id", "metadata_id", "subject_id", "assessment_id"} + for _, s := range columns { + result = gormDB.Migrator().HasColumn(&results{}, s) + assert.True(t, result, "Column doesn't exist: %s", s) + } + + constraintName := "fk_results_control_id" + result = gormDB.Migrator().HasConstraint(&results{}, constraintName) + assert.True(t, result, "Table doesn't have constraint: %s", constraintName) + + constraintName = "fk_results_metadata_id" + result = gormDB.Migrator().HasConstraint(&results{}, constraintName) + assert.True(t, result, "Table doesn't have constraint: %s", constraintName) + + constraintName = "fk_results_subject_id" + result = gormDB.Migrator().HasConstraint(&results{}, constraintName) + assert.True(t, result, "Table doesn't have constraint: %s", constraintName) + + constraintName = "fk_results_assessment_id" + result = gormDB.Migrator().HasConstraint(&results{}, constraintName) + assert.True(t, result, "Table doesn't have constraint: %s", constraintName) + + // Ensure the table is removed on downgrade + if err := m.Migrate(8); err != nil { + t.Fatalf("Unable to upgrade database: %s", err) + } + result = gormDB.Migrator().HasTable(tableName) + assert.False(t, result, "Table exists after downgrade: %s", tableName) +} + func TestInsertAssessmentSucceeds(t *testing.T) { // nolint:paralleltest // database tests should run serially m := getMigrationHelper(t) gormDB := getGormHelper() @@ -463,7 +516,6 @@ func TestInsertAssessmentSucceeds(t *testing.T) { // nolint:paralleltest // data if err := m.Migrate(4); err != nil { t.Fatalf("Unable to upgrade database: %s", err) } - id := getUUIDString() metadataID, err := insertMetadata() if err != nil { @@ -471,15 +523,75 @@ func TestInsertAssessmentSucceeds(t *testing.T) { // nolint:paralleltest // data } name := getUUIDString() - assessment := Assessments{ID: id, Name: name, MetadataID: metadataID} + assessment := Assessment{ID: id, Name: name, MetadataID: metadataID} err = gormDB.Create(&assessment).Error assert.Nil(t, err) - a := Assessments{} + a := Assessment{} + gormDB.First(&a, "id = ?", id) + + e := Assessment{ID: id, Name: name, MetadataID: metadataID} + assert.Equal(t, e.ID, a.ID, "expected %s got %s", e.ID, a.ID) + assert.Equal(t, e.Name, a.Name, "expected %s got %s", e.Name, a.Name) + assert.Equal(t, e.MetadataID, a.MetadataID, "expected %s got %s", e.MetadataID, a.MetadataID) +} + +func TestInsertResultSucceeds(t *testing.T) { // nolint:paralleltest // database tests should run serially + m := getMigrationHelper(t) + gormDB := getGormHelper() + + if err := m.Migrate(9); err != nil { + t.Fatalf("Unable to upgrade database: %s", err) + } + + metadataID, err := insertMetadata() + if err != nil { + t.Fatalf("Unable to create necessary metadata: %s", err) + } + subjectID, err := insertSubject() + if err != nil { + t.Fatalf("Unable to create necessary subject: %s", err) + } + controlID, err := insertControl() + if err != nil { + t.Fatalf("Unable to create necessary control: %s", err) + } + assessmentID, err := insertAssessment() + if err != nil { + t.Fatalf("Unable to create necessary assessment: %s", err) + } + + id := getUUIDString() + name := getUUIDString() + outcome := getUUIDString() + instruction := strings.Repeat(getUUIDString(), 100) + + r := Result{ + ID: id, Name: name, ControlID: controlID, + Outcome: outcome, Instruction: instruction, + MetadataID: metadataID, SubjectID: subjectID, + AssessmentID: assessmentID, + } + + err = gormDB.Create(&r).Error + assert.Nil(t, err) + + a := Result{} gormDB.First(&a, "id = ?", id) - e := Assessments{ID: id, Name: name, MetadataID: metadataID} + e := Result{ + ID: id, Name: name, ControlID: controlID, + Outcome: outcome, Instruction: instruction, + MetadataID: metadataID, SubjectID: subjectID, + AssessmentID: assessmentID, + } + assert.Equal(t, e.ID, a.ID, "expected %s got %s", e.ID, a.ID) assert.Equal(t, e.Name, a.Name, "expected %s got %s", e.Name, a.Name) + assert.Equal(t, e.ControlID, a.ControlID, "expected %s got %s", e.ControlID, a.ControlID) + assert.Equal(t, e.Outcome, a.Outcome, "expected %s got %s", e.Outcome, a.Outcome) + assert.Equal(t, e.Instruction, a.Instruction, "expected %s got %s", e.Instruction, a.Instruction) assert.Equal(t, e.MetadataID, a.MetadataID, "expected %s got %s", e.MetadataID, a.MetadataID) + assert.Equal(t, e.SubjectID, a.SubjectID, "expected %s got %s", e.SubjectID, a.SubjectID) + assert.Equal(t, e.AssessmentID, a.AssessmentID, "expected %s got %s", e.AssessmentID, a.AssessmentID) } diff --git a/tests/utils.go b/tests/utils.go index 2e1647b7..be48d546 100644 --- a/tests/utils.go +++ b/tests/utils.go @@ -15,7 +15,29 @@ import ( "gorm.io/gorm" ) -type Assessments struct { +type Catalog struct { + ID string + Name string + MetadataID string + Content string +} + +type Profile struct { + ID string + Name string + MetadataID string + CatalogID string +} + +type Control struct { + ID string + Name string + Severity string + ProfileID string + MetadataID string +} + +type Assessment struct { ID string Name string MetadataID string @@ -37,6 +59,17 @@ type Subject struct { MetadataID sql.NullString } +type Result struct { + ID string + Name string + Outcome string + Instruction string + ControlID string + MetadataID string + SubjectID string + AssessmentID string +} + func getDatabaseConnection(t *testing.T) *sql.DB { t.Helper() // Generlize this so that it can be used to connect to any Postgres @@ -127,3 +160,100 @@ func insertMetadata() (string, error) { return id, nil } + +func insertSubject() (string, error) { + gormDB := getGormHelper() + + id := getUUIDString() + name := getUUIDString() + subjectTypeStr := getUUIDString() + + s := Subject{ID: id, Name: name, Type: subjectTypeStr} + if err := gormDB.Create(&s).Error; err != nil { + return "", err + } + + return id, nil +} + +func insertControl() (string, error) { + gormDB := getGormHelper() + + id := getUUIDString() + name := getUUIDString() + metadataID, err := insertMetadata() + if err != nil { + return "", err + } + profileID, err := insertProfile() + if err != nil { + return "", err + } + severity := getUUIDString() + + c := Control{ID: id, Name: name, Severity: severity, ProfileID: profileID, MetadataID: metadataID} + if err := gormDB.Create(&c).Error; err != nil { + return "", err + } + + return id, nil +} + +func insertProfile() (string, error) { + gormDB := getGormHelper() + + id := getUUIDString() + name := getUUIDString() + metadataID, err := insertMetadata() + if err != nil { + return "", err + } + catalogID, err := insertCatalog() + if err != nil { + return "", err + } + + p := Profile{ID: id, Name: name, MetadataID: metadataID, CatalogID: catalogID} + if err := gormDB.Create(&p).Error; err != nil { + return "", err + } + + return id, nil +} + +func insertCatalog() (string, error) { + gormDB := getGormHelper() + + id := getUUIDString() + name := getUUIDString() + metadataID, err := insertMetadata() + if err != nil { + return "", err + } + content := getUUIDString() + + c := Catalog{ID: id, Name: name, MetadataID: metadataID, Content: content} + if err := gormDB.Create(&c).Error; err != nil { + return "", err + } + + return id, nil +} + +func insertAssessment() (string, error) { + gormDB := getGormHelper() + + id := getUUIDString() + name := getUUIDString() + metadataID, err := insertMetadata() + if err != nil { + return "", err + } + + c := Assessment{ID: id, Name: name, MetadataID: metadataID} + if err := gormDB.Create(&c).Error; err != nil { + return "", err + } + + return id, nil +}