diff --git a/checkin/transport_test.go b/checkin/transport_test.go
new file mode 100644
index 00000000..0c6075a1
--- /dev/null
+++ b/checkin/transport_test.go
@@ -0,0 +1,198 @@
+package checkin
+
+import (
+ "bytes"
+ "database/sql"
+ "github.com/DavidHuie/gomigrate"
+ "github.com/go-kit/kit/log"
+ "github.com/micromdm/micromdm/command"
+ "github.com/micromdm/micromdm/device"
+ "github.com/micromdm/micromdm/management"
+ "golang.org/x/net/context"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+)
+
+var testConn string = "user=postgres password= dbname=travis_ci_test sslmode=disable"
+
+type fixtures struct {
+ db *sql.DB
+ server *httptest.Server
+ svc Service
+ devices device.Datastore
+ mgmt management.Service
+ cmd command.Service
+ profile []byte
+ logger log.Logger
+ ctx context.Context
+ testConn string
+ migrator *gomigrate.Migrator
+ handler http.Handler
+}
+
+// mockMgmtService mocks the management.Service interface which is a dependency of checkin.Service
+type mockMgmtService struct{ management.Service }
+
+func (s *mockMgmtService) Push(deviceUDID string) (string, error) {
+ return "", nil
+}
+
+func setup(t *testing.T) *fixtures {
+ var f *fixtures = new(fixtures)
+ f.ctx = context.Background()
+ l := log.NewLogfmtLogger(os.Stderr)
+ f.logger = log.NewContext(l).With("source", "testing")
+
+ db, err := sql.Open("postgres", testConn)
+ if err != nil {
+ t.Fatalf("opening database connection: %s", err)
+ }
+
+ f.migrator, _ = gomigrate.NewMigrator(db, gomigrate.Postgres{}, "../migrations")
+ if err = f.migrator.Migrate(); err != nil {
+ t.Fatalf("migrating tables: %s", err)
+ }
+
+ f.devices, err = device.NewDB("postgres", testConn, f.logger)
+ if err != nil {
+ t.Fatalf("constructing device datastore: %s", err)
+ }
+
+ f.mgmt = &mockMgmtService{}
+ f.profile = []byte{}
+ f.svc = NewService(f.devices, f.mgmt, f.cmd, f.profile)
+ f.handler = ServiceHandler(f.ctx, f.svc, f.logger)
+ f.server = httptest.NewServer(f.handler)
+
+ return f
+}
+
+func teardown(f *fixtures, t *testing.T) {
+ f.migrator.RollbackAll()
+
+ //f.db.Close()
+ f.server.Close()
+}
+
+func TestAuthenticate(t *testing.T) {
+ f := setup(t)
+ defer teardown(f, t)
+ requestBody, err := ioutil.ReadFile("../testdata/responses/macos/10.11.x/authenticate.plist")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client := http.DefaultClient
+ theURL := f.server.URL + "/mdm/checkin"
+ req, err := http.NewRequest("PUT", theURL, bytes.NewReader(requestBody))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ response, err := client.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if response.StatusCode != 200 {
+ var body []byte
+ response.Body.Read(body)
+ t.Logf("response body: %v", body)
+ t.Error(response.Status)
+ }
+
+ testDevices, err := f.devices.Devices()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(testDevices) != 1 {
+ t.Errorf("expected 1 device to be inserted, got: %d", len(testDevices))
+ }
+}
+
+func TestTokenUpdate(t *testing.T) {
+ f := setup(t)
+ defer teardown(f, t)
+ requestBody, err := ioutil.ReadFile("../testdata/responses/macos/10.11.x/token_update.plist")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client := http.DefaultClient
+ theURL := f.server.URL + "/mdm/checkin"
+ req, err := http.NewRequest("PUT", theURL, bytes.NewReader(requestBody))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ response, err := client.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if response.StatusCode != 200 {
+ var body []byte
+ response.Body.Read(body)
+ t.Logf("response body: %v", body)
+ t.Error(response.Status)
+ }
+
+ dev, err := f.devices.GetDeviceByUDID("00000000-1111-2222-3333-444455556666", "mdm_enrolled",
+ "apple_mdm_token", "apple_mdm_topic", "apple_push_magic")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if dev.Enrolled != true {
+ t.Error("expected device to be enrolled")
+ }
+
+ if dev.PushMagic != "00000000-1111-2222-3333-444455556666" {
+ t.Error("push magic was not updated")
+ }
+
+ if dev.MDMTopic != "com.apple.mgmt.test.00000000-1111-2222-3333-444455556666" {
+ t.Error("push topic was not updated")
+ }
+}
+
+func TestCheckout(t *testing.T) {
+ f := setup(t)
+ defer teardown(f, t)
+ requestBody, err := ioutil.ReadFile("../testdata/responses/macos/10.11.x/checkout.plist")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client := http.DefaultClient
+ theURL := f.server.URL + "/mdm/checkin"
+ req, err := http.NewRequest("PUT", theURL, bytes.NewReader(requestBody))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ response, err := client.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if response.StatusCode != 200 {
+ var body []byte
+ response.Body.Read(body)
+ t.Logf("response body: %v", body)
+ t.Error(response.Status)
+ }
+
+ dev, err := f.devices.GetDeviceByUDID("00000000-1111-2222-3333-444455556666", "mdm_enrolled")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if dev.Enrolled != false {
+ t.Error("expected device to be unenrolled")
+ }
+}
diff --git a/connect/service.go b/connect/service.go
index f9739221..695bc54b 100644
--- a/connect/service.go
+++ b/connect/service.go
@@ -187,11 +187,8 @@ func (svc service) ackInstalledApplicationList(req mdm.Response) error {
}
requestApps[i] = newApp
- //fmt.Printf("%v\n", newApp)
}
- //fmt.Printf("%v\n", requestApps)
-
return nil
}
diff --git a/connect/service_test.go b/connect/service_test.go
deleted file mode 100644
index 3f9c27ca..00000000
--- a/connect/service_test.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package connect
-
-import (
- "os"
- "testing"
-
- "github.com/go-kit/kit/log"
- "github.com/jmoiron/sqlx"
- "github.com/micromdm/mdm"
- "github.com/micromdm/micromdm/device"
- "golang.org/x/net/context"
-)
-
-type MockDevices struct{}
-
-func (md MockDevices) New(src string, d *device.Device) (string, error) {
- return "", nil
-}
-func (md MockDevices) GetDeviceByUDID(udid string, fields ...string) (*device.Device, error) {
- return &device.Device{
- UUID: "00000000-1111-2222-3333-444455556666",
- }, nil
-}
-func (md MockDevices) GetDeviceByUUID(uuid string, fields ...string) (*device.Device, error) {
- return &device.Device{}, nil
-}
-func (md MockDevices) Devices(params ...interface{}) ([]device.Device, error) {
- return []device.Device{
- {
- UUID: "00000000-1111-2222-3333-444455556666",
- },
- }, nil
-}
-func (md MockDevices) Save(msg string, dev *device.Device) error {
- return nil
-}
-
-type MockCmd struct{}
-
-func (mc MockCmd) NewCommand(*mdm.CommandRequest) (*mdm.Payload, error) {
- return &mdm.Payload{}, nil
-}
-func (mc MockCmd) NextCommand(udid string) ([]byte, int, error) {
- return []byte{}, 0, nil
-}
-func (mc MockCmd) DeleteCommand(deviceUDID, commandUUID string) (int, error) {
- return 0, nil
-}
-
-type serviceFixtures struct {
- dbx sqlx.DB
- appsDB applications.Datastore
- mock sqlmock.Sqlmock
-}
-
-func setup() (serviceFixtures, error) {
- db, mock, err := sqlmock.New()
- if err != nil {
- return nil, err
- }
- dbx := sqlx.NewDb(db, "mock")
- logger := log.NewLogfmtLogger(os.Stdout)
- appsDB, err := applications.NewDB("postgres", "host=localhost", logger)
- if err != nil {
- return nil, err
- }
-
- return serviceFixtures{dbx, appsDB, mock}, nil
-}
-
-func teardown(fixtures serviceFixtures) {
- fixtures.dbx.Close()
-}
-
-func TestAckQueryResponses(t *testing.T) {
- fixtures, err := setup()
- if err != nil {
- t.Fatalf("could not set up fixtures: %s", err)
- }
- defer teardown(fixtures.dbx)
- ctx := context.Background()
-
- response := mdm.Response{
- UDID: "00000000-1111-2222-3333-444455556666",
- Status: "Acknowledged",
- CommandUUID: "10000000-1111-2222-3333-444455556666",
- RequestType: "DeviceInformation",
- QueryResponses: mdm.QueryResponses{},
- }
-
- mockDevices := MockDevices{}
- mockCmd := MockCmd{}
-
- svc := NewService(mockDevices, fixtures.appsDB, mockCmd)
- svc.Acknowledge(ctx, response)
-
- if err := fixtures.mock.ExpectationsWereMet(); err != nil {
- t.Errorf("there were unfulfilled expectations: %s", err)
- }
-}
-
-func TestAckInstalledApplicationList(t *testing.T) {
- fixtures, err := setup()
- if err != nil {
- t.Fatalf("could not set up fixtures: %s", err)
- }
- defer teardown(fixtures.dbx)
- ctx := context.Background()
-
- response := mdm.Response{
- UDID: "00000000-1111-2222-3333-444455556666",
- Status: "Acknowledged",
- CommandUUID: "10000000-1111-2222-3333-444455556666",
- RequestType: "InstalledApplicationList",
- InstalledApplicationList: []mdm.InstalledApplicationListItem{
- {
- Name: "Wireless Network Utility",
- BundleSize: 2416111,
- },
- {
- Name: "Keychain Access",
- Identifier: "com.apple.keychainaccess",
- ShortVersion: "9.0",
- Version: "9.0",
- BundleSize: 14166172,
- },
- {
- Name: "Bundle Size Regression",
- BundleSize: 2463209237,
- },
- },
- }
-
- mockDevices := MockDevices{}
- mockCmd := MockCmd{}
- mock := fixtures.mock
-
- // Expect insert applications as new
- wifiAppUuidRow := sqlmock.NewRows([]string{"application_uuid"}).AddRow("90000000-1111-2222-3333-444455556666")
- mock.ExpectQuery("INSERT INTO applications").WithArgs("Wireless Network Utility", nil, nil, nil, 2416111, 0, nil).WillReturnRows(wifiAppUuidRow)
- kcAppUuidRow := sqlmock.NewRows([]string{"application_uuid"}).AddRow("A0000000-1111-2222-3333-444455556666")
- mock.ExpectQuery("INSERT INTO applications").WithArgs("Keychain Access", "com.apple.keychainaccess", "9.0", "9.0", 14166172, 0, nil).WillReturnRows(kcAppUuidRow)
- sizeAppUuidRow := sqlmock.NewRows([]string{"application_uuid"}).AddRow("B0000000-1111-2222-3333-444455556666")
- mock.ExpectQuery("INSERT INTO applications").WithArgs("Bundle Size Regression", nil, nil, nil, 2463209237, 0, nil).WillReturnRows(sizeAppUuidRow)
-
- // Expect query for device installed apps
- deviceAppsRow := sqlmock.NewRows([]string{"application_uuid", "name"}).AddRow("APP00000-1111-2222-3333-444455556666", "Mock Application")
- mock.ExpectQuery("RIGHT JOIN devices_applications").WithArgs("00000000-1111-2222-3333-444455556666").WillReturnRows(deviceAppsRow)
-
- mock.ExpectExec("INSERT INTO devices_applications").WithArgs("00000000-1111-2222-3333-444455556666", "90000000-1111-2222-3333-444455556666").WillReturnResult(sqlmock.NewResult(1, 1))
- mock.ExpectExec("INSERT INTO devices_applications").WithArgs("00000000-1111-2222-3333-444455556666", "A0000000-1111-2222-3333-444455556666").WillReturnResult(sqlmock.NewResult(1, 1))
- mock.ExpectExec("INSERT INTO devices_applications").WithArgs("00000000-1111-2222-3333-444455556666", "B0000000-1111-2222-3333-444455556666").WillReturnResult(sqlmock.NewResult(1, 1))
-
- svc := NewService(mockDevices, fixtures.appsDB, mockCmd)
- svc.Acknowledge(ctx, response)
-
- if err := mock.ExpectationsWereMet(); err != nil {
- t.Errorf("there were unfulfilled expectations: %s", err)
- }
-}
-
-// TODO: A regression exists where a device reports the installed application list twice and apps are duplicated.
-func TestAckInstalledApplicationListDuplicateRegression(t *testing.T) {
-
-}
diff --git a/connect/transport_test.go b/connect/transport_test.go
new file mode 100644
index 00000000..6ddb532f
--- /dev/null
+++ b/connect/transport_test.go
@@ -0,0 +1,259 @@
+package connect
+
+import (
+ "bytes"
+ "database/sql"
+ "github.com/DavidHuie/gomigrate"
+ "github.com/go-kit/kit/log"
+ "github.com/micromdm/mdm"
+ "github.com/micromdm/micromdm/application"
+ "github.com/micromdm/micromdm/certificate"
+ "github.com/micromdm/micromdm/command"
+ "github.com/micromdm/micromdm/device"
+ "golang.org/x/net/context"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+)
+
+// mockCommandService implements the command.Service interface and returns only the single command struct given to it
+// instead of querying a live redis instance.
+type mockCommandService struct {
+ t *testing.T
+ MockCommand *mdm.CommandRequest
+}
+
+func (svc mockCommandService) NewCommand(cmd *mdm.CommandRequest) (*mdm.Payload, error) {
+ svc.MockCommand = cmd
+ return nil, nil
+}
+
+func (svc mockCommandService) NextCommand(udid string) ([]byte, int, error) {
+ return nil, 0, nil
+}
+
+func (svc mockCommandService) DeleteCommand(deviceUDID, commandUUID string) (int, error) {
+ svc.t.Logf("Deleting mock command with UUID %s", commandUUID)
+ return 1, nil
+}
+
+func (svc mockCommandService) Commands(deviceUDID string) ([]mdm.Payload, error) {
+ return []mdm.Payload{}, nil
+}
+
+func (svc mockCommandService) Find(commandUUID string) (*mdm.Payload, error) {
+ svc.t.Logf("Returning mock response finding command with UUID %s", commandUUID)
+
+ payload, _ := mdm.NewPayload(svc.MockCommand)
+ payload.CommandUUID = commandUUID
+
+ return payload, nil
+}
+
+type connectFixtures struct {
+ db *sql.DB
+ server *httptest.Server
+ svc Service
+ devices device.Datastore
+ apps application.Datastore
+ certs certificate.Datastore
+ cs command.Service
+ logger log.Logger
+ deviceUUID string
+ migrator *gomigrate.Migrator
+}
+
+func setup(t *testing.T, cmd *mdm.CommandRequest) *connectFixtures {
+ ctx := context.Background()
+ l := log.NewLogfmtLogger(os.Stderr)
+ logger := log.NewContext(l).With("source", "testing")
+
+ var (
+ err error
+ testConn string = "user=postgres password= dbname=travis_ci_test sslmode=disable"
+ devices device.Datastore
+ apps application.Datastore
+ certs certificate.Datastore
+ cs command.Service
+ )
+
+ db, err := sql.Open("postgres", testConn)
+ if err != nil {
+ t.Fatal(err)
+ }
+ migrator, _ := gomigrate.NewMigrator(db, gomigrate.Postgres{}, "../migrations")
+ if err = migrator.Migrate(); err != nil {
+ t.Fatalf("migrating tables: %s", err)
+ }
+
+ devices, err = device.NewDB("postgres", testConn, logger)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ apps, err = application.NewDB("postgres", testConn, logger)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ certs, err = certificate.NewDB("postgres", testConn, logger)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cs = mockCommandService{t, cmd}
+
+ d := &device.Device{
+ UDID: device.JsonNullString{sql.NullString{"00000000-1111-2222-3333-444455556666", true}},
+ MDMTopic: "mdmtopic",
+ OSVersion: "10.11",
+ BuildVersion: "10G1000",
+ ProductName: "Mock Product",
+ SerialNumber: device.JsonNullString{sql.NullString{"11111111", true}},
+ Model: "MockModel",
+ }
+
+ deviceUUID, err := devices.New("authenticate", d)
+ if err != nil {
+ t.Fatalf("creating fixture device: %s", err)
+ }
+ t.Logf("created mock device with UUID %v", deviceUUID)
+
+ svc := NewService(devices, apps, certs, cs)
+ handler := ServiceHandler(ctx, svc, logger)
+ server := httptest.NewServer(handler)
+
+ return &connectFixtures{
+ db: db,
+ server: server,
+ svc: svc,
+ devices: devices,
+ apps: apps,
+ certs: certs,
+ cs: cs,
+ logger: logger,
+ deviceUUID: deviceUUID,
+ migrator: migrator,
+ }
+}
+
+func teardown(fixtures *connectFixtures) {
+ defer fixtures.server.Close()
+ defer fixtures.db.Close()
+
+ fixtures.migrator.RollbackAll()
+}
+
+func TestAcknowledgeDeviceInformation(t *testing.T) {
+ cmd := mdm.CommandRequest{
+ UDID: "00000000-1111-2222-3333-444455556666",
+ RequestType: "DeviceInformation",
+ }
+
+ fixtures := setup(t, &cmd)
+ defer teardown(fixtures)
+
+ requestBody, err := ioutil.ReadFile("../testdata/responses/macos/10.11.x/device_information.plist")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client := http.DefaultClient
+ theURL := fixtures.server.URL + "/mdm/connect"
+ req, err := http.NewRequest("PUT", theURL, bytes.NewReader(requestBody))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ response, err := client.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if response.StatusCode != 200 {
+ var body []byte
+ response.Body.Read(body)
+ t.Logf("response body: %v", body)
+ t.Error(response.Status)
+ }
+}
+
+func TestAcknowledgeInstalledApplicationList(t *testing.T) {
+ cmd := mdm.CommandRequest{
+ UDID: "00000000-1111-2222-3333-444455556666",
+ RequestType: "InstalledApplicationList",
+ }
+
+ fixtures := setup(t, &cmd)
+ defer teardown(fixtures)
+
+ requestBody, err := ioutil.ReadFile("../testdata/responses/macos/10.11.x/installed_application_list.plist")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client := http.DefaultClient
+ theURL := fixtures.server.URL + "/mdm/connect"
+ req, err := http.NewRequest("PUT", theURL, bytes.NewReader(requestBody))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ response, err := client.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if response.StatusCode != 200 {
+ var body []byte
+ response.Body.Read(body)
+ t.Logf("response body: %v", body)
+ t.Error(response.Status)
+ }
+
+ var count int
+ err = fixtures.db.QueryRow("SELECT COUNT(*) FROM devices_applications;").Scan(&count)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if count != 3 {
+ t.Error("expected number of inserted applications to be 3")
+ }
+}
+
+func TestAcknowledgeCertificateList(t *testing.T) {
+ cmd := mdm.CommandRequest{
+ UDID: "00000000-1111-2222-3333-444455556666",
+ RequestType: "CertificateList",
+ }
+
+ fixtures := setup(t, &cmd)
+ defer teardown(fixtures)
+
+ requestBody, err := ioutil.ReadFile("../testdata/responses/macos/10.11.x/certificate_list.plist")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ client := http.DefaultClient
+ theURL := fixtures.server.URL + "/mdm/connect"
+ req, err := http.NewRequest("PUT", theURL, bytes.NewReader(requestBody))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ response, err := client.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if response.StatusCode != 200 {
+ var body []byte
+ response.Body.Read(body)
+ t.Logf("response body: %v", body)
+ t.Error(response.Status)
+ }
+}
diff --git a/migrations/201606170001_devices_down.sql b/migrations/201606170001_devices_down.sql
index 834523f9..94aace54 100644
--- a/migrations/201606170001_devices_down.sql
+++ b/migrations/201606170001_devices_down.sql
@@ -1,2 +1,3 @@
-DROP INDEX serial_idx;
+DROP INDEX IF EXISTS serial_idx;
DROP TABLE devices;
+DROP EXTENSION "uuid-ossp";
diff --git a/migrations/201606210002_devices_udid_idx_up.sql b/migrations/201606210002_devices_udid_idx_up.sql
index a2ce7844..a73f4cba 100644
--- a/migrations/201606210002_devices_udid_idx_up.sql
+++ b/migrations/201606210002_devices_udid_idx_up.sql
@@ -1,4 +1,4 @@
-DROP INDEX serial_idx;
+DROP INDEX IF EXISTS serial_idx;
-- Need a composite constraint because DEP uses serial and OTA enrollment uses UDID
CREATE UNIQUE INDEX IF NOT EXISTS udid_serial_idx ON devices (udid, serial_number);
diff --git a/migrations/201608150001_devices_applications_down.sql b/migrations/201608150001_devices_applications_down.sql
index df610dcc..aebb78c0 100644
--- a/migrations/201608150001_devices_applications_down.sql
+++ b/migrations/201608150001_devices_applications_down.sql
@@ -1,4 +1,11 @@
--- ALTER TABLE devices_applications DROP CONSTRAINT IF EXISTS devices_applications_application_uuid_fkey;
---
--- ALTER TABLE devices_applications ADD CONSTRAINT devices_applications.application_uuid PRIMARY KEY;
--- ALTER TABLE devices_applications ALTER application_uuid SET DEFAULT uuid_generate_v4();
+ALTER TABLE devices_applications
+ DROP COLUMN name,
+ DROP COLUMN identifier,
+ DROP COLUMN short_version,
+ DROP COLUMN version,
+ DROP COLUMN bundle_size,
+ DROP COLUMN dynamic_size,
+ DROP COLUMN is_validated;
+
+ALTER TABLE devices_applications ALTER application_uuid DROP DEFAULT;
+-- ALTER TABLE devices_applications ADD PRIMARY KEY (application_uuid); // drop?
\ No newline at end of file
diff --git a/migrations/201608150001_devices_applications_up.sql b/migrations/201608150001_devices_applications_up.sql
index 11d50d74..69b9b333 100644
--- a/migrations/201608150001_devices_applications_up.sql
+++ b/migrations/201608150001_devices_applications_up.sql
@@ -1,7 +1,4 @@
ALTER TABLE devices_applications DROP CONSTRAINT IF EXISTS devices_applications_application_uuid_fkey;
-
--- DELETE FROM devices_applications;
-
ALTER TABLE devices_applications ADD PRIMARY KEY (application_uuid);
ALTER TABLE devices_applications ALTER application_uuid SET DEFAULT uuid_generate_v4();
@@ -13,4 +10,3 @@ ALTER TABLE devices_applications
ADD COLUMN bundle_size BIGINT,
ADD COLUMN dynamic_size BIGINT,
ADD COLUMN is_validated BOOLEAN;
-
diff --git a/testdata/responses/ios/9.x/authenticate.plist b/testdata/responses/ios/9.x/authenticate.plist
new file mode 100644
index 00000000..917f4972
--- /dev/null
+++ b/testdata/responses/ios/9.x/authenticate.plist
@@ -0,0 +1,20 @@
+
+
+
+
+ BuildVersion
+ 13F69
+ MessageType
+ Authenticate
+ OSVersion
+ 9.3.2
+ ProductName
+ iPad4,1
+ SerialNumber
+ XXXXXXXXXXXX
+ Topic
+ io.micromdm.topic.00000000-1111-2222-3333-444455556666
+ UDID
+ 1111111111111111111111111111111111111111
+
+
diff --git a/testdata/responses/ios/9.x/security_info.plist b/testdata/responses/ios/9.x/security_info.plist
new file mode 100644
index 00000000..c9a9022c
--- /dev/null
+++ b/testdata/responses/ios/9.x/security_info.plist
@@ -0,0 +1,27 @@
+
+
+
+
+ CommandUUID
+ 00000000-1111-2222-3333-444455556666
+ SecurityInfo
+
+ HardwareEncryptionCaps
+ 3
+ PasscodeCompliant
+
+ PasscodeCompliantWithProfiles
+
+ PasscodeLockGracePeriod
+ 0
+ PasscodeLockGracePeriodEnforced
+ 0
+ PasscodePresent
+
+
+ Status
+ Acknowledged
+ UDID
+ 1111111111111111111111111111111111111111
+
+
\ No newline at end of file
diff --git a/testdata/responses/macos/10.11.x/authenticate.plist b/testdata/responses/macos/10.11.x/authenticate.plist
new file mode 100644
index 00000000..c6bcf100
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/authenticate.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ BuildVersion
+ 15G1004
+ Challenge
+
+ YXBwbGU=
+
+ DeviceName
+ micromdm-test
+ MessageType
+ Authenticate
+ Model
+ iMac15,1
+ ModelName
+ iMac
+ OSVersion
+ 10.11.6
+ ProductName
+ iMac15,1
+ SerialNumber
+ C00000000004
+ Topic
+ com.apple.mgmt.test.00000000-1111-2222-3333-444455556666
+ UDID
+ 00000000-1111-2222-3333-444455556666
+
+
\ No newline at end of file
diff --git a/testdata/responses/macos/10.11.x/certificate_list.plist b/testdata/responses/macos/10.11.x/certificate_list.plist
new file mode 100644
index 00000000..03cbe191
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/certificate_list.plist
@@ -0,0 +1,77 @@
+
+
+
+
+ CertificateList
+
+
+ CommonName
+ com.apple.systemdefault
+ Data
+
+ MIICFDCCAX2gAwIBAgIEMshmtjALBgkqhkiG9w0BAQUwPDEgMB4G
+ A1UEAwwXY29tLmFwcGxlLnN5c3RlbWRlZmF1bHQxGDAWBgNVBAoM
+ D1N5c3RlbSBJZGVudGl0eTAeFw0xNTAzMDgyMjM4NDBaFw0zNTAz
+ MDMyMjM4NDBaMDwxIDAeBgNVBAMMF2NvbS5hcHBsZS5zeXN0ZW1k
+ ZWZhdWx0MRgwFgYDVQQKDA9TeXN0ZW0gSWRlbnRpdHkwgZ8wDQYJ
+ KoZIhvcNAQEBBQADgY0AMIGJAoGBALWpKmld573u/zaPBwCuAMSy
+ SxwUqQsTCi8TxPIbDLSssMkDJMlcukB9zpDkVZnP49uZow7TGE6t
+ VuXKDmcx6gfskwkdyra05X2xNZACopdbV2OSjv87hh2yMRcmq+tt
+ ao/3L3Ynp2ZWTVFIfgcJTHzkIKFLRwWfmEV0DE1WYNMpAgMBAAGj
+ JTAjMAsGA1UdDwQEAwIEsDAUBgNVHSUEDTALBgkqhkiG92NkBAQw
+ DQYJKoZIhvcNAQEFBQADgYEAjN23bV17Xk9+om0NVMhcb1dou3E0
+ bVfMuvpYx6xcClP8Im9gGaIt8sHQPx2sRfZ0EHIPAWIDdtg8Qun3
+ cIOLal4LigmgZEgU2V+JLyFRI7ps9QVDfbM0/So0j/B5VvUH+K8h
+ ZTM0Y7Dg0GVwQg2tP2Fg7xFjnKz/6AO0n03Im44=
+
+ IsIdentity
+
+
+
+ CommonName
+ Apple Worldwide Developer Relations Certification Authority
+ Data
+
+ MIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAw
+ YjELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAk
+ BgNVBAsTHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYw
+ FAYDVQQDEw1BcHBsZSBSb290IENBMB4XDTEzMDIwNzIxNDg0N1oX
+ DTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVTMRMwEQYDVQQK
+ DApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUg
+ RGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29y
+ bGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlv
+ biBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+ AoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0U3rOfGOAYXdkXqUH
+ I7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkVCBms
+ qtsqMu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81R
+ ZJ/GXNG8V25nNYB2NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVg
+ nJ0zTlX5ElgMhrgWDcHld0WNUEi6Ky3klIXh6MSdxmilsKP8Z35w
+ ugJZS3dCkTm59c3hTO/AO0iMpuUhXf1qarunFjVg0uat80YpyejD
+ i+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGjgaYwgaMw
+ HQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB
+ /wQFMAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/
+ CF4wLgYDVR0fBCcwJTAjoCGgH4YdaHR0cDovL2NybC5hcHBsZS5j
+ b20vcm9vdC5jcmwwDgYDVR0PAQH/BAQDAgGGMBAGCiqGSIb3Y2QG
+ AgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Zviz1smwvj+4T
+ hzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/N
+ w0Uwj6ODDc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4Q
+ ttkdUGwhgAqJTleMa1s8Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1
+ eliHe2iK5IaMSuviSRSqpd1VAKmuu0swruGgsbwpgOYJd+W+NKIB
+ yn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur+cmV6U/kTecmmYHp
+ vPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxRpVzs
+ cYqCtGwPDBUf
+
+ IsIdentity
+
+
+
+ CommandUUID
+ 00000000-1111-2222-3333-444455556666
+ RequestType
+ CertificateList
+ Status
+ Acknowledged
+ UDID
+ 00000000-1111-2222-3333-444455556666
+
+
diff --git a/testdata/responses/macos/10.11.x/checkout.plist b/testdata/responses/macos/10.11.x/checkout.plist
new file mode 100644
index 00000000..64855f59
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/checkout.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ MessageType
+ CheckOut
+ Topic
+ com.apple.mgmt.test.00000000-1111-2222-3333-444455556666
+ UDID
+ 00000000-1111-2222-3333-444455556666
+
+
\ No newline at end of file
diff --git a/testdata/responses/macos/10.11.x/device_information.plist b/testdata/responses/macos/10.11.x/device_information.plist
new file mode 100644
index 00000000..d593d5d8
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/device_information.plist
@@ -0,0 +1,84 @@
+
+
+
+
+ CommandUUID
+ 00000000-1111-2222-3333-444455556666
+ QueryResponses
+
+ ActiveManagedUsers
+
+ 00000000-1111-2222-3333-444455556666
+
+ AvailableDeviceCapacity
+ 60.977592468261719
+ AwaitingConfiguration
+
+ BluetoothMAC
+ 00-00-00-00-00-00
+ BuildVersion
+ 15G1004
+ CurrentConsoleManagedUser
+ 00000000-1111-2222-3333-444455556666
+ DeviceCapacity
+ 464.82241058349609
+ DeviceName
+ micromdm-testing
+ HostName
+ micromdm-testing.dev
+ Languages
+
+ en
+
+ LocalHostName
+ micromdm-testing
+ Model
+ iMac15,1
+ ModelName
+ iMac
+ OSUpdateSettings
+
+ AutoCheckEnabled
+
+ AutomaticAppInstallationEnabled
+
+ AutomaticOSInstallationEnabled
+
+ AutomaticSecurityUpdatesEnabled
+
+ BackgroundDownloadEnabled
+
+ CatalogURL
+ https://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz
+ IsDefaultCatalog
+
+ PerformPeriodicCheck
+
+ PreviousScanDate
+ 2016-11-03T03:12:26Z
+ PreviousScanResult
+ 0
+
+ OSVersion
+ 10.11.6
+ ProductName
+ iMac15,1
+ SerialNumber
+ C00000000004
+ UDID
+ 00000000-1111-2222-3333-444455556666
+ WiFiMAC
+ 00:00:00:00:00:00
+ iTunesStoreAccountHash
+ aAaAaAaAaAaAaAaAaAaAaAaAaAa=
+ iTunesStoreAccountIsActive
+
+
+ RequestType
+ DeviceInformation
+ Status
+ Acknowledged
+ UDID
+ 00000000-1111-2222-3333-444455556666
+
+
diff --git a/testdata/responses/macos/10.11.x/error_invalid_request_type.plist b/testdata/responses/macos/10.11.x/error_invalid_request_type.plist
new file mode 100644
index 00000000..0f7eb031
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/error_invalid_request_type.plist
@@ -0,0 +1,25 @@
+
+
+
+
+ CommandUUID
+ 00000000-1111-2222-3333-444455556666
+ ErrorChain
+
+
+ ErrorCode
+ 12021
+ ErrorDomain
+ MCMDMErrorDomain
+ LocalizedDescription
+ “OSUpdateStatus” is not a valid request type.
+ USEnglishDescription
+ “OSUpdateStatus” is not a valid request type.
+
+
+ Status
+ Error
+ UDID
+ 00000000-1111-2222-3333-444455556666
+
+
\ No newline at end of file
diff --git a/testdata/responses/macos/10.11.x/installed_application_list.plist b/testdata/responses/macos/10.11.x/installed_application_list.plist
new file mode 100644
index 00000000..d74cd5cd
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/installed_application_list.plist
@@ -0,0 +1,41 @@
+
+
+
+
+ CommandUUID
+ 00000000-1111-2222-3333-444455556666
+ InstalledApplicationList
+
+
+ BundleSize
+ 5855484
+ Identifier
+ com.apple.systempreferences
+ Name
+ System Preferences
+ ShortVersion
+ 14.0
+ Version
+ 14.0
+
+
+ BundleSize
+ 65092
+ Name
+ Set Info
+
+
+ BundleSize
+ 0
+ Name
+ Install OS X Yosemite
+
+
+ RequestType
+ InstalledApplicationList
+ Status
+ Acknowledged
+ UDID
+ 00000000-1111-2222-3333-444455556666
+
+
\ No newline at end of file
diff --git a/testdata/responses/macos/10.11.x/profile_list.plist b/testdata/responses/macos/10.11.x/profile_list.plist
new file mode 100644
index 00000000..337d15e2
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/profile_list.plist
@@ -0,0 +1,106 @@
+
+
+
+
+ CommandUUID
+ 00000000-1111-2222-3333-444455556666
+ ProfileList
+
+
+ HasRemovalPasscode
+
+ IsEncrypted
+
+ PayloadContent
+
+
+ PayloadDescription
+ Installs the TLS certificate for MicroMDM
+ PayloadDisplayName
+ Self-signed TLS certificate for MicroMDM
+ PayloadIdentifier
+ com.github.micromdm.tls
+ PayloadOrganization
+
+ PayloadType
+ com.apple.security.pkcs1
+ PayloadUUID
+ 4b12d46f-0cfb-4d58-ab5c-74873235b60b
+ PayloadVersion
+ 1
+
+
+ PayloadDescription
+ Installs the root CA certificate for MicroMDM
+ PayloadDisplayName
+ Root certificate for MicroMDM
+ PayloadIdentifier
+ com.github.micromdm.ssl.ca
+ PayloadOrganization
+
+ PayloadType
+ com.apple.security.root
+ PayloadUUID
+ de6a8869-e0c3-4fa3-ba3e-f89001f8ee71
+ PayloadVersion
+ 1
+
+
+ PayloadDescription
+ Enrolls with the MDM server
+ PayloadDisplayName
+
+ PayloadIdentifier
+ com.github.micromdm.mdm
+ PayloadOrganization
+ MicroMDM
+ PayloadType
+ com.apple.mdm
+ PayloadUUID
+ e021da61-092a-4b73-8c26-00f5fdcf7e4e
+ PayloadVersion
+ 1
+
+
+ PayloadDescription
+ Configures SCEP
+ PayloadDisplayName
+ SCEP
+ PayloadIdentifier
+ com.github.micromdm.scep
+ PayloadOrganization
+ MicroMDM
+ PayloadType
+ com.apple.security.scep
+ PayloadUUID
+ 519e158c-c699-42fd-8cdf-1cd5612088df
+ PayloadVersion
+ 1
+
+
+ PayloadDescription
+ The server may alter your settings
+ PayloadDisplayName
+ Enrollment Profile
+ PayloadIdentifier
+ com.github.micromdm.micromdm.mdm
+ PayloadOrganization
+ MicroMDM
+ PayloadRemovalDisallowed
+
+ PayloadUUID
+ dd3c707b-b18c-4979-bd94-2fe5cd804a47
+ PayloadVersion
+ 1
+ SignerCertificates
+
+
+
+ RequestType
+ ProfileList
+ Status
+ Acknowledged
+ UDID
+ 00000000-1111-2222-3333-444455556666
+
+
\ No newline at end of file
diff --git a/testdata/responses/macos/10.11.x/security_info.plist b/testdata/responses/macos/10.11.x/security_info.plist
new file mode 100644
index 00000000..32caf6f5
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/security_info.plist
@@ -0,0 +1,19 @@
+
+
+
+
+ CommandUUID
+ 00000000-1111-2222-3333-444455556666
+ RequestType
+ SecurityInfo
+ SecurityInfo
+
+ FDE_Enabled
+
+
+ Status
+ Acknowledged
+ UDID
+ 00000000-1111-2222-3333-444455556666
+
+
\ No newline at end of file
diff --git a/testdata/responses/macos/10.11.x/token_update.plist b/testdata/responses/macos/10.11.x/token_update.plist
new file mode 100644
index 00000000..83e6e9f4
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/token_update.plist
@@ -0,0 +1,20 @@
+
+
+
+
+ AwaitingConfiguration
+
+ MessageType
+ TokenUpdate
+ PushMagic
+ 00000000-1111-2222-3333-444455556666
+ Token
+
+ YXBwbGU=
+
+ Topic
+ com.apple.mgmt.test.00000000-1111-2222-3333-444455556666
+ UDID
+ 00000000-1111-2222-3333-444455556666
+
+
\ No newline at end of file
diff --git a/testdata/responses/macos/10.11.x/token_update_user.plist b/testdata/responses/macos/10.11.x/token_update_user.plist
new file mode 100644
index 00000000..67fa73e3
--- /dev/null
+++ b/testdata/responses/macos/10.11.x/token_update_user.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ MessageType
+ TokenUpdate
+ NotOnConsole
+
+ PushMagic
+ 00000000-1111-2222-3333-444455556666
+ Token
+
+ AAAA=
+
+ Topic
+ com.apple.mgmt.test.00000000-1111-2222-3333-444455556666
+ UDID
+ 00000000-1111-2222-3333-444455556666
+ UserID
+ 00000000-1111-2222-3333-444455556666
+ UserLongName
+ Administrator
+ UserShortName
+ admin
+
+
\ No newline at end of file