Repository: moov-io/customers (go)

Issue:
customers: status update needs to be on main server, not admin
Customers Version: `master`

**What were you trying to do?**
Updating a customer's status should be `PUT /customers/{customerID}/status` on the main HTTP server. It seems to exist on the admin server, so we have a LB hack to work around it. 

- [x] Remove customer status update from admin server
- [x] Remove OpenAPI endpoint in `api/admin.yml`
- [x] Ensure the same logic for the admin route exists in the main HTTP route
- [x] Ensure the `api/client.yml` OpenAPI spec has this endpoint

Pull request:
http: move update customer status endpoint from admin server
Moved and refactored update customer status endpoint from admin server to client server.

Updated related docs and tests.

Fixes #199

Patch:
diff --git a/api/admin.yaml b/api/admin.yaml
index e3a63c6..d14948b 100644
--- a/api/admin.yaml
+++ b/api/admin.yaml
@@ -83,36 +83,6 @@ paths:
             application/json:
               schema:
                 $ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
-  /customers/{customerID}/status:
-    put:
-      tags: [Customers]
-      summary: Update Customer status
-      description: Updates a customer status and initiates the required checks for that new status
-      operationId: updateCustomerStatus
-      parameters:
-        - name: customerID
-          in: path
-          description: Customer ID
-          required: true
-          schema:
-            type: string
-            example: e210a9d6-d755-4455-9bd2-9577ea7e1081
-      requestBody:
-        required: true
-        content:
-          application/json:
-            schema:
-              $ref: '#/components/schemas/UpdateCustomerStatus'
-      responses:
-        '200':
-          description: Updated Customer status
-        '400':
-          description: See error message
-          content:
-            application/json:
-              schema:
-                $ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
-
 components:
   schemas:
     LivenessProbes:
@@ -133,13 +103,3 @@ components:
           example: Please read and accept the attached document
       required:
         - text
-    UpdateCustomerStatus:
-      properties:
-        comment:
-          type: string
-          description: Free form text for admin users
-          example: User submitted address information
-        status:
-          $ref: 'https://raw.githubusercontent.com/moov-io/customers/master/api/client.yaml#/components/schemas/CustomerStatus'
-      required:
-        - status
diff --git a/api/client.yaml b/api/client.yaml
index 0cfb44f..1220ac1 100644
--- a/api/client.yaml
+++ b/api/client.yaml
@@ -856,7 +856,7 @@ paths:
                 $ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
   /customers/{customerID}/accounts/{accountID}/ofac:
     get:
-      tags: [ Customers ]
+      tags: [Customers]
       summary: Latest Account OFAC search
       description: Get the latest OFAC search for an Account
       operationId: getLatestAccountOFACSearch
@@ -902,7 +902,7 @@ paths:
                 $ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
   /customers/{customerID}/accounts/{accountID}/refresh/ofac:
     put:
-      tags: [ Customers ]
+      tags: [Customers]
       summary: Refresh Account OFAC search
       description: Refresh OFAC search for a given Account
       operationId: refreshAccountOFACSearch
@@ -948,7 +948,7 @@ paths:
                 $ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
   /customers/{customerID}/accounts/{accountID}/status:
     put:
-      tags: [ Customers ]
+      tags: [Customers]
       summary: Update Account Status
       description: Update the status for the specified accountID
       operationId: updateAccountStatus
diff --git a/cmd/server/main.go b/cmd/server/main.go
index ecfc2d9..533151f 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -132,7 +132,6 @@ func main() {
 	ofac := customers.NewOFACSearcher(customerRepo, watchmanClient)
 
 	// Register our admin routes
-	customers.AddApprovalRoutes(logger, adminServer, customerRepo, customerSSNRepo, ofac)
 	documents.AddDisclaimerAdminRoutes(logger, adminServer, disclaimerRepo, documentRepo)
 
 	// Setup Customer SSN storage wrapper
diff --git a/pkg/admin/README.md b/pkg/admin/README.md
index 71c742d..ff37552 100644
--- a/pkg/admin/README.md
+++ b/pkg/admin/README.md
@@ -37,16 +37,13 @@ Class | Method | HTTP request | Description
 *AdminApi* | [**GetLivenessProbes**](docs/AdminApi.md#getlivenessprobes) | **Get** /live | Get Liveness Probes
 *AdminApi* | [**GetVersion**](docs/AdminApi.md#getversion) | **Get** /version | Get Version
 *CustomersApi* | [**CreateCustomerDisclaimer**](docs/CustomersApi.md#createcustomerdisclaimer) | **Post** /customers/{customerID}/disclaimers | Create disclaimer
-*CustomersApi* | [**UpdateCustomerStatus**](docs/CustomersApi.md#updatecustomerstatus) | **Put** /customers/{customerID}/status | Update Customer status
 
 
 ## Documentation For Models
 
  - [CreateUserDisclaimer](docs/CreateUserDisclaimer.md)
- - [CustomerStatus](docs/CustomerStatus.md)
  - [Error](docs/Error.md)
  - [LivenessProbes](docs/LivenessProbes.md)
- - [UpdateCustomerStatus](docs/UpdateCustomerStatus.md)
 
 
 ## Documentation For Authorization
diff --git a/pkg/admin/api/openapi.yaml b/pkg/admin/api/openapi.yaml
index 26bea3e..b9f578d 100644
--- a/pkg/admin/api/openapi.yaml
+++ b/pkg/admin/api/openapi.yaml
@@ -87,39 +87,6 @@ paths:
       summary: Create disclaimer
       tags:
       - Customers
-  /customers/{customerID}/status:
-    put:
-      description: Updates a customer status and initiates the required checks for
-        that new status
-      operationId: updateCustomerStatus
-      parameters:
-      - description: Customer ID
-        explode: false
-        in: path
-        name: customerID
-        required: true
-        schema:
-          example: e210a9d6-d755-4455-9bd2-9577ea7e1081
-          type: string
-        style: simple
-      requestBody:
-        content:
-          application/json:
-            schema:
-              $ref: '#/components/schemas/UpdateCustomerStatus'
-        required: true
-      responses:
-        "200":
-          description: Updated Customer status
-        "400":
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/Error'
-          description: See error message
-      summary: Update Customer status
-      tags:
-      - Customers
 components:
   schemas:
     LivenessProbes:
@@ -145,18 +112,6 @@ components:
           type: string
       required:
       - text
-    UpdateCustomerStatus:
-      example:
-        comment: User submitted address information
-      properties:
-        comment:
-          description: Free form text for admin users
-          example: User submitted address information
-          type: string
-        status:
-          $ref: '#/components/schemas/CustomerStatus'
-      required:
-      - status
     Error:
       properties:
         error:
@@ -165,12 +120,3 @@ components:
           type: string
       required:
       - error
-    CustomerStatus:
-      description: State of the customer
-      enum:
-      - Deceased
-      - Rejected
-      - Unknown
-      - ReceiveOnly
-      - Verified
-      type: string
diff --git a/pkg/admin/api_customers.go b/pkg/admin/api_customers.go
index b9a518c..c9f65fc 100644
--- a/pkg/admin/api_customers.go
+++ b/pkg/admin/api_customers.go
@@ -103,82 +103,3 @@ func (a *CustomersApiService) CreateCustomerDisclaimer(ctx _context.Context, cus
 
 	return localVarHTTPResponse, nil
 }
-
-/*
-UpdateCustomerStatus Update Customer status
-Updates a customer status and initiates the required checks for that new status
- * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
- * @param customerID Customer ID
- * @param updateCustomerStatus
-*/
-func (a *CustomersApiService) UpdateCustomerStatus(ctx _context.Context, customerID string, updateCustomerStatus UpdateCustomerStatus) (*_nethttp.Response, error) {
-	var (
-		localVarHTTPMethod   = _nethttp.MethodPut
-		localVarPostBody     interface{}
-		localVarFormFileName string
-		localVarFileName     string
-		localVarFileBytes    []byte
-	)
-
-	// create path and map variables
-	localVarPath := a.client.cfg.BasePath + "/customers/{customerID}/status"
-	localVarPath = strings.Replace(localVarPath, "{"+"customerID"+"}", _neturl.QueryEscape(parameterToString(customerID, "")), -1)
-
-	localVarHeaderParams := make(map[string]string)
-	localVarQueryParams := _neturl.Values{}
-	localVarFormParams := _neturl.Values{}
-
-	// to determine the Content-Type header
-	localVarHTTPContentTypes := []string{"application/json"}
-
-	// set Content-Type header
-	localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
-	if localVarHTTPContentType != "" {
-		localVarHeaderParams["Content-Type"] = localVarHTTPContentType
-	}
-
-	// to determine the Accept header
-	localVarHTTPHeaderAccepts := []string{"application/json"}
-
-	// set Accept header
-	localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
-	if localVarHTTPHeaderAccept != "" {
-		localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
-	}
-	// body params
-	localVarPostBody = &updateCustomerStatus
-	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes)
-	if err != nil {
-		return nil, err
-	}
-
-	localVarHTTPResponse, err := a.client.callAPI(r)
-	if err != nil || localVarHTTPResponse == nil {
-		return localVarHTTPResponse, err
-	}
-
-	localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body)
-	localVarHTTPResponse.Body.Close()
-	if err != nil {
-		return localVarHTTPResponse, err
-	}
-
-	if localVarHTTPResponse.StatusCode >= 300 {
-		newErr := GenericOpenAPIError{
-			body:  localVarBody,
-			error: localVarHTTPResponse.Status,
-		}
-		if localVarHTTPResponse.StatusCode == 400 {
-			var v Error
-			err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
-			if err != nil {
-				newErr.error = err.Error()
-				return localVarHTTPResponse, newErr
-			}
-			newErr.model = v
-		}
-		return localVarHTTPResponse, newErr
-	}
-
-	return localVarHTTPResponse, nil
-}
diff --git a/pkg/admin/docs/CustomerStatus.md b/pkg/admin/docs/CustomerStatus.md
deleted file mode 100644
index 3928da5..0000000
--- a/pkg/admin/docs/CustomerStatus.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# CustomerStatus
-
-## Properties
-
-Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
-
-[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
-
-
diff --git a/pkg/admin/docs/CustomersApi.md b/pkg/admin/docs/CustomersApi.md
index ad1930e..44b069f 100644
--- a/pkg/admin/docs/CustomersApi.md
+++ b/pkg/admin/docs/CustomersApi.md
@@ -5,7 +5,6 @@ All URIs are relative to *http://localhost:9097*
 Method | HTTP request | Description
 ------------- | ------------- | -------------
 [**CreateCustomerDisclaimer**](CustomersApi.md#CreateCustomerDisclaimer) | **Post** /customers/{customerID}/disclaimers | Create disclaimer
-[**UpdateCustomerStatus**](CustomersApi.md#UpdateCustomerStatus) | **Put** /customers/{customerID}/status | Update Customer status
 
 
 
@@ -43,38 +42,3 @@ No authorization required
 [[Back to Model list]](../README.md#documentation-for-models)
 [[Back to README]](../README.md)
 
-
-## UpdateCustomerStatus
-
-> UpdateCustomerStatus(ctx, customerID, updateCustomerStatus)
-
-Update Customer status
-
-Updates a customer status and initiates the required checks for that new status
-
-### Required Parameters
-
-
-Name | Type | Description  | Notes
-------------- | ------------- | ------------- | -------------
-**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc.
-**customerID** | **string**| Customer ID | 
-**updateCustomerStatus** | [**UpdateCustomerStatus**](UpdateCustomerStatus.md)|  | 
-
-### Return type
-
- (empty response body)
-
-### Authorization
-
-No authorization required
-
-### HTTP request headers
-
-- **Content-Type**: application/json
-- **Accept**: application/json
-
-[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints)
-[[Back to Model list]](../README.md#documentation-for-models)
-[[Back to README]](../README.md)
-
diff --git a/pkg/admin/docs/UpdateCustomerStatus.md b/pkg/admin/docs/UpdateCustomerStatus.md
deleted file mode 100644
index 7dfecac..0000000
--- a/pkg/admin/docs/UpdateCustomerStatus.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# UpdateCustomerStatus
-
-## Properties
-
-Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
-**Comment** | **string** | Free form text for admin users | [optional] 
-**Status** | [**CustomerStatus**](CustomerStatus.md) |  | 
-
-[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
-
-
diff --git a/pkg/admin/model_customer_status.go b/pkg/admin/model_customer_status.go
deleted file mode 100644
index f16947b..0000000
--- a/pkg/admin/model_customer_status.go
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Customers Admin API
- *
- * Customers focuses on solving authentic identification of humans who are legally able to hold and transfer currency within the US. Primarily this project solves [Know Your Customer](https://en.wikipedia.org/wiki/Know_your_customer) (KYC), [Customer Identification Program](https://en.wikipedia.org/wiki/Customer_Identification_Program) (CIP), [Office of Foreign Asset Control](https://www.treasury.gov/about/organizational-structure/offices/Pages/Office-of-Foreign-Assets-Control.aspx) (OFAC) checks and verification workflows to comply with US federal law and ensure authentic transfers. Also, Customers has an objective to be a service for detailed due diligence on individuals and companies for Financial Institutions and services in a modernized and extensible way.
- *
- * API version: v1
- * Generated by: OpenAPI Generator (https://openapi-generator.tech)
- */
-
-package admin
-
-// CustomerStatus State of the customer
-type CustomerStatus string
-
-// List of CustomerStatus
-const (
-	DECEASED     CustomerStatus = "Deceased"
-	REJECTED     CustomerStatus = "Rejected"
-	UNKNOWN      CustomerStatus = "Unknown"
-	RECEIVE_ONLY CustomerStatus = "ReceiveOnly"
-	VERIFIED     CustomerStatus = "Verified"
-)
diff --git a/pkg/admin/model_update_customer_status.go b/pkg/admin/model_update_customer_status.go
deleted file mode 100644
index 5b6f2e8..0000000
--- a/pkg/admin/model_update_customer_status.go
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Customers Admin API
- *
- * Customers focuses on solving authentic identification of humans who are legally able to hold and transfer currency within the US. Primarily this project solves [Know Your Customer](https://en.wikipedia.org/wiki/Know_your_customer) (KYC), [Customer Identification Program](https://en.wikipedia.org/wiki/Customer_Identification_Program) (CIP), [Office of Foreign Asset Control](https://www.treasury.gov/about/organizational-structure/offices/Pages/Office-of-Foreign-Assets-Control.aspx) (OFAC) checks and verification workflows to comply with US federal law and ensure authentic transfers. Also, Customers has an objective to be a service for detailed due diligence on individuals and companies for Financial Institutions and services in a modernized and extensible way.
- *
- * API version: v1
- * Generated by: OpenAPI Generator (https://openapi-generator.tech)
- */
-
-package admin
-
-// UpdateCustomerStatus struct for UpdateCustomerStatus
-type UpdateCustomerStatus struct {
-	// Free form text for admin users
-	Comment string         `json:"comment,omitempty"`
-	Status  CustomerStatus `json:"status"`
-}
diff --git a/pkg/customers/address.go b/pkg/customers/address.go
index fa5fef1..b9f46ce 100644
--- a/pkg/customers/address.go
+++ b/pkg/customers/address.go
@@ -10,6 +10,7 @@ import (
 	"github.com/gorilla/mux"
 	moovhttp "github.com/moov-io/base/http"
 
+	"github.com/moov-io/customers/pkg/client"
 	"github.com/moov-io/customers/pkg/route"
 )
 
@@ -125,3 +126,12 @@ func (req *updateCustomerAddressRequest) validate() error {
 		return fmt.Errorf("updateCustomerAddressRequest: unknown type: %s", req.Type)
 	}
 }
+
+func containsValidPrimaryAddress(addrs []client.CustomerAddress) bool {
+	for i := range addrs {
+		if strings.EqualFold(addrs[i].Type, "primary") && addrs[i].Validated {
+			return true
+		}
+	}
+	return false
+}
diff --git a/pkg/customers/approval.go b/pkg/customers/approval.go
index 9db95e8..01a2114 100644
--- a/pkg/customers/approval.go
+++ b/pkg/customers/approval.go
@@ -8,11 +8,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
-	"os"
-	"strconv"
-	"strings"
 
-	"github.com/moov-io/base/admin"
 	moovhttp "github.com/moov-io/base/http"
 
 	"github.com/moov-io/customers/pkg/client"
@@ -21,54 +17,16 @@ import (
 	"github.com/go-kit/kit/log"
 )
 
-var (
-	ofacMatchThreshold float32 = func() float32 {
-		if v := os.Getenv("OFAC_MATCH_THRESHOLD"); v != "" {
-			f, err := strconv.ParseFloat(v, 32)
-			if err == nil && f > 0.00 {
-				return float32(f)
-			}
-		}
-		return 0.99 // default, 99%
-	}()
-)
-
-// AddApprovalRoutes contains "back office" admin endpoints used to validate (or reject) a Customer
-// TODO(adam): We need to hide these behind an admin level auth, but we'll write them for now.
-// What about a header like x-admin-id ??
-func AddApprovalRoutes(logger log.Logger, svc *admin.Server, repo CustomerRepository, customerSSNRepo CustomerSSNRepository, ofac *OFACSearcher) {
-	svc.AddHandler("/customers/{customerID}/status", updateCustomerStatus(logger, repo, customerSSNRepo, ofac))
-}
-
-type updateCustomerStatusRequest struct {
-	Comment string                `json:"comment,omitempty"`
-	Status  client.CustomerStatus `json:"status"`
-}
-
-func containsValidPrimaryAddress(addrs []client.CustomerAddress) bool {
-	for i := range addrs {
-		if strings.EqualFold(addrs[i].Type, "primary") && addrs[i].Validated {
-			return true
-		}
-	}
-	return false
-}
-
-func updateCustomerStatus(logger log.Logger, repo CustomerRepository, customerSSNRepo CustomerSSNRepository, ofac *OFACSearcher) http.HandlerFunc {
+func updateCustomerStatus(logger log.Logger, repo CustomerRepository) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		w = route.Responder(logger, w, r)
 
-		if r.Method != "PUT" {
-			moovhttp.Problem(w, fmt.Errorf("unsupported HTTP verb %s", r.Method))
-			return
-		}
-
-		customerID, requestID := route.GetCustomerID(w, r), moovhttp.GetRequestID(r)
+		customerID := route.GetCustomerID(w, r)
 		if customerID == "" {
 			return
 		}
 
-		var req updateCustomerStatusRequest
+		var req client.UpdateCustomerStatus
 		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 			moovhttp.Problem(w, err)
 			return
@@ -84,12 +42,12 @@ func updateCustomerStatus(logger log.Logger, repo CustomerRepository, customerSS
 			return
 		}
 
-		// Update Customer's status in the database
 		if err := repo.updateCustomerStatus(customerID, req.Status, req.Comment); err != nil {
 			moovhttp.Problem(w, err)
 			return
 		}
 
+		requestID := moovhttp.GetRequestID(r)
 		respondWithCustomer(logger, w, customerID, requestID, repo)
 	}
 }
diff --git a/pkg/customers/approval_ofac.go b/pkg/customers/approval_ofac.go
index 928ead1..ba62d34 100644
--- a/pkg/customers/approval_ofac.go
+++ b/pkg/customers/approval_ofac.go
@@ -10,18 +10,33 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
+	"os"
+	"strconv"
 	"time"
 
 	moovhttp "github.com/moov-io/base/http"
+	watchmanClient "github.com/moov-io/watchman/client"
+
 	"github.com/moov-io/customers/pkg/client"
 	"github.com/moov-io/customers/pkg/route"
 	"github.com/moov-io/customers/pkg/watchman"
-	watchmanClient "github.com/moov-io/watchman/client"
 
 	"github.com/go-kit/kit/log"
 	"github.com/gorilla/mux"
 )
 
+var (
+	ofacMatchThreshold float32 = func() float32 {
+		if v := os.Getenv("OFAC_MATCH_THRESHOLD"); v != "" {
+			f, err := strconv.ParseFloat(v, 32)
+			if err == nil && f > 0.00 {
+				return float32(f)
+			}
+		}
+		return 0.99 // default, 99%
+	}()
+)
+
 type ofacSearchResult struct {
 	EntityID  string    `json:"entityID"`
 	SDNName   string    `json:"sdnName"`
diff --git a/pkg/customers/customers.go b/pkg/customers/customers.go
index 2452b74..7f3b36c 100644
--- a/pkg/customers/customers.go
+++ b/pkg/customers/customers.go
@@ -33,6 +33,7 @@ func AddCustomerRoutes(logger log.Logger, r *mux.Router, repo CustomerRepository
 	r.Methods("DELETE").Path("/customers/{customerID}").HandlerFunc(deleteCustomer(logger, repo))
 	r.Methods("POST").Path("/customers").HandlerFunc(createCustomer(logger, repo, customerSSNStorage, ofac))
 	r.Methods("PUT").Path("/customers/{customerID}/metadata").HandlerFunc(replaceCustomerMetadata(logger, repo))
+	r.Methods("PUT").Path("/customers/{customerID}/status").HandlerFunc(updateCustomerStatus(logger, repo))
 }
 
 // formatCustomerName returns a Customer's name joined as one string. It accounts for


Test patch:
diff --git a/pkg/customers/approval_test.go b/pkg/customers/approval_test.go
index c5f001b..356a644 100644
--- a/pkg/customers/approval_test.go
+++ b/pkg/customers/approval_test.go
@@ -7,14 +7,13 @@ package customers
 import (
 	"bytes"
 	"encoding/json"
-	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
-	"strings"
 	"testing"
 
+	"github.com/gorilla/mux"
 	"github.com/moov-io/base"
-	"github.com/moov-io/base/admin"
+	"github.com/stretchr/testify/require"
 
 	"github.com/moov-io/customers/pkg/client"
 
@@ -28,67 +27,32 @@ func TestCustomers__updateCustomerStatus(t *testing.T) {
 			Status:     "none",
 		},
 	}
-	searcher := createTestOFACSearcher(repo, nil)
-	ssnRepo := &testCustomerSSNRepository{}
+	router := mux.NewRouter()
+	AddCustomerRoutes(log.NewNopLogger(), router, repo, testCustomerSSNStorage(t), createTestOFACSearcher(repo, nil))
 
-	svc := admin.NewServer(":10001")
-	defer svc.Shutdown()
-	AddApprovalRoutes(log.NewNopLogger(), svc, repo, ssnRepo, searcher)
-	go svc.Listen()
+	updateStatusRequest := client.UpdateCustomerStatus{
+		Status:  "ReceiveOnly",
+		Comment: "test comment",
+	}
+	payload, err := json.Marshal(&updateStatusRequest)
+	require.NoError(t, err)
 
-	body := strings.NewReader(`{"status": "ReceiveOnly", "comment": "test comment"}`)
-	req, err := http.NewRequest("PUT", "http://localhost:10001/customers/foo/status", body)
+	req, err := http.NewRequest("PUT", "/customers/_id_/status", bytes.NewReader(payload))
 	if err != nil {
 		t.Fatal(err)
 	}
 	req.Header.Set("x-organization", "test")
 	req.Header.Set("x-request-id", "test")
 
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	respBody, _ := ioutil.ReadAll(resp.Body)
-
-	if resp.StatusCode != http.StatusOK {
-		t.Errorf("bogus HTTP status: %d: %v", resp.StatusCode, string(respBody))
-	}
+	res := httptest.NewRecorder()
+	router.ServeHTTP(res, req)
+	require.Equal(t, http.StatusOK, res.Code)
 
 	var customer client.Customer
-	if err := json.NewDecoder(bytes.NewReader(respBody)).Decode(&customer); err != nil {
-		t.Fatal(err)
-	}
-	if customer.CustomerID == "" {
-		t.Errorf("missing customer JSON: %#v", customer)
-	}
-	if repo.updatedStatus != client.RECEIVE_ONLY {
-		t.Errorf("unexpected status: %s", repo.updatedStatus)
-	}
-}
-
-func TestCustomers__updateCustomerStatusFailure(t *testing.T) {
-	repo := &testCustomerRepository{}
-	searcher := createTestOFACSearcher(repo, nil)
-	ssnRepo := &testCustomerSSNRepository{}
-
-	w := httptest.NewRecorder()
-	req := httptest.NewRequest("GET", "/customers/foo/status", nil)
-	updateCustomerStatus(log.NewNopLogger(), repo, ssnRepo, searcher)(w, req)
-	w.Flush()
-
-	if w.Code != http.StatusBadRequest {
-		t.Errorf("bogus HTTP status: %d", w.Code)
-	}
-
-	// try the proper HTTP verb
-	w = httptest.NewRecorder()
-	req = httptest.NewRequest("PUT", "/customers/foo/status", nil)
-	updateCustomerStatus(log.NewNopLogger(), repo, ssnRepo, searcher)(w, req)
-	w.Flush()
-
-	if w.Code != http.StatusBadRequest {
-		t.Errorf("bogus HTTP status: %d", w.Code)
-	}
+	err = json.NewDecoder(res.Body).Decode(&customer)
+	require.NoError(t, err)
+	require.NotNil(t, customer)
+	require.Equal(t, updateStatusRequest.Status, repo.updatedStatus)
 }
 
 func TestCustomers__containsValidPrimaryAddress(t *testing.T) {