mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-12 21:35:40 +08:00
Further comments on applications datastore.
Attempt to make some where clause helper functions. Expand test suites to cover many different type of installed applications. All tests passing.
This commit is contained in:
@@ -78,6 +78,9 @@ func (p Version) where() string {
|
||||
return fmt.Sprintf("version = '%s'", p.Version)
|
||||
}
|
||||
|
||||
// This function inserts a new application into the applications table.
|
||||
// Applications are uniquely identifier by both their name and their long form version because some do not have
|
||||
// identifiers, and some do not have short versions.
|
||||
func (store pgStore) New(a *Application) (string, error) {
|
||||
err := store.QueryRow(
|
||||
`INSERT INTO applications (
|
||||
@@ -113,6 +116,7 @@ func (store pgStore) New(a *Application) (string, error) {
|
||||
return a.UUID, nil
|
||||
}
|
||||
|
||||
// Retrieve a list of applications
|
||||
func (store pgStore) Applications(params ...interface{}) ([]Application, error) {
|
||||
stmt := `SELECT * FROM applications`
|
||||
stmt = addWhereFilters(stmt, "OR", params...)
|
||||
@@ -126,8 +130,9 @@ func (store pgStore) Applications(params ...interface{}) ([]Application, error)
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
// Retrieve only applications which are installed on the given device.
|
||||
func (store pgStore) GetApplicationsByDeviceUUID(deviceUUID string) (*[]Application, error) {
|
||||
apps := []Application{}
|
||||
var apps []Application
|
||||
query := `SELECT * FROM applications
|
||||
RIGHT JOIN devices_applications ON applications.application_uuid = devices_applications.application_uuid
|
||||
WHERE devices_applications.device_uuid=$1`
|
||||
@@ -171,3 +176,67 @@ func addWhereFilters(stmt string, separator string, params ...interface{}) strin
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
|
||||
// boolean operators are applied to where conditions which are part of a whereClauseGroup
|
||||
type booleanOperator string
|
||||
|
||||
const (
|
||||
OR = "OR"
|
||||
AND = "AND"
|
||||
)
|
||||
|
||||
type whereClauseGroup struct {
|
||||
Operator booleanOperator
|
||||
Clauses []whereClause
|
||||
}
|
||||
|
||||
// Get a string representing the where clause
|
||||
// Second return value is an array of arguments to give to db.Exec etc.
|
||||
func (cg whereClauseGroup) String() (string, []string) {
|
||||
var clauses []string
|
||||
var values []string = make([]string, len(cg.Clauses))
|
||||
|
||||
for i, c := range cg.Clauses {
|
||||
c.Placeholder = fmt.Sprintf("$%d", i)
|
||||
clauses = append(clauses, c.String())
|
||||
values = append(values, c.Value)
|
||||
}
|
||||
|
||||
return strings.Join(clauses, string(cg.Operator)), values
|
||||
}
|
||||
|
||||
// Struct representation of a where clause. Does not deal with field name escaping or any inference of the value.
|
||||
// I.E Do your own quoting.
|
||||
type whereClause struct {
|
||||
Operator string
|
||||
Field string
|
||||
Value string
|
||||
Placeholder string
|
||||
}
|
||||
|
||||
func (c whereClause) String() string {
|
||||
return fmt.Sprintf(`%s %s %s`, c.Field, c.Operator, c.Value)
|
||||
}
|
||||
|
||||
func Where(field string, operator string, value string) whereClause {
|
||||
return whereClause{
|
||||
Operator: operator,
|
||||
Field: field,
|
||||
Value: value,
|
||||
Placeholder: "$1",
|
||||
}
|
||||
}
|
||||
|
||||
func WhereAnd(clauses ...whereClause) whereClauseGroup {
|
||||
return whereClauseGroup{
|
||||
Operator: "AND",
|
||||
Clauses: clauses,
|
||||
}
|
||||
}
|
||||
|
||||
func WhereOr(clauses ...whereClause) whereClauseGroup {
|
||||
return whereClauseGroup{
|
||||
Operator: "OR",
|
||||
Clauses: clauses,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,40 @@ import (
|
||||
const MockUUID string = "ABCD-EFGH-IJKL"
|
||||
const MockName string = "Mock Application"
|
||||
|
||||
var appFixtures []Application = []Application{
|
||||
{ // Normal macOS Application
|
||||
UUID: "aba03d9d-6d80-4b96-bc6d-04233bddb26d",
|
||||
Name: "Keychain Access",
|
||||
Identifier: sql.NullString{"com.apple.keychainaccess", true},
|
||||
ShortVersion: sql.NullString{"9.0", true},
|
||||
Version: sql.NullString{"9.0", true},
|
||||
BundleSize: sql.NullInt64{14166172, true},
|
||||
},
|
||||
{ // macOS Application with no versioning available
|
||||
UUID: "ddacb35f-6a6a-42eb-8ce6-aad55da5a237",
|
||||
Name: "unetbootin",
|
||||
Identifier: sql.NullString{"com.yourcompany.unetbootin", true},
|
||||
BundleSize: sql.NullInt64{22292686, true},
|
||||
},
|
||||
{ // macOS Application with no bundle size available
|
||||
UUID: "cdd950a1-f596-4777-a63a-9839c28e4d48",
|
||||
Name: "FileMerge",
|
||||
Identifier: sql.NullString{"com.apple.FileMerge", true},
|
||||
ShortVersion: sql.NullString{"2.9.1", true},
|
||||
Version: sql.NullString{"2.9.1", true},
|
||||
},
|
||||
{ // macOS Application with no bundle identifier
|
||||
UUID: "84c78174-8331-4cf3-98c3-a4b1434617e5",
|
||||
Name: "Wireless Network Utility",
|
||||
BundleSize: sql.NullInt64{2416111, true},
|
||||
},
|
||||
}
|
||||
|
||||
var logger log.Logger = log.NewNopLogger()
|
||||
|
||||
//func TestNewDB(t *testing.T) {
|
||||
// var log log.Logger = log.NewNopLogger()
|
||||
// appsDB, err := NewDB("postgres", "host=localhost", log)
|
||||
// var logger logger.Logger = logger.NewNopLogger()
|
||||
// appsDB, err := NewDB("postgres", "host=localhost", logger)
|
||||
//
|
||||
// if err != nil {
|
||||
// t.Error(err)
|
||||
@@ -26,7 +57,6 @@ const MockName string = "Mock Application"
|
||||
//}
|
||||
|
||||
func TestNewDatastore(t *testing.T) {
|
||||
var log log.Logger = log.NewNopLogger()
|
||||
db, _, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
@@ -34,13 +64,12 @@ func TestNewDatastore(t *testing.T) {
|
||||
dbx := sqlx.NewDb(db, "mock")
|
||||
defer dbx.Close()
|
||||
|
||||
if _, err := NewDatastore(dbx, log); err != nil {
|
||||
if _, err := NewDatastore(dbx, logger); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewApplication(t *testing.T) {
|
||||
var log log.Logger = log.NewNopLogger()
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
@@ -48,38 +77,31 @@ func TestNewApplication(t *testing.T) {
|
||||
dbx := sqlx.NewDb(db, "mock")
|
||||
defer dbx.Close()
|
||||
|
||||
appsDs, err := NewDatastore(dbx, log)
|
||||
appsDs, err := NewDatastore(dbx, logger)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// macOS style: no DynamicSize, no IsValidated
|
||||
fixture := Application{
|
||||
Name: "Keychain Access",
|
||||
Identifier: sql.NullString{"com.apple.keychainaccess", true},
|
||||
ShortVersion: sql.NullString{"9.0", true},
|
||||
Version: sql.NullString{"9.0", true},
|
||||
BundleSize: sql.NullInt64{14166172, true},
|
||||
}
|
||||
for _, fixture := range appFixtures {
|
||||
newRow := sqlmock.NewRows([]string{"application_uuid"}).AddRow(MockUUID)
|
||||
mock.ExpectQuery("INSERT INTO applications").WithArgs(
|
||||
fixture.Name,
|
||||
fixture.Identifier,
|
||||
fixture.ShortVersion,
|
||||
fixture.Version,
|
||||
fixture.BundleSize,
|
||||
nil,
|
||||
nil,
|
||||
).WillReturnRows(newRow)
|
||||
|
||||
newRow := sqlmock.NewRows([]string{"application_uuid"}).AddRow(MockUUID)
|
||||
mock.ExpectQuery("INSERT INTO applications").WithArgs(
|
||||
fixture.Name,
|
||||
fixture.Identifier.String,
|
||||
fixture.ShortVersion.String,
|
||||
fixture.Version.String,
|
||||
fixture.BundleSize.Int64,
|
||||
nil,
|
||||
nil,
|
||||
).WillReturnRows(newRow)
|
||||
appUuid, err := appsDs.New(&fixture)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
appUuid, err := appsDs.New(&fixture)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if appUuid != MockUUID {
|
||||
t.Errorf("inserting a mock application did not return the mock uuid, got: %s", appUuid)
|
||||
if appUuid != MockUUID {
|
||||
t.Errorf("inserting a mock application did not return the mock uuid, got: %s", appUuid)
|
||||
}
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
@@ -88,7 +110,6 @@ func TestNewApplication(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestApplications(t *testing.T) {
|
||||
var log log.Logger = log.NewNopLogger()
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
@@ -96,7 +117,7 @@ func TestApplications(t *testing.T) {
|
||||
dbx := sqlx.NewDb(db, "mock")
|
||||
defer dbx.Close()
|
||||
|
||||
appsDs, err := NewDatastore(dbx, log)
|
||||
appsDs, err := NewDatastore(dbx, logger)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -115,7 +136,6 @@ func TestApplications(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestApplicationsWhereUUID(t *testing.T) {
|
||||
var log log.Logger = log.NewNopLogger()
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
@@ -123,13 +143,13 @@ func TestApplicationsWhereUUID(t *testing.T) {
|
||||
dbx := sqlx.NewDb(db, "mock")
|
||||
defer dbx.Close()
|
||||
|
||||
appsDs, err := NewDatastore(dbx, log)
|
||||
appsDs, err := NewDatastore(dbx, logger)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
mockRow := sqlmock.NewRows([]string{"application_uuid"}).AddRow(MockUUID)
|
||||
mock.ExpectQuery(`WHERE application_uuid =`).WithArgs(MockUUID).WillReturnRows(mockRow)
|
||||
mock.ExpectQuery(`WHERE application_uuid =`).WillReturnRows(mockRow)
|
||||
|
||||
apps, err := appsDs.Applications(UUID{MockUUID})
|
||||
if err != nil {
|
||||
@@ -150,7 +170,6 @@ func TestApplicationsWhereUUID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestApplicationsWhereName(t *testing.T) {
|
||||
var log log.Logger = log.NewNopLogger()
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
@@ -158,13 +177,13 @@ func TestApplicationsWhereName(t *testing.T) {
|
||||
dbx := sqlx.NewDb(db, "mock")
|
||||
defer dbx.Close()
|
||||
|
||||
appsDs, err := NewDatastore(dbx, log)
|
||||
appsDs, err := NewDatastore(dbx, logger)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
mockRow := sqlmock.NewRows([]string{"application_uuid", "name"}).AddRow(MockUUID, MockName)
|
||||
mock.ExpectQuery(`WHERE name =`).WithArgs(MockName).WillReturnRows(mockRow)
|
||||
mock.ExpectQuery(`WHERE name =`).WillReturnRows(mockRow)
|
||||
|
||||
apps, err := appsDs.Applications(Name{MockName})
|
||||
if err != nil {
|
||||
@@ -185,7 +204,6 @@ func TestApplicationsWhereName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetApplicationsByDeviceUUID(t *testing.T) {
|
||||
var log log.Logger = log.NewNopLogger()
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
@@ -193,7 +211,7 @@ func TestGetApplicationsByDeviceUUID(t *testing.T) {
|
||||
dbx := sqlx.NewDb(db, "mock")
|
||||
defer dbx.Close()
|
||||
|
||||
appsDs, err := NewDatastore(dbx, log)
|
||||
appsDs, err := NewDatastore(dbx, logger)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -211,7 +229,6 @@ func TestGetApplicationsByDeviceUUID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSaveApplicationByDeviceUUID(t *testing.T) {
|
||||
var log log.Logger = log.NewNopLogger()
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
@@ -219,20 +236,16 @@ func TestSaveApplicationByDeviceUUID(t *testing.T) {
|
||||
dbx := sqlx.NewDb(db, "mock")
|
||||
defer dbx.Close()
|
||||
|
||||
appsDs, err := NewDatastore(dbx, log)
|
||||
appsDs, err := NewDatastore(dbx, logger)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
fixture := Application{
|
||||
Name: "Keychain Access",
|
||||
Identifier: sql.NullString{"com.apple.keychainaccess", true},
|
||||
ShortVersion: sql.NullString{"9.0", true},
|
||||
Version: sql.NullString{"9.0", true},
|
||||
BundleSize: sql.NullInt64{14166172, true},
|
||||
}
|
||||
if err := appsDs.SaveApplicationByDeviceUUID(MockUUID, &fixture); err != nil {
|
||||
t.Error(err)
|
||||
for _, fixture := range appFixtures {
|
||||
mock.ExpectExec("INSERT INTO devices_applications").WithArgs(MockUUID, fixture.UUID).WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
if err := appsDs.SaveApplicationByDeviceUUID(MockUUID, &fixture); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user