Repository: PagerDuty/go-pagerduty
Pull request #408 (2021-12-22 00:48:01)

feat: Add support for fetching Audit Records
See issue [394](https://github.com/PagerDuty/go-pagerduty/issues/394)

t-junjie: I pulled in changes from `master` and made the relevant changes as requested above. 
It looks like I overwrote some of the commits when I squashed them when I did a `git rebase`, causing the previous review to be "outdated". Sorry about that
theckman: The tests are failing:

```
=== RUN   TestAudit_List
    audit_test.go:94: values not equal (-want / +got):
          interface{}(
        - 	&pagerduty.ListAuditRecordsResponse{
        - 		Records: []pagerduty.AuditRecord{
        - 			{
        - 				ID:               "PDRECORDID4_UPDATED_USERS_NOTIFI"...,
        - 				ExecutionTime:    "2020-06-04T15:30:16.272Z",
        - 				ExecutionContext: pagerduty.ExecutionContext{...},
        - 				Actors:           []pagerduty.APIObject{...},
        - 				...
        - 			},
        - 		},
        - 		Limit: 10,
        - 	},
        + 	pagerduty.ListAuditRecordsResponse{
        + 		Records: []pagerduty.AuditRecord{
        + 			{
        + 				ID:               "PDRECORDID4_UPDATED_USERS_NOTIFI"...,
        + 				ExecutionTime:    "2020-06-04T15:30:16.272Z",
        + 				ExecutionContext: pagerduty.ExecutionContext{...},
        + 				Actors:           []pagerduty.APIObject{...},
        + 				...
        + 			},
        + 		},
        + 		Limit: 10,
        + 	},
          )
--- FAIL: TestAudit_List (0.00s)
````

Patch:
diff --git a/audit.go b/audit.go
new file mode 100644
index 0000000..5d96ec2
--- /dev/null
+++ b/audit.go
@@ -0,0 +1,167 @@
+package pagerduty
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	"github.com/google/go-querystring/query"
+)
+
+const auditBaseURL = "/audit/records"
+
+// ListAuditRecordsOptions is the data structure used when calling the
+// ListAuditRecords API endpoint.
+type ListAuditRecordsOptions struct {
+	Actions              []string `url:"actions,omitempty,brackets"`
+	ActorID              string   `url:"actor_id,omitempty"`
+	ActorType            string   `url:"actor_type,omitempty"`
+	Cursor               string   `url:"cursor,omitempty"`
+	Limit                uint     `url:"limit,omitempty"`
+	MethodTruncatedToken string   `url:"method_truncated_token,omitempty"`
+	MethodType           string   `url:"method_type,omitempty"`
+	RootResourcesTypes   []string `url:"root_resources_types,omitempty,brackets"`
+	Since                string   `url:"since,omitempty"`
+	Until                string   `url:"until,omitempty"`
+}
+
+// ListAuditRecordsResponse is the response data received when calling the
+// ListAuditRecords API endpoint.
+type ListAuditRecordsResponse struct {
+	Records []AuditRecord `json:"records,omitempty"`
+	// ResponseMetaData is not a required field in the pagerduty API response,
+	// using a pointer allows us to not marshall an empty ResponseMetaData struct
+	// into a JSON.
+	ResponseMetaData *ResponseMetaData `json:"response_metadata,omitempty"`
+	Limit            uint              `json:"limit,omitempty"`
+	// NextCursor is an  opaque string that will deliver the next set of results
+	// when provided as the cursor parameter in a subsequent request.
+	// A null value for this field indicates that there are no additional results.
+	// We use a pointer here to marshall the string value into null
+	// when NextCursor is an empty string.
+	NextCursor *string `json:"next_cursor"`
+}
+
+// AuditRecord is a audit trail record that matches the query criteria.
+type AuditRecord struct {
+	ID               string           `json:"id,omitempty"`
+	Self             string           `json:"self,omitempty"`
+	ExecutionTime    string           `json:"execution_time,omitempty"`
+	ExecutionContext ExecutionContext `json:"execution_context,omitempty"`
+	Actors           []APIObject      `json:"actors,omitempty"`
+	Method           Method           `json:"method,omitempty"`
+	RootResource     APIObject        `json:"root_resource,omitempty"`
+	Action           string           `json:"action,omitempty"`
+	Details          Details          `json:"details,omitempty"`
+}
+
+// ResponseMetadata contains information about the response.
+type ResponseMetaData struct {
+	Messages []string `json:"messages,omitempty"`
+}
+
+// ExecutionContext contains information about the action execution context.
+type ExecutionContext struct {
+	RequestID     string `json:"request_id,omitempty"`
+	RemoteAddress string `json:"remote_address,omitempty"`
+}
+
+// Method contains information on the method used to perform the action.
+type Method struct {
+	Description    string `json:"description,omitempty"`
+	TruncatedToken string `json:"truncated_token,omitempty"`
+	Type           string `json:"type,omitempty"`
+}
+
+// Details contain additional information about the action or the resource
+// that has been audited.
+type Details struct {
+	Resource APIObject `json:"resource,omitempty"`
+	// A set of fields that have been affected.
+	// The fields that have not been affected MAY be returned.
+	Fields []Field `json:"fields,omitempty"`
+	// A set of references that have been affected.
+	References []Reference `json:"references,omitempty"`
+}
+
+// Field contains information about the resource field that have been affected.
+type Field struct {
+	Name        string `json:"name,omitempty"`
+	Description string `json:"description,omitempty"`
+	Value       string `json:"value,omitempty"`
+	BeforeValue string `json:"before_value,omitempty"`
+}
+
+// Reference contains information about the reference that have been affected.
+type Reference struct {
+	Name        string      `json:"name,omitempty"`
+	Description string      `json:"description,omitempty"`
+	Added       []APIObject `json:"added,omitempty"`
+	Removed     []APIObject `json:"removed,omitempty"`
+}
+
+// ListAuditRecords lists audit trial records matching provided query params
+// or default criteria.
+func (c *Client) ListAuditRecords(ctx context.Context, o ListAuditRecordsOptions) (ListAuditRecordsResponse, error) {
+	v, err := query.Values(o)
+	if err != nil {
+		return ListAuditRecordsResponse{}, err
+	}
+
+	u := fmt.Sprintf("%s?%s", auditBaseURL, v.Encode())
+	resp, err := c.get(ctx, u)
+	if err != nil {
+		return ListAuditRecordsResponse{}, err
+	}
+
+	var result ListAuditRecordsResponse
+	if err = c.decodeJSON(resp, &result); err != nil {
+		return ListAuditRecordsResponse{}, err
+	}
+
+	return result, nil
+
+}
+
+// ListAuditRecordsPaginated lists audit trial records matching provided query
+// params or default criteria, processing paginated responses. The include
+// function decides whether or not to include a specific AuditRecord in
+// the final result. If the include function is nil, all audit records from
+// the API are included by default.
+func (c *Client) ListAuditRecordsPaginated(ctx context.Context, o ListAuditRecordsOptions, include func(AuditRecord) bool) ([]AuditRecord, error) {
+	v, err := query.Values(o)
+	if err != nil {
+		return nil, err
+	}
+
+	if include == nil {
+		include = func(AuditRecord) bool { return true }
+	}
+
+	var records []AuditRecord
+
+	responseHandler := func(response *http.Response) (cursor, error) {
+		var result ListAuditRecordsResponse
+		if err := c.decodeJSON(response, &result); err != nil {
+			return cursor{}, err
+		}
+
+		for _, r := range result.Records {
+			if include(r) {
+				records = append(records, r)
+			}
+		}
+
+		return cursor{
+			Limit:      result.Limit,
+			NextCursor: *result.NextCursor,
+		}, nil
+	}
+
+	u := fmt.Sprintf("%s?%s", auditBaseURL, v.Encode())
+	if err := c.cursorGet(ctx, u, responseHandler); err != nil {
+		return nil, err
+	}
+
+	return records, nil
+}
diff --git a/client.go b/client.go
index 67d49b3..bdc2970 100644
--- a/client.go
+++ b/client.go
@@ -656,3 +656,53 @@ func (c *Client) pagedGet(ctx context.Context, basePath string, handler response
 
 	return nil
 }
+
+type cursor struct {
+	// The minimum of the 'limit' parameter used in the request or
+	// the maximum request size of the API.
+	Limit uint
+	// An opaque string used to fetch the next set of results or 'null'
+	// if no additional results are available.
+	NextCursor string
+}
+
+// cursorHandler is capable of parsing a response, using cursor-based pagination.
+// At a minimum it must extract the page information for the current page.
+type cursorHandler func(r *http.Response) (cursor, error)
+
+func (c *Client) cursorGet(ctx context.Context, basePath string, handler cursorHandler) error {
+	var next string
+
+	basePrefix := getBasePrefix(basePath)
+
+	for {
+		var cs string
+		if len(next) > 0 {
+			cs = fmt.Sprintf("cursor=%s", next)
+		}
+
+		// The next set of results can be obtained by providing the
+		// NextCursor value from the previous request in a cursor
+		// query parameter on the subsequent request.
+		resp, err := c.do(ctx, http.MethodGet, fmt.Sprintf("%s%s", basePrefix, cs), nil, nil)
+		if err != nil {
+			return err
+		}
+
+		// Call handler to extract page information and execute additional necessary handling.
+		c, err := handler(resp)
+		if err != nil {
+			return err
+		}
+
+		// Stop parsing if there are no more results.
+		if len(c.NextCursor) == 0 {
+			break
+		}
+
+		// Otherwise, update next to parse more results.
+		next = c.NextCursor
+	}
+
+	return nil
+}
diff --git a/incident.go b/incident.go
index 1431a6b..3fcd1e4 100644
--- a/incident.go
+++ b/incident.go
@@ -98,7 +98,6 @@ type Incident struct {
 	ConferenceBridge     *ConferenceBridge    `json:"conference_bridge,omitempty"`
 	AssignedVia          string               `json:"assigned_via,omitempty"`
 	Occurrence           *Occurrence          `json:"occurrence,omitempty"`
-	IncidentResponders   []IncidentResponders `json:"incidents_responders,omitempty"`
 }
 
 // ListIncidentsResponse is the response structure when calling the ListIncident API endpoint.
@@ -750,10 +749,10 @@ func (c *Client) ManageIncidentAlerts(ctx context.Context, incidentID, from stri
 
 // IncidentStatusUpdate is a status update for the specified incident.
 type IncidentStatusUpdate struct {
-	ID        string    `json:"id"`
-	Message   string    `json:"message"`
-	CreatedAt string    `json:"created_at"`
-	Sender    APIObject `json:"sender"`
+	ID        string	`json:"id"`
+	Message   string	`json:"message"`
+	CreatedAt string	`json:"created_at"`
+	Sender    APIObject	`json:"sender"`
 }
 
 // CreateIncidentStatusUpdate creates a new status update for the specified incident.
diff --git a/response_play.go b/response_play.go
deleted file mode 100644
index b46bacc..0000000
--- a/response_play.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package pagerduty
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-
-	"github.com/google/go-querystring/query"
-)
-
-type ResponsePlay struct {
-	ID                 string          `json:"id,omitempty"`
-	Type               string          `json:"type,omitempty"`
-	Summary            string          `json:"summary,omitempty"`
-	Self               string          `json:"self,omitempty"`
-	HTMLURL            string          `json:"html_url,omitempty"`
-	Name               string          `json:"name,omitempty"`
-	Description        string          `json:"description"`
-	Team               *APIReference   `json:"team,omitempty"`
-	Subscribers        []*APIReference `json:"subscribers,omitempty"`
-	SubscribersMessage string          `json:"subscribers_message"`
-	Responders         []*APIReference `json:"responders,omitempty"`
-	RespondersMessage  string          `json:"responders_message"`
-	Runnability        *string         `json:"runnability"`
-	ConferenceNumber   *string         `json:"conference_number"`
-	ConferenceUrl      *string         `json:"conference_url"`
-	ConferenceType     *string         `json:"conference_type"`
-}
-
-type ListResponsePlaysResponse struct {
-	ResponsePlays []ResponsePlay `json:"response_plays"`
-}
-
-type ListResponsePlaysOptions struct {
-	FilterForManualRun bool   `url:"filter_for_manual_run,omitempty"`
-	Query              string `url:"query,omitempty"`
-}
-
-// ListResponsePlays lists existing response plays.
-func (c *Client) ListResponsePlays(ctx context.Context, o ListResponsePlaysOptions) (ListResponsePlaysResponse, error) {
-	var result ListResponsePlaysResponse
-
-	v, err := query.Values(o)
-	if err != nil {
-		return result, err
-	}
-
-	resp, err := c.get(ctx, "/response_plays?"+v.Encode())
-	if err != nil {
-		return result, err
-	}
-
-	if err = c.decodeJSON(resp, &result); err != nil {
-		return result, err
-	}
-
-	return result, nil
-}
-
-// CreateResponsePlay creates a new response play.
-func (c *Client) CreateResponsePlay(ctx context.Context, rp ResponsePlay) (ResponsePlay, error) {
-	d := map[string]ResponsePlay{
-		"response_play": rp,
-	}
-
-	resp, err := c.post(ctx, "/response_plays", d, nil)
-	return getResponsePlayFromResponse(c, resp, err)
-}
-
-// GetResponsePlay gets details about an existing response play.
-func (c *Client) GetResponsePlay(ctx context.Context, id string) (ResponsePlay, error) {
-	resp, err := c.get(ctx, "/response_plays/"+id)
-	return getResponsePlayFromResponse(c, resp, err)
-}
-
-// UpdateResponsePlay updates an existing response play.
-func (c *Client) UpdateResponsePlay(ctx context.Context, rp ResponsePlay) (ResponsePlay, error) {
-	d := map[string]ResponsePlay{
-		"response_play": rp,
-	}
-
-	resp, err := c.put(ctx, "/response_plays/"+rp.ID, d, nil)
-	return getResponsePlayFromResponse(c, resp, err)
-}
-
-// DeleteResponsePlay deletes an existing response play.
-func (c *Client) DeleteResponsePlay(ctx context.Context, id string) error {
-	_, err := c.delete(ctx, "/response_plays/"+id)
-	return err
-}
-
-// RunResponsePlay runs a response play on a given incident.
-func (c *Client) RunResponsePlay(ctx context.Context, from string, responsePlayID string, incidentID string) error {
-	d := map[string]APIReference{
-		"incident": {
-			ID:   incidentID,
-			Type: "incident_reference",
-		},
-	}
-
-	h := map[string]string{
-		"From": from,
-	}
-
-	resp, err := c.post(ctx, "/response_plays/"+responsePlayID+"/run", d, h)
-	if err != nil {
-		return err
-	}
-
-	if resp.StatusCode != http.StatusOK {
-		return fmt.Errorf("failed to run response play %s on incident %s (status code: %d)", responsePlayID, incidentID, resp.StatusCode)
-	}
-
-	return nil
-}
-
-func getResponsePlayFromResponse(c *Client, resp *http.Response, err error) (ResponsePlay, error) {
-	if err != nil {
-		return ResponsePlay{}, err
-	}
-
-	var target map[string]ResponsePlay
-	if dErr := c.decodeJSON(resp, &target); dErr != nil {
-		return ResponsePlay{}, fmt.Errorf("Could not decode JSON response: %v", dErr)
-	}
-
-	const rootNode = "response_play"
-
-	t, nodeOK := target[rootNode]
-	if !nodeOK {
-		return ResponsePlay{}, fmt.Errorf("JSON response does not have %s field", rootNode)
-	}
-
-	return t, nil
-}
diff --git a/service.go b/service.go
index f231bea..887e536 100644
--- a/service.go
+++ b/service.go
@@ -95,7 +95,6 @@ type Service struct {
 	AlertGrouping           string                   `json:"alert_grouping,omitempty"`
 	AlertGroupingTimeout    *uint                    `json:"alert_grouping_timeout,omitempty"`
 	AlertGroupingParameters *AlertGroupingParameters `json:"alert_grouping_parameters,omitempty"`
-	ResponsePlay            *APIObject               `json:"response_play,omitempty"`
 }
 
 // AlertGroupingParameters defines how alerts on the servicewill be automatically grouped into incidents


Test patch:
diff --git a/audit_test.go b/audit_test.go
new file mode 100644
index 0000000..8d59335
--- /dev/null
+++ b/audit_test.go
@@ -0,0 +1,95 @@
+package pagerduty
+
+import (
+	"context"
+	"net/http"
+	"testing"
+)
+
+func TestAudit_List(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/audit/records", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		_, _ = w.Write([]byte(`{"records":[{"id":"PDRECORDID4_UPDATED_USERS_NOTIFICATION_RULE","execution_time":"2020-06-04T15:30:16.272Z","execution_context":{"request_id":"222lDEOIH-534-4ljhLHJjh222","remote_address":"201.19.20.19"},"actors":[{"id":"PDUSER","summary":"John Snow","type":"user_reference"}],"method":{"type":"api_token","truncated_token":"2adm"},"root_resource":{"id":"PDUSER","type":"user_reference","summary":"John Snow"},"action":"update","details":{"resource":{"id":"PXOGWUS","type":"assignment_notification_rule_reference","summary":"0 minutes: channel P1IAAPZ"},"fields":[{"name":"start_delay_in_minutes","before_value":"0","value":"2"}],"references":[{"name":"contact_method","removed":[{"id":"POE6L88","type":"push_notification_contact_method_reference","summary":"Pixel 3"}],"added":[{"id":"P4GTUMK","type":"sms_contact_method_reference","summary":"Mobile"}]}]}}],"next_cursor":null,"limit":10}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+	recordOpts := ListAuditRecordsOptions{
+		Limit:  10,
+		Cursor: "",
+	}
+
+	resp, err := client.ListAuditRecords(context.Background(), recordOpts)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	want := ListAuditRecordsResponse{
+		Records: []AuditRecord{
+			{
+				ID:            "PDRECORDID4_UPDATED_USERS_NOTIFICATION_RULE",
+				ExecutionTime: "2020-06-04T15:30:16.272Z",
+				ExecutionContext: ExecutionContext{
+					RequestID:     "222lDEOIH-534-4ljhLHJjh222",
+					RemoteAddress: "201.19.20.19",
+				},
+				Actors: []APIObject{
+					{
+						ID:      "PDUSER",
+						Summary: "John Snow",
+						Type:    "user_reference",
+					},
+				},
+				Method: Method{
+					Type:           "api_token",
+					TruncatedToken: "2adm",
+				},
+				RootResource: APIObject{
+					ID:      "PDUSER",
+					Summary: "John Snow",
+					Type:    "user_reference",
+				},
+				Action: "update",
+				Details: Details{
+					Resource: APIObject{
+						ID:      "PXOGWUS",
+						Type:    "assignment_notification_rule_reference",
+						Summary: "0 minutes: channel P1IAAPZ",
+					},
+					Fields: []Field{
+						{
+							Name:        "start_delay_in_minutes",
+							BeforeValue: "0",
+							Value:       "2",
+						},
+					},
+					References: []Reference{
+						{
+							Name: "contact_method",
+							Removed: []APIObject{
+								{
+									ID:      "POE6L88",
+									Type:    "push_notification_contact_method_reference",
+									Summary: "Pixel 3",
+								},
+							},
+							Added: []APIObject{
+								{
+									ID:      "P4GTUMK",
+									Type:    "sms_contact_method_reference",
+									Summary: "Mobile",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		NextCursor: nil,
+		Limit:      10,
+	}
+
+	testEqual(t, want, resp)
+}
diff --git a/incident_test.go b/incident_test.go
index 9f9e911..a6a42f3 100644
--- a/incident_test.go
+++ b/incident_test.go
@@ -495,7 +495,7 @@ func TestIncident_Get(t *testing.T) {
 
 	mux.HandleFunc("/incidents/1", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
-		_, _ = w.Write([]byte(`{"incident": {"id": "1", "incidents_responders": [{"incident": {"id": "1"}}]}}`))
+		_, _ = w.Write([]byte(`{"incident": {"id": "1"}}`))
 	})
 
 	client := defaultTestClient(server.URL, "foo")
@@ -503,7 +503,7 @@ func TestIncident_Get(t *testing.T) {
 	id := "1"
 	res, err := client.GetIncident(id)
 
-	want := &Incident{APIObject: APIObject{ID: "1"}, IncidentResponders: []IncidentResponders{{Incident: APIObject{ID: "1"}}}}
+	want := &Incident{APIObject: APIObject{ID: "1"}}
 
 	if err != nil {
 		t.Fatal(err)
@@ -985,7 +985,7 @@ func TestIncident_CreateIncidentStatusUpdate(t *testing.T) {
 		Message: wantMessage,
 		Sender: APIObject{
 			Summary: wantFrom,
-			Type:    "user_reference",
+			Type: "user_reference",
 		},
 	}
 
diff --git a/response_play_test.go b/response_play_test.go
deleted file mode 100644
index 1fc6e2c..0000000
--- a/response_play_test.go
+++ /dev/null
@@ -1,151 +0,0 @@
-package pagerduty
-
-import (
-	"context"
-	"net/http"
-	"testing"
-)
-
-// ListResponsePlays
-func TestResponsePlay_List(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/response_plays", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, "GET")
-		_, _ = w.Write([]byte(`{"response_plays": [{"id": "1"}]}`))
-	})
-
-	client := defaultTestClient(server.URL, "foo")
-	res, err := client.ListResponsePlays(context.TODO(), ListResponsePlaysOptions{})
-
-	want := ListResponsePlaysResponse{
-		ResponsePlays: []ResponsePlay{
-			{
-				ID: "1",
-			},
-		},
-	}
-
-	if err != nil {
-		t.Fatal(err)
-	}
-	testEqual(t, want, res)
-}
-
-// Get ResponsePlay
-func TestResponsePlay_Get(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/response_plays/1", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, "GET")
-		_, _ = w.Write([]byte(`{"response_play": {"id": "1","name":"foo"}}`))
-	})
-
-	client := defaultTestClient(server.URL, "foo")
-
-	id := "1"
-	res, err := client.GetResponsePlay(context.TODO(), id)
-
-	want := ResponsePlay{
-		ID:   "1",
-		Name: "foo",
-	}
-
-	if err != nil {
-		t.Fatal(err)
-	}
-	testEqual(t, want, res)
-}
-
-// Create ResponsePlay
-func TestResponsePlay_Create(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/response_plays", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, "POST")
-		_, _ = w.Write([]byte(`{"response_play": {"id": "1","name":"foo"}}`))
-	})
-
-	client := defaultTestClient(server.URL, "foo")
-	input := ResponsePlay{
-		Name: "foo",
-	}
-	res, err := client.CreateResponsePlay(context.TODO(), input)
-
-	want := ResponsePlay{
-		ID:   "1",
-		Name: "foo",
-	}
-
-	if err != nil {
-		t.Fatal(err)
-	}
-	testEqual(t, want, res)
-}
-
-// Update ResponsePlay
-func TestResponsePlay_Update(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/response_plays/1", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, "PUT")
-		_, _ = w.Write([]byte(`{"response_play": {"id": "1","name":"foo"}}`))
-	})
-
-	client := defaultTestClient(server.URL, "foo")
-
-	input := ResponsePlay{
-		ID:   "1",
-		Name: "foo",
-	}
-	res, err := client.UpdateResponsePlay(context.TODO(), input)
-
-	want := ResponsePlay{
-		ID:   "1",
-		Name: "foo",
-	}
-
-	if err != nil {
-		t.Fatal(err)
-	}
-	testEqual(t, want, res)
-}
-
-// Delete ResponsePlay
-func TestResponsePlay_Delete(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/response_plays/1", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, "DELETE")
-	})
-
-	client := defaultTestClient(server.URL, "foo")
-	id := "1"
-	err := client.DeleteResponsePlay(context.TODO(), id)
-	if err != nil {
-		t.Fatal(err)
-	}
-}
-
-// Run ResponsePlay
-func TestResponsePlay_Run(t *testing.T) {
-	setup()
-	defer teardown()
-
-	mux.HandleFunc("/response_plays/1/run", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, "POST")
-		_, _ = w.Write([]byte(`{"incident": {"id": "5","type":"incident_reference"}}`))
-	})
-
-	client := defaultTestClient(server.URL, "foo")
-	err := client.RunResponsePlay(context.TODO(), "foo@example.com", "1", "5")
-
-	if err != nil {
-		t.Fatal(err)
-	}
-}