add delete method for profiles

This commit is contained in:
Victor Vrantchan
2016-05-15 00:22:38 -04:00
parent 6e86a88ade
commit 3f535f8eb1
9 changed files with 389 additions and 106 deletions

View File

@@ -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
);

View File

@@ -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)
}

View File

@@ -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"`
}

View File

@@ -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)

View File

@@ -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)
}

61
workflow/testdata/sample_profile vendored Normal file
View File

@@ -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==

View File

@@ -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,

View File

@@ -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)

View File

@@ -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()
}