From 3f535f8eb1681e422816e46e111cc0bb1f344128 Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Sun, 15 May 2016 00:22:38 -0400 Subject: [PATCH] add delete method for profiles --- workflow/datastore.go | 33 ++++++- workflow/datastore_test.go | 56 +++++++++++ workflow/profile.go | 2 +- workflow/profile_store.go | 57 +++++++---- workflow/profile_store_test.go | 158 +++++++++++++++++++++++++++++++ workflow/testdata/sample_profile | 61 ++++++++++++ workflow/workflow.go | 4 +- workflow/workflow_store.go | 9 ++ workflow/workflow_store_test.go | 115 +++++++--------------- 9 files changed, 389 insertions(+), 106 deletions(-) create mode 100644 workflow/datastore_test.go create mode 100644 workflow/testdata/sample_profile diff --git a/workflow/datastore.go b/workflow/datastore.go index dd0403c0..298c7f69 100644 --- a/workflow/datastore.go +++ b/workflow/datastore.go @@ -2,6 +2,7 @@ package workflow import ( "fmt" + "strings" "time" kitlog "github.com/go-kit/kit/log" @@ -15,15 +16,21 @@ type Datastore interface { // Create adds a new workflow to the datastore CreateWorkflow(wf *Workflow) (*Workflow, error) + // Workflows can query the datastore for workflows + // Workflows accepts one or more params as filters + // to narrow down the number of results Workflows(params ...interface{}) ([]Workflow, error) // CreateProfile adds a new profile to the datastore, // If a profile already exists, an error will be returned - CreateProfile(pr *Profile) (*Profile, error) + CreateProfile(p *Profile) (*Profile, error) - // Profiles can query the datastore for one or more profiles - // and accepts one or more params as filters - // Example: Profiles(Identifier{"com.example.id")} + // DeleteProfile removes a profile from the datastore + DeleteProfile(pr *Profile) error + + // Profiles can query the datastore for profiles + // Profiles accepts one or more params as filters + // to narrow down the number of results Profiles(params ...interface{}) ([]Profile, error) } @@ -36,6 +43,22 @@ type whereer interface { where() string } +// add WHERE clause from params +func addWhereFilters(stmt string, params ...interface{}) string { + var where []string + for _, param := range params { + if f, ok := param.(whereer); ok { + where = append(where, f.where()) + } + } + + if len(where) != 0 { + whereFilter := strings.Join(where, ",") + stmt = fmt.Sprintf("%s WHERE %s", selectProfilesStmt, whereFilter) + } + return stmt +} + //NewDB creates a Datastore func NewDB(driver, conn string, logger kitlog.Logger) (Datastore, error) { switch driver { @@ -76,7 +99,7 @@ func migrate(db *sqlx.DB) { CREATE TABLE IF NOT EXISTS profiles ( profile_uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4(), - identifier text UNIQUE NOT NULL, + payload_identifier text UNIQUE NOT NULL CHECK (payload_identifier <> ''), profile_data bytea ); diff --git a/workflow/datastore_test.go b/workflow/datastore_test.go new file mode 100644 index 00000000..081b68cc --- /dev/null +++ b/workflow/datastore_test.go @@ -0,0 +1,56 @@ +package workflow + +import ( + "math/rand" + "os" + "testing" + "time" + + "github.com/go-kit/kit/log" + "github.com/jmoiron/sqlx" +) + +// Test that a new datastore is succesfuly created and destroyed. +func TestNewDB(t *testing.T) { + _ = datastore(t) + defer teardown() +} + +var ( + testConn = "user=micromdm password=micromdm dbname=micromdm sslmode=disable" +) + +func datastore(t *testing.T) Datastore { + logger := log.NewLogfmtLogger(os.Stderr) + ds, err := NewDB("postgres", testConn, logger) + if err != nil { + t.Fatal(err) + } + return ds +} + +func teardown() { + db, err := sqlx.Open("postgres", testConn) + if err != nil { + panic(err) + } + + drop := ` + DROP TABLE IF EXISTS workflow_profile; + DROP TABLE IF EXISTS workflow_workflow; + DROP TABLE IF EXISTS profiles; + DROP TABLE IF EXISTS workflows; + ` + db.MustExec(drop) + defer db.Close() +} + +func randomString(strlen int) string { + rand.Seed(time.Now().UTC().UnixNano()) + const chars = "abcdefghijklmnopqrstuvwxyz0123456789" + result := make([]byte, strlen) + for i := 0; i < strlen; i++ { + result[i] = chars[rand.Intn(len(chars))] + } + return string(result) +} diff --git a/workflow/profile.go b/workflow/profile.go index 5d19ebae..8186b99a 100644 --- a/workflow/profile.go +++ b/workflow/profile.go @@ -3,6 +3,6 @@ package workflow // Profile is an Apple Configuration profile type Profile struct { UUID string `plist:"-" json:"profile_uuid,omitempty" db:"profile_uuid"` - PayloadIdentifier string `json:"payload_identifier" db:"identifier"` + PayloadIdentifier string `json:"payload_identifier" db:"payload_identifier"` ProfileData string `json:"profile_data,omitempty" db:"profile_data"` } diff --git a/workflow/profile_store.go b/workflow/profile_store.go index 76684c3e..f60530e0 100644 --- a/workflow/profile_store.go +++ b/workflow/profile_store.go @@ -3,19 +3,39 @@ package workflow import ( "database/sql" "fmt" - "strings" "github.com/pkg/errors" ) // sql statements var ( - addProfileStmt = `INSERT INTO profiles (identifier, profile_data) VALUES ($1, $2) - ON CONFLICT ON CONSTRAINT profiles_identifier_key DO NOTHING - RETURNING profile_uuid;` - selectProfilesStmt = `SELECT profile_uuid, identifier FROM profiles` + addProfileStmt = `INSERT INTO profiles + (payload_identifier, profile_data) VALUES ($1, $2) + ON CONFLICT ON CONSTRAINT profiles_payload_identifier_key DO NOTHING + RETURNING profile_uuid;` + + selectProfilesStmt = `SELECT profile_uuid, payload_identifier, profile_data FROM profiles` + deleteProfileStmt = `DELETE FROM profiles` ) +// ProfileUUID is a filter we can add as a parameter to narrow down the list of returned results +type ProfileUUID struct { + UUID string +} + +func (p ProfileUUID) where() string { + return fmt.Sprintf("profile_uuid = '%s'", p.UUID) +} + +// PayloadIdentifier is a filter we can add as a parameter to narrow down the list of returned results +type PayloadIdentifier struct { + PayloadIdentifier string +} + +func (p PayloadIdentifier) where() string { + return fmt.Sprintf("payload_identifier = '%s'", p.PayloadIdentifier) +} + func (store pgStore) CreateProfile(prf *Profile) (*Profile, error) { err := store.QueryRow(addProfileStmt, prf.PayloadIdentifier, prf.ProfileData).Scan(&prf.UUID) if err == sql.ErrNoRows { @@ -27,19 +47,24 @@ func (store pgStore) CreateProfile(prf *Profile) (*Profile, error) { return prf, nil } +func (store pgStore) DeleteProfile(p *Profile) error { + stmt := deleteProfileStmt + if p.UUID == "" || p.PayloadIdentifier == "" { + return nil // just don't do anything + } + if p.UUID != "" { + stmt = addWhereFilters(stmt, ProfileUUID{p.UUID}) + } + _, err := store.Exec(stmt) + if err != nil { + return errors.Wrap(err, "delete profile") + } + return nil +} + func (store pgStore) Profiles(params ...interface{}) ([]Profile, error) { stmt := selectProfilesStmt - var where []string - for _, param := range params { - if f, ok := param.(whereer); ok { - where = append(where, f.where()) - } - } - - if len(where) != 0 { - whereFilter := strings.Join(where, ",") - stmt = fmt.Sprintf("%s WHERE %s", selectProfilesStmt, whereFilter) - } + stmt = addWhereFilters(stmt, params...) var profiles []Profile err := store.Select(&profiles, stmt) diff --git a/workflow/profile_store_test.go b/workflow/profile_store_test.go index 0e59ea27..159a0fcd 100644 --- a/workflow/profile_store_test.go +++ b/workflow/profile_store_test.go @@ -1 +1,159 @@ package workflow + +import ( + "fmt" + "io/ioutil" + "math/rand" + "reflect" + "testing" + "testing/quick" +) + +func TestDeleteProfile(t *testing.T) { + ds := datastore(t) + defer teardown() + testProfiles := addTestProfiles(t, ds, 5) + + for _, p := range testProfiles { + err := ds.DeleteProfile(&p) + if err != nil { + t.Fatal(err) + } + + } + + empty := Profile{} + err := ds.DeleteProfile(&empty) + if err != nil { + t.Error(err) + } + badUUIDProfile := Profile{ + UUID: "bad.uuid", + PayloadIdentifier: "with.bad.uuid", + } + err = ds.DeleteProfile(&badUUIDProfile) + if err == nil { + t.Fatal("expected an error but got nil") + } + +} + +func TestRetrieveProfiles(t *testing.T) { + ds := datastore(t) + defer teardown() + testProfiles := addTestProfiles(t, ds, 5) + // retrieve all + profiles, err := ds.Profiles() + if err != nil { + t.Fatal(err) + } + if len(profiles) != 5 { + t.Error("expected", 5, "got", len(profiles)) + } + + for _, p := range testProfiles { + byUUID, err := ds.Profiles(ProfileUUID{p.UUID}) + if err != nil { + t.Fatal(err) + } + if len(byUUID) != 1 { + t.Log("filtering by UUID should only return 1 result") + t.Fatal("expected", 1, "got", len(byUUID)) + } + + uuid := byUUID[0].UUID + if p.UUID != uuid { + t.Log("result should have the same UUID as the one in the query") + t.Fatal("expected", p.UUID, "got", uuid) + + } + } + + badUUIDQuery := ProfileUUID{"bad_uuid"} + _, err = ds.Profiles(badUUIDQuery) + if err == nil { + t.Fatal("expected an error but got nil") + + } +} + +// Generates new Profile types and stores them in the datastore +func TestCreateProfile(t *testing.T) { + ds := datastore(t) + defer teardown() + + assertion := func(pf Profile) bool { + newPrf, err := ds.CreateProfile(&pf) + if err != nil { + t.Fatal(err) + return false + } + if newPrf.UUID == "" || newPrf.PayloadIdentifier != pf.PayloadIdentifier { + return false + } + + // now try duplicates + + _, err = ds.CreateProfile(&pf) + if err != ErrExists || err == nil { + t.Log("ds should not create duplicate resources") + t.Fatal("expected", ErrExists, "got", err) + return false + } + return true + } + + if err := quick.Check(assertion, nil); err != nil { + t.Error(err) + } + + // empty profile test + empty := Profile{} + _, err := ds.CreateProfile(&empty) + if err == nil { + t.Fatal("expected an error but got nil") + } +} + +// add some profiles to the datastore for quick testing +func addTestProfiles(t *testing.T, ds Datastore, numProfiles int) []Profile { + var profiles []Profile + for i := 0; i < numProfiles; i++ { + input := randomProfile() + newProfile, err := ds.CreateProfile(&input) + if err != nil { + t.Fatal(err) + } + profiles = append(profiles, *newProfile) + } + return profiles +} + +func randomProfile() Profile { + vrf, ok := quick.Value(reflect.TypeOf(Profile{}), rand.New(rand.NewSource(1))) + if !ok { + panic("randomProfile: no value") + } + if f, ok := vrf.Interface().(Profile); ok { + return f + } + return Profile{} +} + +// Generate a random profile +func (pf Profile) Generate(rand *rand.Rand, size int) reflect.Value { + a := randomString(3) + b := randomString(10) + c := randomString(5) + data, err := ioutil.ReadFile("testdata/sample_profile") + if err != nil { + panic(err) + } + randomIdnetifier := fmt.Sprintf("%v.%v.%v", a, b, c) + randomProfile := Profile{ + PayloadIdentifier: randomIdnetifier, + ProfileData: string(data), + } + return reflect.ValueOf(randomProfile) + +} diff --git a/workflow/testdata/sample_profile b/workflow/testdata/sample_profile new file mode 100644 index 00000000..788ca4cc --- /dev/null +++ b/workflow/testdata/sample_profile @@ -0,0 +1,61 @@ +PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBV +QkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20v +RFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4K +CTxrZXk+UGF5bG9hZENvbnRlbnQ8L2tleT4KCTxhcnJheT4KCQk8ZGljdD4KCQkJPGtleT5BZG1p +bkhvc3RJbmZvPC9rZXk+CgkJCTxzdHJpbmc+SG9zdE5hbWU8L3N0cmluZz4KCQkJPGtleT5BZG1p +bk1heURpc2FibGVNQ1g8L2tleT4KCQkJPGZhbHNlLz4KCQkJPGtleT5BbGxvd0xpc3Q8L2tleT4K +CQkJPGFycmF5Lz4KCQkJPGtleT5BbHdheXNTaG93V29ya2dyb3VwRGlhbG9nPC9rZXk+CgkJCTxm +YWxzZS8+CgkJCTxrZXk+Q29tYmluZVVzZXJXb3JrZ3JvdXBzPC9rZXk+CgkJCTx0cnVlLz4KCQkJ +PGtleT5EZW55TGlzdDwva2V5PgoJCQk8YXJyYXkvPgoJCQk8a2V5PkRpc2FibGVDb25zb2xlQWNj +ZXNzPC9rZXk+CgkJCTxmYWxzZS8+CgkJCTxrZXk+RW5hYmxlRXh0ZXJuYWxBY2NvdW50czwva2V5 +PgoJCQk8ZmFsc2UvPgoJCQk8a2V5PkZsYXR0ZW5Vc2VyV29ya2dyb3Vwczwva2V5PgoJCQk8ZmFs +c2UvPgoJCQk8a2V5PkhpZGVBZG1pblVzZXJzPC9rZXk+CgkJCTxmYWxzZS8+CgkJCTxrZXk+SGlk +ZUxvY2FsVXNlcnM8L2tleT4KCQkJPGZhbHNlLz4KCQkJPGtleT5IaWRlTW9iaWxlQWNjb3VudHM8 +L2tleT4KCQkJPGZhbHNlLz4KCQkJPGtleT5JbmNsdWRlTmV0d29ya1VzZXI8L2tleT4KCQkJPGZh +bHNlLz4KCQkJPGtleT5Mb2NhbFVzZXJMb2dpbkVuYWJsZWQ8L2tleT4KCQkJPHRydWUvPgoJCQk8 +a2V5PkxvY2FsVXNlcnNIYXZlV29ya2dyb3Vwczwva2V5PgoJCQk8ZmFsc2UvPgoJCQk8a2V5PlBh +eWxvYWREaXNwbGF5TmFtZTwva2V5PgoJCQk8c3RyaW5nPkxvZ2luIFdpbmRvdzwvc3RyaW5nPgoJ +CQk8a2V5PlBheWxvYWRFbmFibGVkPC9rZXk+CgkJCTx0cnVlLz4KCQkJPGtleT5QYXlsb2FkSWRl +bnRpZmllcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5naXRodWIuZ3JlZ25lYWdsZS5sb2dpbndpbmRv +dy5sb2dpbndpbmRvdzwvc3RyaW5nPgoJCQk8a2V5PlBheWxvYWRUeXBlPC9rZXk+CgkJCTxzdHJp +bmc+Y29tLmFwcGxlLmxvZ2lud2luZG93PC9zdHJpbmc+CgkJCTxrZXk+UGF5bG9hZFVVSUQ8L2tl +eT4KCQkJPHN0cmluZz5mMjY0ZGQ4Mi01NzMwLTcxNDktMDI0Zi1iNDc1ZjVlOGMwOTA8L3N0cmlu +Zz4KCQkJPGtleT5QYXlsb2FkVmVyc2lvbjwva2V5PgoJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJ +CQk8a2V5PlJlc3RhcnREaXNhYmxlZDwva2V5PgoJCQk8ZmFsc2UvPgoJCQk8a2V5PlJldHJpZXNV +bnRpbEhpbnQ8L2tleT4KCQkJPGludGVnZXI+MzwvaW50ZWdlcj4KCQkJPGtleT5TSE9XRlVMTE5B +TUU8L2tleT4KCQkJPHRydWUvPgoJCQk8a2V5PlNIT1dPVEhFUlVTRVJTX01BTkFHRUQ8L2tleT4K +CQkJPGZhbHNlLz4KCQkJPGtleT5TaHV0RG93bkRpc2FibGVkPC9rZXk+CgkJCTxmYWxzZS8+CgkJ +CTxrZXk+U2xlZXBEaXNhYmxlZDwva2V5PgoJCQk8ZmFsc2UvPgoJCQk8a2V5PlVzZUNvbXB1dGVy +TmFtZUZvckNvbXB1dGVyUmVjb3JkTmFtZTwva2V5PgoJCQk8ZmFsc2UvPgoJCQk8a2V5PmNvbS5h +cHBsZS5sb2dpbi5tY3guRGlzYWJsZUF1dG9Mb2dpbkNsaWVudDwva2V5PgoJCQk8dHJ1ZS8+CgkJ +PC9kaWN0PgoJCTxkaWN0PgoJCQk8a2V5PkRpc2FibGVHdWVzdEFjY291bnQ8L2tleT4KCQkJPHRy +dWUvPgoJCQk8a2V5PkVuYWJsZUd1ZXN0QWNjb3VudDwva2V5PgoJCQk8ZmFsc2UvPgoJCQk8a2V5 +PlBheWxvYWREaXNwbGF5TmFtZTwva2V5PgoJCQk8c3RyaW5nPkxvZ2luIFdpbmRvdzogIE1DWCBQ +cmVmZXJlbmNlczwvc3RyaW5nPgoJCQk8a2V5PlBheWxvYWRFbmFibGVkPC9rZXk+CgkJCTx0cnVl +Lz4KCQkJPGtleT5QYXlsb2FkSWRlbnRpZmllcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5naXRodWIu +Z3JlZ25lYWdsZS5sb2dpbndpbmRvdy5NQ1g8L3N0cmluZz4KCQkJPGtleT5QYXlsb2FkVHlwZTwv +a2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5NQ1g8L3N0cmluZz4KCQkJPGtleT5QYXlsb2FkVVVJ +RDwva2V5PgoJCQk8c3RyaW5nPjQ5N2MzNmQxLTQyMmMtYjVlMC1jMjNlLWIxMTM4YjJkNDkwYTwv +c3RyaW5nPgoJCQk8a2V5PlBheWxvYWRWZXJzaW9uPC9rZXk+CgkJCTxpbnRlZ2VyPjE8L2ludGVn +ZXI+CgkJCTxrZXk+U2xlZXBEaXNhYmxlZDwva2V5PgoJCQk8ZmFsc2UvPgoJCTwvZGljdD4KCQk8 +ZGljdD4KCQkJPGtleT5NdWx0aXBsZVNlc3Npb25FbmFibGVkPC9rZXk+CgkJCTxmYWxzZS8+CgkJ +CTxrZXk+UGF5bG9hZERpc3BsYXlOYW1lPC9rZXk+CgkJCTxzdHJpbmc+TG9naW4gV2luZG93OiAg +R2xvYmFsIFByZWZlcmVuY2VzPC9zdHJpbmc+CgkJCTxrZXk+UGF5bG9hZEVuYWJsZWQ8L2tleT4K +CQkJPHRydWUvPgoJCQk8a2V5PlBheWxvYWRJZGVudGlmaWVyPC9rZXk+CgkJCTxzdHJpbmc+Y29t +LmdpdGh1Yi5ncmVnbmVhZ2xlLmxvZ2lud2luZG93Lkdsb2JhbFByZWZlcmVuY2VzPC9zdHJpbmc+ +CgkJCTxrZXk+UGF5bG9hZFR5cGU8L2tleT4KCQkJPHN0cmluZz4uR2xvYmFsUHJlZmVyZW5jZXM8 +L3N0cmluZz4KCQkJPGtleT5QYXlsb2FkVVVJRDwva2V5PgoJCQk8c3RyaW5nPjc2ZTNhMTUwLTZm +MmMtNzI2MC0wNmQ1LTUwMjYxNWE1ODY4Njwvc3RyaW5nPgoJCQk8a2V5PlBheWxvYWRWZXJzaW9u +PC9rZXk+CgkJCTxpbnRlZ2VyPjE8L2ludGVnZXI+CgkJCTxrZXk+Y29tLmFwcGxlLmF1dG9sb2dv +dXQuQXV0b0xvZ091dERlbGF5PC9rZXk+CgkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJPC9kaWN0 +PgoJPC9hcnJheT4KCTxrZXk+UGF5bG9hZERlc2NyaXB0aW9uPC9rZXk+Cgk8c3RyaW5nPkxvZ2lu +d2luZG93IGNvbmZpZ3VyYXRpb24gc2V0dGluZ3M8L3N0cmluZz4KCTxrZXk+UGF5bG9hZERpc3Bs +YXlOYW1lPC9rZXk+Cgk8c3RyaW5nPkxvZ2lud2luZG93IENvbmZpZ3VyYXRpb248L3N0cmluZz4K +CTxrZXk+UGF5bG9hZElkZW50aWZpZXI8L2tleT4KCTxzdHJpbmc+Y29tLmdpdGh1Yi5ncmVnbmVh +Z2xlLmxvZ2lud2luZG93PC9zdHJpbmc+Cgk8a2V5PlBheWxvYWRPcmdhbml6YXRpb248L2tleT4K +CTxzdHJpbmc+PC9zdHJpbmc+Cgk8a2V5PlBheWxvYWRSZW1vdmFsRGlzYWxsb3dlZDwva2V5PgoJ +PHRydWUvPgoJPGtleT5QYXlsb2FkU2NvcGU8L2tleT4KCTxzdHJpbmc+U3lzdGVtPC9zdHJpbmc+ +Cgk8a2V5PlBheWxvYWRUeXBlPC9rZXk+Cgk8c3RyaW5nPkNvbmZpZ3VyYXRpb248L3N0cmluZz4K +CTxrZXk+UGF5bG9hZFVVSUQ8L2tleT4KCTxzdHJpbmc+MGRjMzE5YTAtYzMzMS0wMTMxLWVlYjUt +MDAwYzI5NGFiODFiPC9zdHJpbmc+Cgk8a2V5PlBheWxvYWRWZXJzaW9uPC9rZXk+Cgk8aW50ZWdl +cj4xPC9pbnRlZ2VyPgo8L2RpY3Q+CjwvcGxpc3Q+Cg== diff --git a/workflow/workflow.go b/workflow/workflow.go index 54500ca2..1c273018 100644 --- a/workflow/workflow.go +++ b/workflow/workflow.go @@ -2,8 +2,8 @@ package workflow import "errors" -// ErrExists is returned if a workflow already exists -var ErrExists = errors.New("workflow already exists. each workflow must have a unique name") +// ErrExists is returned when trying to add a resource which already exists +var ErrExists = errors.New("resource already exists in the datastore") // Workflow describes a workflow that a device will execute // A workflow contains a list of configuration profiles, diff --git a/workflow/workflow_store.go b/workflow/workflow_store.go index 1c580a87..acaacb63 100644 --- a/workflow/workflow_store.go +++ b/workflow/workflow_store.go @@ -16,6 +16,15 @@ var ( selectWorkflowsStmt = `SELECT workflow_uuid, name FROM profiles` ) +// WrkflowUUID is a filter we can add as a parameter to narrow down the list of returned results +type WrkflowUUID struct { + UUID string +} + +func (p WrkflowUUID) where() string { + return fmt.Sprintf("workflow_uuid = '%s'", p.UUID) +} + // Create stores a new workflow in Postgres func (store pgStore) CreateWorkflow(wf *Workflow) (*Workflow, error) { err := store.QueryRow(createWorkflowStmt, wf.Name).Scan(&wf.UUID) diff --git a/workflow/workflow_store_test.go b/workflow/workflow_store_test.go index 40bd8fce..5661c827 100644 --- a/workflow/workflow_store_test.go +++ b/workflow/workflow_store_test.go @@ -2,13 +2,8 @@ package workflow import ( "math/rand" - "os" "reflect" "testing" - "time" - - "github.com/go-kit/kit/log" - "github.com/jmoiron/sqlx" ) func TestDatastoreWorkflows(t *testing.T) { @@ -25,22 +20,40 @@ func (wf Workflow) Generate(rand *rand.Rand, size int) reflect.Value { return reflect.ValueOf(randomWorkflow) } -func randomString(strlen int) string { - rand.Seed(time.Now().UTC().UnixNano()) - const chars = "abcdefghijklmnopqrstuvwxyz0123456789" - result := make([]byte, strlen) - for i := 0; i < strlen; i++ { - result[i] = chars[rand.Intn(len(chars))] - } - return string(result) +var createWorkflowTests = []struct { + in *Workflow + shouldErr bool + testErr error +}{ + { + in: &Workflow{}, + shouldErr: true, + }, + { + in: &Workflow{ + Name: "exampleWorkflow", + }, + shouldErr: false, + }, + { + in: &Workflow{ + Name: "exampleWorkflow", + }, + shouldErr: true, + testErr: ErrExists, + }, + { + in: &Workflow{ + Name: "exampleWorkflowWithProfiles", + Profiles: []Profile{ + Profile{UUID: "c7616875-df2d-4fe5-9c1e-0cb36c1ede8a"}, + }, + }, + shouldErr: true, + }, } -func TestNewDB(t *testing.T) { - _ = datastore(t) - defer teardown() -} - -func TestDatastoreCreate(t *testing.T) { +func TestDatastoreCreateWorkflow(t *testing.T) { ds := datastore(t) defer teardown() @@ -50,40 +63,7 @@ func TestDatastoreCreate(t *testing.T) { } } - var createTests = []struct { - in *Workflow - shouldErr bool - testErr error - }{ - { - in: &Workflow{}, - shouldErr: true, - }, - { - in: &Workflow{ - Name: "exampleWorkflow", - }, - shouldErr: false, - }, - { - in: &Workflow{ - Name: "exampleWorkflow", - }, - shouldErr: true, - testErr: ErrExists, - }, - { - in: &Workflow{ - Name: "exampleWorkflowWithProfiles", - Profiles: []Profile{ - Profile{UUID: "c7616875-df2d-4fe5-9c1e-0cb36c1ede8a"}, - }, - }, - shouldErr: true, - }, - } - - for _, tt := range createTests { + for _, tt := range createWorkflowTests { _, err := ds.CreateWorkflow(tt.in) if !tt.shouldErr { checkErr(err) @@ -96,32 +76,3 @@ func TestDatastoreCreate(t *testing.T) { // check profiles } } - -func datastore(t *testing.T) Datastore { - logger := log.NewLogfmtLogger(os.Stderr) - ds, err := NewDB("postgres", testConn, logger) - if err != nil { - t.Fatal(err) - } - return ds -} - -var ( - testConn = "user=micromdm password=micromdm dbname=micromdm sslmode=disable" -) - -func teardown() { - db, err := sqlx.Open("postgres", testConn) - if err != nil { - panic(err) - } - - drop := ` - DROP TABLE IF EXISTS workflow_profile; - DROP TABLE IF EXISTS workflow_workflow; - DROP TABLE IF EXISTS profiles; - DROP TABLE IF EXISTS workflows; - ` - db.MustExec(drop) - defer db.Close() -}