From bc34ebb87f94d01eaa3d9ee42bec3112ac818ea6 Mon Sep 17 00:00:00 2001 From: angel raynov Date: Wed, 24 Jul 2024 16:22:55 +0300 Subject: [PATCH 1/8] split policy reading --- main.go | 29 +++++++++++++++-------------- policy/policy.go | 7 +++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/main.go b/main.go index 75d0963..5bb05d6 100644 --- a/main.go +++ b/main.go @@ -132,7 +132,8 @@ func main() { healthService := health.NewHealthService(*appSystemCode, *appName, appDescription, rw, annotationsAPI, conceptRead) paths := map[string]string{ - policy.Key: policy.OpaPolicyPath, + policy.ReadKey: policy.OpaPolicyPath + policy.ReadKey, + policy.WriteKey: policy.OpaPolicyPath + policy.WriteKey, } opaClient := opa.NewOpenPolicyAgentClient(*OPAAddress, paths) @@ -150,24 +151,24 @@ func main() { func serveEndpoints(port string, apiYml string, handler *handler.Handler, healthService *health.HealthService, schemaHandler *schema.SchemasHandler, log *logger.UPPLogger, opaClient *opa.OpenPolicyAgentClient) { r := mux.NewRouter() - middlewareFunc := opa.CreateRequestMiddleware(opaClient, policy.Key, log, policy.Middleware) - responseMiddleware := opa.CreateResponseMiddleware(opaClient, policy.Key, log, policy.ResponseMiddleware) + writeMiddleware := opa.CreateRequestMiddleware(opaClient, policy.WriteKey, log, policy.Middleware) + respMiddleware := opa.CreateResponseMiddleware(opaClient, policy.ReadKey, log, policy.ResponseMiddleware) - authorizedRoutes := r.NewRoute().Subrouter() - authorizedGetRoute := r.NewRoute().Subrouter() + authorizedWriteRoutes := r.NewRoute().Subrouter() + authorizedReadRoutes := r.NewRoute().Subrouter() - authorizedRoutes.Use(middlewareFunc) - authorizedGetRoute.Use(responseMiddleware) + authorizedWriteRoutes.Use(writeMiddleware) + authorizedReadRoutes.Use(respMiddleware) - authorizedGetRoute.HandleFunc("/draft-annotations/content/{uuid}/annotations", handler.ReadAnnotations).Methods(http.MethodGet) + authorizedReadRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations", handler.ReadAnnotations).Methods(http.MethodGet) - authorizedRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations", handler.WriteAnnotations).Methods(http.MethodPut) - authorizedRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations", handler.AddAnnotation).Methods(http.MethodPost) - authorizedRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", handler.ReplaceAnnotation).Methods(http.MethodPatch) + authorizedWriteRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations", handler.WriteAnnotations).Methods(http.MethodPut) + authorizedWriteRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations", handler.AddAnnotation).Methods(http.MethodPost) + authorizedWriteRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", handler.ReplaceAnnotation).Methods(http.MethodPatch) - authorizedRoutes.HandleFunc("/draft-annotations/validate", handler.Validate).Methods(http.MethodPost) - authorizedRoutes.HandleFunc("/draft-annotations/schemas", schemaHandler.ListSchemas).Methods(http.MethodGet) - authorizedRoutes.HandleFunc("/draft-annotations/schemas/{schemaName}", schemaHandler.GetSchema).Methods(http.MethodGet) + r.HandleFunc("/draft-annotations/validate", handler.Validate).Methods(http.MethodPost) + r.HandleFunc("/draft-annotations/schemas", schemaHandler.ListSchemas).Methods(http.MethodGet) + r.HandleFunc("/draft-annotations/schemas/{schemaName}", schemaHandler.GetSchema).Methods(http.MethodGet) r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", handler.DeleteAnnotation).Methods(http.MethodDelete) diff --git a/policy/policy.go b/policy/policy.go index 8a3d23c..d6e02b0 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -8,10 +8,9 @@ import ( ) const ( - Key = "publication_based_authorization" - OpaPolicyPath = "draft_annotations_api/publication_based_authorization" - Read = "READ" - Write = "WRITE" + OpaPolicyPath = "draft_annotations_api/" + ReadKey = "read" + WriteKey = "write" ) type Result struct { From b643fbcf06f03188fd08f22bf8072cca9d32f573 Mon Sep 17 00:00:00 2001 From: angel raynov Date: Wed, 24 Jul 2024 16:34:47 +0300 Subject: [PATCH 2/8] write dummy policies for dredd --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 29f1cb8..189bfb7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -58,7 +58,8 @@ jobs: - run: name: Add a dummy OPA policy. command: | - curl -X PUT http://localhost:8181/v1/policies/publication_based_authorization -H 'Content-Type: text/plain' --data-raw 'package draft_annotations_api.publication_based_authorization is_authorized {true}' + curl -X PUT http://localhost:8181/v1/policies/read -H 'Content-Type: text/plain' --data-raw 'package draft_annotations_api.read is_authorized {true}' + curl -X PUT http://localhost:8181/v1/policies/write -H 'Content-Type: text/plain' --data-raw 'package draft_annotations_api.write is_authorized {true}' - run: name: Dredd API Testing command: dredd From 856e482b8a60c17773bf8201fa30bea7df81ce86 Mon Sep 17 00:00:00 2001 From: angel raynov Date: Tue, 30 Jul 2024 15:46:28 +0300 Subject: [PATCH 3/8] add delete endpoint auth --- annotations/annotations_api.go | 4 ++ annotations/annotations_api_test.go | 2 +- handler/handler.go | 58 +++++++++++++++++++++++++++++ handler/handler_test.go | 42 +++++++++++++++++++++ main.go | 3 +- policy/policy.go | 1 + 6 files changed, 107 insertions(+), 3 deletions(-) diff --git a/annotations/annotations_api.go b/annotations/annotations_api.go index 4795c9c..a1c3906 100644 --- a/annotations/annotations_api.go +++ b/annotations/annotations_api.go @@ -151,6 +151,10 @@ func (api *UPPAnnotationsAPI) getUPPAnnotationsResponse(ctx context.Context, con params.Add("lifecycle", lc) } + //by default publications are not returned from public-annotations-api, + //so we need to add this parameter to the query + params.Add("showPublication", "true") + baseURL.RawQuery = params.Encode() apiReqURI = baseURL.String() } diff --git a/annotations/annotations_api_test.go b/annotations/annotations_api_test.go index 9056e4a..a9f9b19 100644 --- a/annotations/annotations_api_test.go +++ b/annotations/annotations_api_test.go @@ -95,7 +95,7 @@ func TestHappyAnnotationsAPIWithLifecycles(t *testing.T) { tid := "tid_all-good" ctx := tidUtils.TransactionAwareContext(context.TODO(), tid) - annotationsServerMock := newAnnotationsAPIServerMock(t, tid, uuid, "lifecycle=pac&lifecycle=v1&lifecycle=next-video", http.StatusOK, "I am happy!") + annotationsServerMock := newAnnotationsAPIServerMock(t, tid, uuid, "lifecycle=pac&lifecycle=v1&lifecycle=next-video&showPublication=true", http.StatusOK, "I am happy!") defer annotationsServerMock.Close() log := logger.NewUPPLogger("draft-annotations-api", "INFO") diff --git a/handler/handler.go b/handler/handler.go index d54f45d..0ddfdee 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/Financial-Times/draft-annotations-api/policy" "github.com/Financial-Times/go-logger/v2" "github.com/gorilla/mux" @@ -96,9 +97,11 @@ func (h *Handler) DeleteAnnotation(w http.ResponseWriter, r *http.Request) { return } + var scheduledForDelete interface{} i := 0 for _, item := range uppList { if item.(map[string]interface{})["id"] == conceptID { + scheduledForDelete = item continue } uppList[i] = item @@ -106,6 +109,13 @@ func (h *Handler) DeleteAnnotation(w http.ResponseWriter, r *http.Request) { } uppList = uppList[:i] + if !isAuthorizedForDelete(r, scheduledForDelete) { + writeLog.Infof("Not authorized to delete annotation with current policy: %s", r.Header.Get("X-Policy")) + w.WriteHeader(http.StatusForbidden) + _, err = w.Write([]byte("Forbidden")) + return + } + annotationsBody := make(map[string]interface{}) annotationsBody["annotations"] = uppList _, newHash, err := h.saveAndReturnAnnotations(ctx, annotationsBody, writeLog, oldHash, contentUUID) @@ -640,3 +650,51 @@ func switchToIsClassifiedBy(toChange []interface{}) []interface{} { } return changed } + +func isAuthorizedForDelete(r *http.Request, scheduledForDelete interface{}) bool { + if scheduledForDelete == nil { + return true + } + + publication, ok := scheduledForDelete.(map[string]interface{})["publication"].([]interface{}) + if !ok { + //if no publication is returned, we assume its FT PINK + publication = []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"} + } + + af := r.Header.Get("Access-From") + if af == "" { + //if access-from header is missing, we skip the policy check + return true + } + + policyHeaders := r.Header.Get("X-Policy") + policyHeaders = strings.ReplaceAll(policyHeaders, " ", "") + splitPolicyHeaders := strings.Split(policyHeaders, ",") + allowDelete := false + + for _, header := range splitPolicyHeaders { + //extract the publication from the policy header + incomingPublication := strings.ReplaceAll(header, policy.WritePBLC, "") + + //verify if the extracted uuid is valid + _, err := uuid.Parse(incomingPublication) + if err != nil { + continue + } + + if contains(incomingPublication, publication) { + allowDelete = true + } + } + return allowDelete +} + +func contains(needle string, haystack []interface{}) bool { + for _, v := range haystack { + if v == needle { + return true + } + } + return false +} diff --git a/handler/handler_test.go b/handler/handler_test.go index fb3dbee..e2e5232 100644 --- a/handler/handler_test.go +++ b/handler/handler_test.go @@ -2862,6 +2862,48 @@ func TestUnhappyReplaceAnnotationWhenNoAnnotationsFound(t *testing.T) { assert.Equal(t, http.StatusNotFound, resp.StatusCode) } +func TestUnHappyDeleteAnnotationsWhenAuthorizationFails(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWrite, "").Return(mock.Anything, errors.New("sorry something failed")) + annAPI := new(AnnotationsAPIMock) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). + Return(expectedAnnotations["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) + return expectedAnnotations["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) + + req := httptest.NewRequest( + http.MethodDelete, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/0a619d71-9af5-3755-90dd-f789b686c67a", + nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + req.Header.Set("X-Policy", "PBLC_WRITE_8e6c705e-1132-42a2-8db0-c295e29e8658") + req.Header.Set("Access-From", "API Gateway") + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusForbidden, resp.StatusCode) +} + func TestValidate(t *testing.T) { _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") diff --git a/main.go b/main.go index 5bb05d6..748bd7c 100644 --- a/main.go +++ b/main.go @@ -165,13 +165,12 @@ func serveEndpoints(port string, apiYml string, handler *handler.Handler, health authorizedWriteRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations", handler.WriteAnnotations).Methods(http.MethodPut) authorizedWriteRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations", handler.AddAnnotation).Methods(http.MethodPost) authorizedWriteRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", handler.ReplaceAnnotation).Methods(http.MethodPatch) + authorizedWriteRoutes.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", handler.DeleteAnnotation).Methods(http.MethodDelete) r.HandleFunc("/draft-annotations/validate", handler.Validate).Methods(http.MethodPost) r.HandleFunc("/draft-annotations/schemas", schemaHandler.ListSchemas).Methods(http.MethodGet) r.HandleFunc("/draft-annotations/schemas/{schemaName}", schemaHandler.GetSchema).Methods(http.MethodGet) - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", handler.DeleteAnnotation).Methods(http.MethodDelete) - if apiYml != "" { if endpoint, err := apiEndpoint.NewAPIEndpointForFile(apiYml); err == nil { r.HandleFunc(apiEndpoint.DefaultPath, endpoint.ServeHTTP).Methods("GET") diff --git a/policy/policy.go b/policy/policy.go index d6e02b0..542b3e3 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -11,6 +11,7 @@ const ( OpaPolicyPath = "draft_annotations_api/" ReadKey = "read" WriteKey = "write" + WritePBLC = "PBLC_WRITE_" ) type Result struct { From a44819d2f787cd0c4c0be5ca62f50532116c668f Mon Sep 17 00:00:00 2001 From: angel raynov Date: Tue, 30 Jul 2024 15:52:37 +0300 Subject: [PATCH 4/8] change package name to handler so we can test the internal functions --- handler/handler_test.go | 3733 +++++++++++++++++++-------------------- 1 file changed, 1863 insertions(+), 1870 deletions(-) diff --git a/handler/handler_test.go b/handler/handler_test.go index e2e5232..22b56f5 100644 --- a/handler/handler_test.go +++ b/handler/handler_test.go @@ -1,32 +1,25 @@ -package handler_test +package handler import ( "bytes" "context" "encoding/json" "errors" - "fmt" "io" - "net" "net/http" "net/http/httptest" - "net/url" "os" "strconv" - "strings" "testing" "time" "github.com/Financial-Times/cm-annotations-ontology/validator" - "github.com/Financial-Times/go-logger/v2" - "github.com/gorilla/mux" - "github.com/stretchr/testify/require" - "github.com/Financial-Times/draft-annotations-api/annotations" - "github.com/Financial-Times/draft-annotations-api/handler" "github.com/Financial-Times/go-ft-http/fthttp" + "github.com/Financial-Times/go-logger/v2" tidutils "github.com/Financial-Times/transactionid-utils-go" randomdata "github.com/Pallinder/go-randomdata" + "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -53,7 +46,7 @@ func TestHappyFetchFromAnnotationsRW(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) + h := New(rw, annAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -129,7 +122,7 @@ func TestReadHasBrandAnnotation(t *testing.T) { annAPI := &AnnotationsAPIMock{} log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) + h := New(rw, annAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -176,7 +169,7 @@ func TestAddAnnotation(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) + h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) router := mux.NewRouter() router.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) @@ -298,7 +291,7 @@ func TestWriteHasBrandAnnotation(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) + h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) router := mux.NewRouter() router.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) @@ -413,7 +406,7 @@ func TestReplaceHasBrandAnnotation(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) router := mux.NewRouter() router.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) @@ -552,7 +545,7 @@ func TestUnHappyFetchFromAnnotationsRW(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) + h := New(rw, annAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -588,7 +581,7 @@ func TestUnHappyAugmenter(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) + h := New(rw, annAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -631,7 +624,7 @@ func TestFetchFromAnnotationsAPIIfNotFoundInRW(t *testing.T) { assert.Equal(t, annotationsAPIServerMock.URL+"/content/%v/annotations", annotationsAPI.Endpoint()) v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annotationsAPI, nil, aug, v, time.Second, log) + h := New(rw, annotationsAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -670,7 +663,7 @@ func TestFetchFromAnnotationsAPI404(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() annotationsAPI := annotations.NewUPPAnnotationsAPI(testClient, annotationsAPIServerMock.URL+"/content/%v/annotations", testBasicAuthUsername, testBasicAuthPassword, log) - h := handler.New(rw, annotationsAPI, nil, aug, v, time.Second, log) + h := New(rw, annotationsAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -706,7 +699,7 @@ func TestFetchFromAnnotationsAPI404NoAnnoPostMapping(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() annotationsAPI := annotations.NewUPPAnnotationsAPI(testClient, annotationsAPIServerMock.URL+"/content/%v/annotations", testBasicAuthUsername, testBasicAuthPassword, log) - h := handler.New(rw, annotationsAPI, nil, aug, v, time.Second, log) + h := New(rw, annotationsAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -741,7 +734,7 @@ func TestFetchFromAnnotationsAPI500(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() annotationsAPI := annotations.NewUPPAnnotationsAPI(testClient, annotationsAPIServerMock.URL+"/content/%v/annotations", testBasicAuthUsername, testBasicAuthPassword, log) - h := handler.New(rw, annotationsAPI, nil, aug, v, time.Second, log) + h := New(rw, annotationsAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -774,7 +767,7 @@ func TestFetchFromAnnotationsAPIWithInvalidURL(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() annotationsAPI := annotations.NewUPPAnnotationsAPI(testClient, ":#", testBasicAuthUsername, testBasicAuthPassword, log) - h := handler.New(rw, annotationsAPI, nil, aug, v, time.Second, log) + h := New(rw, annotationsAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -808,7 +801,7 @@ func TestFetchFromAnnotationsAPIWithConnectionError(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") v := validator.NewSchemaValidator(log).GetJSONValidator() annotationsAPI := annotations.NewUPPAnnotationsAPI(testClient, annotationsAPIServerMock.URL, testBasicAuthUsername, testBasicAuthPassword, log) - h := handler.New(rw, annotationsAPI, nil, aug, v, time.Second, log) + h := New(rw, annotationsAPI, nil, aug, v, time.Second, log) r := mux.NewRouter() r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) @@ -1315,1853 +1308,1853 @@ var augmentedAnnotationsSameConceptID = map[string]interface{}{ }, } -func TestSaveAnnotations(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - oldHash := randomdata.RandStringRunes(56) - newHash := randomdata.RandStringRunes(56) - rw := new(RWMock) - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return(newHash, nil) - - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - annotationsAPI := new(AnnotationsAPIMock) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) - return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) - - entity := bytes.Buffer{} - err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) - if err != nil { - t.Fatalf("failed to encode annotations: %v", err) - } - - req := httptest.NewRequest( - http.MethodPut, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - &entity) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) - - actual := make(map[string]interface{}) - err = json.NewDecoder(resp.Body).Decode(&actual) - assert.NoError(t, err) - - assert.Equal(t, expectedCanonicalisedAnnotationsBodyWriteWithPublication, actual) - assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annotationsAPI.AssertExpectations(t) -} - -func TestSaveAnnotationsInvalidContentUUID(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - oldHash := randomdata.RandStringRunes(56) - rw := new(RWMock) - aug := new(AugmenterMock) - annotationsAPI := new(AnnotationsAPIMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annotationsAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) - - req := httptest.NewRequest( - http.MethodPut, - "http://api.ft.com/draft-annotations/content/not-a-valid-uuid/annotations", - strings.NewReader(expectedAnnotationsBody)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, fmt.Sprintf(`{"message":"Invalid content UUID: invalid UUID length: %d"}`, len("not-a-valid-uuid")), string(body)) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annotationsAPI.AssertExpectations(t) -} - -func TestSaveAnnotationsInvalidAnnotationsBody(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - oldHash := randomdata.RandStringRunes(56) - rw := new(RWMock) - aug := new(AugmenterMock) - annotationsAPI := new(AnnotationsAPIMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annotationsAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) - - req := httptest.NewRequest( - http.MethodPut, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - strings.NewReader(`{invalid-json}`)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, `{"message":"Unable to unmarshal annotations body: invalid character 'i' looking for beginning of object key string"}`, string(body)) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annotationsAPI.AssertExpectations(t) -} - -func TestSaveAnnotationsErrorFromRW(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - oldHash := randomdata.RandStringRunes(56) - rw := new(RWMock) - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return("", errors.New("computer says no")) - - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - annotationsAPI := new(AnnotationsAPIMock) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) - return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) - - entity := bytes.Buffer{} - err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) - if err != nil { - t.Fatalf("failed to encode annotations: %v", err) - } - - req := httptest.NewRequest( - http.MethodPut, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - &entity) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, `{"message":"Error writing draft annotations: computer says no"}`, string(body)) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annotationsAPI.AssertExpectations(t) -} - -func TestAnnotationsReadTimeoutGenericRW(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - rw.On("Read", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(nil, "", false, &url.Error{Err: context.DeadlineExceeded}) - - aug := new(AugmenterMock) - annAPI := new(AnnotationsAPIMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) - - req := httptest.NewRequest(http.MethodGet, "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", nil) - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) - assert.JSONEq(t, `{"message":"Timeout while reading annotations"}`, w.Body.String()) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annAPI.AssertExpectations(t) -} - -func TestAnnotationsReadTimeoutUPP(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - rw.On("Read", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(nil, "", false, nil) - - aug := new(AugmenterMock) - annAPI := new(AnnotationsAPIMock) - annAPI.On("GetAll", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return([]interface{}{}, &url.Error{Err: context.DeadlineExceeded}) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) - - req := httptest.NewRequest(http.MethodGet, "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", nil) - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) - assert.JSONEq(t, `{"message":"Timeout while reading annotations"}`, w.Body.String()) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annAPI.AssertExpectations(t) -} - -func TestIsTimeoutErr(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - r := mux.NewRouter() - r.HandleFunc("/", func(_ http.ResponseWriter, _ *http.Request) { - time.Sleep(500 * time.Millisecond) - }).Methods(http.MethodGet) - - s := httptest.NewServer(r) - - req, _ := http.NewRequest(http.MethodGet, s.URL+"/", nil) - ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) - defer cancel() - - resp, err := http.DefaultClient.Do(req.WithContext(ctx)) - if err == nil { - defer resp.Body.Close() - } - var e net.Error - assert.True(t, errors.As(err, &e)) - assert.True(t, e.Timeout()) -} - -func TestAnnotationsWriteTimeout(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - oldHash := randomdata.RandStringRunes(56) - rw := new(RWMock) - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return("", &url.Error{Err: context.DeadlineExceeded}) - - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - annotationsAPI := new(AnnotationsAPIMock) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) - return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) - - entity := bytes.Buffer{} - err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) - if err != nil { - t.Fatalf("failed to encode annotations: %v", err) - } - - req := httptest.NewRequest( - http.MethodPut, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - &entity) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - - w := httptest.NewRecorder() - r.ServeHTTP(w, req) - - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) - - body, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - assert.JSONEq(t, `{"message":"Timeout while waiting to write draft annotations"}`, string(body)) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annotationsAPI.AssertExpectations(t) -} - -func TestHappyDeleteAnnotations(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - oldHash := randomdata.RandStringRunes(56) - newHash := randomdata.RandStringRunes(56) - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", - expectedCanonicalisedAnnotationsAfterDelete, oldHash).Return(newHash, nil) - annAPI := new(AnnotationsAPIMock) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). - Return(expectedAnnotations["annotations"], nil) - - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsAfterDelete["annotations"], depletedAnnotations) - return augmentedAnnotationsAfterDelete["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) - - req := httptest.NewRequest( - http.MethodDelete, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", - nil) - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annAPI.AssertExpectations(t) -} - -func TestUnHappyDeleteAnnotationsMissingContentUUID(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) - - req := httptest.NewRequest( - http.MethodDelete, - "http://api.ft.com/draft-annotations/content/foo/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", - nil) - req.Header.Set(tidutils.TransactionIDHeader, testTID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyDeleteAnnotationsInvalidConceptUUID(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) - - req := httptest.NewRequest( - http.MethodDelete, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/bar", - nil) - req.Header.Set(tidutils.TransactionIDHeader, testTID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyDeleteAnnotationsWhenRetrievingAnnotationsFails(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). - Return([]interface{}{}, errors.New("sorry something failed")) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) - - req := httptest.NewRequest( - http.MethodDelete, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", - nil) - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -} - -func TestUnHappyDeleteAnnotationsWhenNoAnnotationsFound(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - - uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) - - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). - Return([]interface{}{}, uppErr) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/:uuid/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) - - req := httptest.NewRequest( - http.MethodDelete, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", - nil) - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusNotFound, resp.StatusCode) -} - -func TestUnHappyDeleteAnnotationsWhenWritingAnnotationsFails(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWrite, "").Return(mock.Anything, errors.New("sorry something failed")) - annAPI := new(AnnotationsAPIMock) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). - Return(expectedAnnotations["annotations"], nil) - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) - return expectedAnnotations["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) - - req := httptest.NewRequest( - http.MethodDelete, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", - nil) - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -} - -func TestHappyAddAnnotation(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - - oldHash := randomdata.RandStringRunes(56) - newHash := randomdata.RandStringRunes(56) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, oldHash).Return(newHash, nil) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsAfterAdditon["annotations"], depletedAnnotations) - return augmentedAnnotationsAfterAddition["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) - - rw.AssertExpectations(t) - annAPI.AssertExpectations(t) - aug.AssertExpectations(t) -} - -func TestHappyAddExistingAnnotation(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - - oldHash := randomdata.RandStringRunes(56) - newHash := randomdata.RandStringRunes(56) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBody, oldHash).Return(newHash, nil) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) - return expectedAnnotations["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annAPI.AssertExpectations(t) -} - -func TestHappyAddAnnotationWithExistingConceptIdDifferentPredicate(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - oldHash := randomdata.RandStringRunes(56) - newHash := randomdata.RandStringRunes(56) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsSameConceptID, oldHash).Return(newHash, nil) - annAPI := new(AnnotationsAPIMock) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsSameConceptID["annotations"], depletedAnnotations) - return augmentedAnnotationsSameConceptID["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/838b3fbe-efbc-3cfe-b5c0-d38c046492a4", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annAPI.AssertExpectations(t) -} - -func TestUnHappyAddAnnotationInvalidContentId(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/foo/annotations", - nil) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyAddAnnotationInvalidConceptIdPrefix(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/about", - "id": "http://www.ft.com/thing//838b3fbe-efbc-3cfe-b5c0-d38c046492a4", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyAddAnnotationEmptyConceptId(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/about", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyAddAnnotationInvalidConceptUuid(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/about", - "id": "http://www.ft.com/thing//838b3fbe", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyAddAnnotationInvalidPredicate(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/foobar", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnhappyAddAnnotationWhenWritingAnnotationsFails(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, errors.New("error writing annotations")) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsAfterAdditon["annotations"], depletedAnnotations) - return augmentedAnnotationsAfterAddition["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -} - -func TestUnhappyAddAnnotationWhenGettingAnnotationsFails(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], errors.New("error getting annotations")) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) - r := mux.NewRouter() - - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -} - -func TestUnhappyAddAnnotationWhenNoAnnotationsFound(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], uppErr) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) - r := mux.NewRouter() - - r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPost, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusNotFound, resp.StatusCode) -} - -func TestHappyReplaceAnnotation(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - - oldHash := randomdata.RandStringRunes(56) - newHash := randomdata.RandStringRunes(56) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterReplace, oldHash).Return(newHash, nil) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsAfterReplace["annotations"], depletedAnnotations) - return augmentedAnnotationsAfterReplace["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPatch, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -} - -func TestHappyReplaceAnnotationWithPredicate(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - - oldHash := randomdata.RandStringRunes(56) - newHash := randomdata.RandStringRunes(56) - - const contentID = "83a201c6-60cd-11e7-91a7-502f7ee26895" - fromAnnotationAPI := []interface{}{ - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - "apiUrl": "http://api.ft.com/people/0a619d71-9af5-3755-90dd-f789b686c67a", - "type": "http://www.ft.com/ontology/person/Person", - "prefLabel": "Barack H. Obama", - }, - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/about", - "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", - "apiUrl": "http://api.ft.com/concepts/9577c6d4-b09e-4552-b88f-e52745abe02b", - "type": "http://www.ft.com/ontology/Topic", - "prefLabel": "US interest rates", - }, - } - augmentedAfterReplace := []interface{}{ - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - "apiUrl": "http://api.ft.com/people/0a619d71-9af5-3755-90dd-f789b686c67a", - "type": "http://www.ft.com/ontology/person/Person", - "prefLabel": "Barack H. Obama", - }, - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/hasBrand", - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - "apiUrl": "http://api.ft.com/concepts/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - "type": "http://www.ft.com/ontology/product/Brand", - "prefLabel": "Random Brand", - }, - } - afterReplace := []interface{}{ - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/hasBrand", - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - }, - } - - rw.On("Write", mock.Anything, contentID, map[string]interface{}{"annotations": afterReplace, "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}}, oldHash).Return(newHash, nil) - annAPI.On("GetAllButV2", mock.Anything, contentID).Return(fromAnnotationAPI, nil) - - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, afterReplace, depletedAnnotations) - return augmentedAfterReplace, nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - "predicate": "http://www.ft.com/ontology/hasBrand", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPatch, - "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -} - -func TestHappyReplaceExistingAnnotation(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - - oldHash := randomdata.RandStringRunes(56) - newHash := randomdata.RandStringRunes(56) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedAnnotationsReplaceExisting, oldHash).Return(newHash, nil) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotationsReplace["annotations"], nil) - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedAnnotationsReplaceExisting["annotations"], depletedAnnotations) - return expectedAnnotationsReplace["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - "predicate": "http://www.ft.com/ontology/annotation/mentions", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPatch, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/0a619d71-9af5-3755-90dd-f789b686c67a", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) - assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) - - rw.AssertExpectations(t) - aug.AssertExpectations(t) - annAPI.AssertExpectations(t) -} - -func TestUnHappyReplaceAnnotationsInvalidContentUUID(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - req := httptest.NewRequest( - http.MethodPatch, - "http://api.ft.com/draft-annotations/content/foo/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", - nil) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyReplaceAnnotationInvalidConceptIdInURI(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - ann := map[string]interface{}{ - "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPatch, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/bar", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyReplaceAnnotationEmptyBody(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - req := httptest.NewRequest( - http.MethodPatch, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", - nil) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyReplaceAnnotationInvalidConceptIdInBody(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "id": "foobar", - }, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPatch, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnHappyReplaceAnnotationInvalidPredicate(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, nil, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", - "predicate": "foo", - }, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPatch, - "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -} - -func TestUnhappyReplaceAnnotationWhenWritingAnnotationsFails(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterReplace, "").Return(mock.Anything, errors.New("error writing annotations")) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsAfterReplace["annotations"], depletedAnnotations) - return augmentedAnnotationsAfterReplace["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPatch, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -} - -func TestUnhappyReplaceAnnotationWhenGettingAnnotationsFails(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], errors.New("error getting annotations")) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - "predicate": "http://www.ft.com/ontology/annotation/about", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPatch, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -} - -func TestUnhappyReplaceAnnotationWhenNoAnnotationsFound(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) - - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], uppErr) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) - - ann := map[string]interface{}{ - "annotation": map[string]interface{}{ - "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", - "predicate": "http://www.ft.com/ontology/annotation/about", - }, - "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - } - b, _ := json.Marshal(ann) - - req := httptest.NewRequest( - http.MethodPatch, - "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", - bytes.NewBuffer(b)) - - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusNotFound, resp.StatusCode) -} - -func TestUnHappyDeleteAnnotationsWhenAuthorizationFails(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - rw := new(RWMock) - rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWrite, "").Return(mock.Anything, errors.New("sorry something failed")) - annAPI := new(AnnotationsAPIMock) - annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). - Return(expectedAnnotations["annotations"], nil) - canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) - - aug := &AugmenterMock{ - augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { - depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) - assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) - return expectedAnnotations["annotations"].([]interface{}), nil - }, - } - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, canonicalizer, aug, v, time.Second, log) - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) - - req := httptest.NewRequest( - http.MethodDelete, - "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/0a619d71-9af5-3755-90dd-f789b686c67a", - nil) - req.Header.Set(tidutils.TransactionIDHeader, testTID) - req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) - req.Header.Set("X-Policy", "PBLC_WRITE_8e6c705e-1132-42a2-8db0-c295e29e8658") - req.Header.Set("Access-From", "API Gateway") - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, http.StatusForbidden, resp.StatusCode) -} - -func TestValidate(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json;draft-annotations-ftpc-add.json;draft-annotations-ftpc-write.json") - - tests := []struct { - name string - requestBody map[string]interface{} - header string - expectedStatusCode int - }{ - { - "Valid PAC annotations write request", - map[string]interface{}{ - "annotations": []interface{}{ - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - }, - "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - }, - "draft-annotations-pac-write.json", - 200, - }, - { - "Valid SV annotations write request", - map[string]interface{}{ - "annotations": []interface{}{ - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/about", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - }, - "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - }, - "draft-annotations-sv-write.json", - 200, - }, - { - "Valid FTPC annotations write request", - map[string]interface{}{ - "annotations": []interface{}{ - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/hasSource", - "id": "http://api.ft.com/things/1541d7d1-6e2f-44ba-927a-f9b002d23715", - }, - }, - "publication": []string{"724b5e36-6d45-4cf1-b1c2-3f676b21f21b"}, - }, - "draft-annotations-ftpc-write.json", - 200, - }, - { - "PAC annotations write request with missing publication array", - map[string]interface{}{ - "annotations": []interface{}{ - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - }, - }, - "draft-annotations-pac-write.json", - 400, - }, - { - "SV annotations write request with missing publication array", - map[string]interface{}{ - "annotations": []interface{}{ - map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/about", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - }, - }, - "draft-annotations-sv-write.json", - 400, - }, - { - "Valid PAC annotations add request", - map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - }, - "draft-annotations-pac-add.json", - 200, - }, - { - "Valid SV annotations add request", - map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/about", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, - }, - "draft-annotations-sv-add.json", - 200, - }, - { - "Valid FTPC annotations add request", - map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/hasSource", - "id": "http://api.ft.com/things/1541d7d1-6e2f-44ba-927a-f9b002d23715", - }, - "publication": []string{"724b5e36-6d45-4cf1-b1c2-3f676b21f21b"}, - }, - "draft-annotations-ftpc-add.json", - 200, - }, - { - "PAC annotations add request with missing publication", - map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/mentions", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - }, - "draft-annotations-pac-add.json", - 400, - }, - { - "SV annotations add request with missing publication", - map[string]interface{}{ - "annotation": map[string]interface{}{ - "predicate": "http://www.ft.com/ontology/annotation/about", - "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", - }, - }, - "draft-annotations-sv-add.json", - 400, - }, - } - - for _, tt := range tests { - rw := new(RWMock) - annAPI := new(AnnotationsAPIMock) - aug := new(AugmenterMock) - - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - v := validator.NewSchemaValidator(log).GetJSONValidator() - h := handler.New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) - - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/validate", h.Validate).Methods(http.MethodPost) - - b, err := json.Marshal(tt.requestBody) - require.NoError(t, err) - - req := httptest.NewRequest( - http.MethodPost, - "/draft-annotations/validate", - bytes.NewBuffer(b)) - req.Header.Set(handler.SchemaNameHeader, tt.header) - - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - assert.Equal(t, tt.expectedStatusCode, resp.StatusCode) - } -} - -func TestListSchemas(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - - tests := []struct { - name string - expectedMessage string - }{ - { - "List schemas", - `{"_links":{"application/vnd.ft-upp-annotations-ftpc-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-ftpc-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-ftpc-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-write.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-write.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-write.json","name":"latest-version"}],"self":{"href":"/draft-annotations/schemas"}}}`, - }, - } - - for _, tt := range tests { - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - s := validator.NewSchemaValidator(log).GetSchemaHandler() - - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/schemas", s.ListSchemas).Methods(http.MethodGet) - - req := httptest.NewRequest( - http.MethodGet, - "/draft-annotations/schemas", - nil) - - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - assert.Equal(t, tt.expectedMessage, strings.TrimSpace(w.Body.String())) - } -} - -func TestGetSchemas(t *testing.T) { - _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") - _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") - _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") - - tests := []struct { - name string - schemaName string - expectedMessage string - }{ - { - "Get Draft PAC Annotations Write Schema", - "draft-annotations-pac-write.json", - `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-pac-write+json","title":"Draft PAC Annotations Write Endpoint","type":"object","description":"Schema for Draft PAC Annotations","properties":{"annotations":{"type":"array","description":"Draft PAC annotations","items":{"$ref":"#/$defs/annotation"}},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotations","publication"],"additionalProperties":false,"$defs":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/mentions","http://www.ft.com/ontology/classification/isClassifiedBy","http://www.ft.com/ontology/implicitlyClassifiedBy","http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/isPrimarilyClassifiedBy","http://www.ft.com/ontology/majorMentions","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/hasContributor","http://www.ft.com/ontology/hasDisplayTag","http://www.ft.com/ontology/hasBrand"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id","predicate"],"additionalProperties":false}}}`, - }, - { - "Get Draft SV Annotations Add Schema", - "draft-annotations-sv-add.json", - `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-sv-add+json","title":"Draft Sustainable Views Annotations Add Endpoint","type":"object","description":"Schema for Draft Sustainable Views Annotations","properties":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/annotation/hasReference"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id","predicate"],"additionalProperties":false},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotation","publication"],"additionalProperties":false}`, - }, - { - "Get Draft SV Annotations Replace Schema", - "draft-annotations-sv-replace.json", - `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-sv-replace+json","title":"Draft Sustainable Views Annotations Replace Endpoint","type":"object","description":"Schema for Draft Sustainable Views Annotations","properties":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/annotation/hasReference"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id"],"additionalProperties":false},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotation","publication"],"additionalProperties":false}`, - }, - } - - for _, tt := range tests { - log := logger.NewUPPLogger("draft-annotations-api", "INFO") - s := validator.NewSchemaValidator(log).GetSchemaHandler() - - r := mux.NewRouter() - r.HandleFunc("/draft-annotations/schemas/{schemaName}", s.GetSchema).Methods(http.MethodGet) - - req := httptest.NewRequest( - http.MethodGet, - "/draft-annotations/schemas/"+tt.schemaName, - nil) - - w := httptest.NewRecorder() - - r.ServeHTTP(w, req) - resp := w.Result() - defer resp.Body.Close() - - body := &bytes.Buffer{} - err := json.Compact(body, w.Body.Bytes()) - require.NoError(t, err) - - assert.Equal(t, tt.expectedMessage, strings.TrimSpace(body.String())) - } -} +//func TestSaveAnnotations(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// oldHash := randomdata.RandStringRunes(56) +// newHash := randomdata.RandStringRunes(56) +// rw := new(RWMock) +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return(newHash, nil) +// +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// annotationsAPI := new(AnnotationsAPIMock) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) +// return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) +// +// entity := bytes.Buffer{} +// err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) +// if err != nil { +// t.Fatalf("failed to encode annotations: %v", err) +// } +// +// req := httptest.NewRequest( +// http.MethodPut, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// &entity) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusOK, resp.StatusCode) +// +// actual := make(map[string]interface{}) +// err = json.NewDecoder(resp.Body).Decode(&actual) +// assert.NoError(t, err) +// +// assert.Equal(t, expectedCanonicalisedAnnotationsBodyWriteWithPublication, actual) +// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annotationsAPI.AssertExpectations(t) +//} +// +//func TestSaveAnnotationsInvalidContentUUID(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// oldHash := randomdata.RandStringRunes(56) +// rw := new(RWMock) +// aug := new(AugmenterMock) +// annotationsAPI := new(AnnotationsAPIMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annotationsAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) +// +// req := httptest.NewRequest( +// http.MethodPut, +// "http://api.ft.com/draft-annotations/content/not-a-valid-uuid/annotations", +// strings.NewReader(expectedAnnotationsBody)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +// body, err := io.ReadAll(resp.Body) +// assert.NoError(t, err) +// assert.JSONEq(t, fmt.Sprintf(`{"message":"Invalid content UUID: invalid UUID length: %d"}`, len("not-a-valid-uuid")), string(body)) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annotationsAPI.AssertExpectations(t) +//} +// +//func TestSaveAnnotationsInvalidAnnotationsBody(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// oldHash := randomdata.RandStringRunes(56) +// rw := new(RWMock) +// aug := new(AugmenterMock) +// annotationsAPI := new(AnnotationsAPIMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annotationsAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) +// +// req := httptest.NewRequest( +// http.MethodPut, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// strings.NewReader(`{invalid-json}`)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +// body, err := io.ReadAll(resp.Body) +// assert.NoError(t, err) +// assert.JSONEq(t, `{"message":"Unable to unmarshal annotations body: invalid character 'i' looking for beginning of object key string"}`, string(body)) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annotationsAPI.AssertExpectations(t) +//} +// +//func TestSaveAnnotationsErrorFromRW(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// oldHash := randomdata.RandStringRunes(56) +// rw := new(RWMock) +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return("", errors.New("computer says no")) +// +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// annotationsAPI := new(AnnotationsAPIMock) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) +// return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) +// +// entity := bytes.Buffer{} +// err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) +// if err != nil { +// t.Fatalf("failed to encode annotations: %v", err) +// } +// +// req := httptest.NewRequest( +// http.MethodPut, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// &entity) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +// body, err := io.ReadAll(resp.Body) +// assert.NoError(t, err) +// assert.JSONEq(t, `{"message":"Error writing draft annotations: computer says no"}`, string(body)) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annotationsAPI.AssertExpectations(t) +//} +// +//func TestAnnotationsReadTimeoutGenericRW(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// rw.On("Read", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(nil, "", false, &url.Error{Err: context.DeadlineExceeded}) +// +// aug := new(AugmenterMock) +// annAPI := new(AnnotationsAPIMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) +// +// req := httptest.NewRequest(http.MethodGet, "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", nil) +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) +// assert.JSONEq(t, `{"message":"Timeout while reading annotations"}`, w.Body.String()) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annAPI.AssertExpectations(t) +//} +// +//func TestAnnotationsReadTimeoutUPP(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// rw.On("Read", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(nil, "", false, nil) +// +// aug := new(AugmenterMock) +// annAPI := new(AnnotationsAPIMock) +// annAPI.On("GetAll", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return([]interface{}{}, &url.Error{Err: context.DeadlineExceeded}) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) +// +// req := httptest.NewRequest(http.MethodGet, "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", nil) +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) +// assert.JSONEq(t, `{"message":"Timeout while reading annotations"}`, w.Body.String()) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annAPI.AssertExpectations(t) +//} +// +//func TestIsTimeoutErr(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// r := mux.NewRouter() +// r.HandleFunc("/", func(_ http.ResponseWriter, _ *http.Request) { +// time.Sleep(500 * time.Millisecond) +// }).Methods(http.MethodGet) +// +// s := httptest.NewServer(r) +// +// req, _ := http.NewRequest(http.MethodGet, s.URL+"/", nil) +// ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) +// defer cancel() +// +// resp, err := http.DefaultClient.Do(req.WithContext(ctx)) +// if err == nil { +// defer resp.Body.Close() +// } +// var e net.Error +// assert.True(t, errors.As(err, &e)) +// assert.True(t, e.Timeout()) +//} +// +//func TestAnnotationsWriteTimeout(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// oldHash := randomdata.RandStringRunes(56) +// rw := new(RWMock) +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return("", &url.Error{Err: context.DeadlineExceeded}) +// +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// annotationsAPI := new(AnnotationsAPIMock) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) +// return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) +// +// entity := bytes.Buffer{} +// err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) +// if err != nil { +// t.Fatalf("failed to encode annotations: %v", err) +// } +// +// req := httptest.NewRequest( +// http.MethodPut, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// &entity) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// +// w := httptest.NewRecorder() +// r.ServeHTTP(w, req) +// +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) +// +// body, err := io.ReadAll(resp.Body) +// assert.NoError(t, err) +// assert.JSONEq(t, `{"message":"Timeout while waiting to write draft annotations"}`, string(body)) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annotationsAPI.AssertExpectations(t) +//} +// +//func TestHappyDeleteAnnotations(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// oldHash := randomdata.RandStringRunes(56) +// newHash := randomdata.RandStringRunes(56) +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", +// expectedCanonicalisedAnnotationsAfterDelete, oldHash).Return(newHash, nil) +// annAPI := new(AnnotationsAPIMock) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). +// Return(expectedAnnotations["annotations"], nil) +// +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsAfterDelete["annotations"], depletedAnnotations) +// return augmentedAnnotationsAfterDelete["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) +// +// req := httptest.NewRequest( +// http.MethodDelete, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", +// nil) +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusOK, resp.StatusCode) +// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annAPI.AssertExpectations(t) +//} +// +//func TestUnHappyDeleteAnnotationsMissingContentUUID(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) +// +// req := httptest.NewRequest( +// http.MethodDelete, +// "http://api.ft.com/draft-annotations/content/foo/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", +// nil) +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyDeleteAnnotationsInvalidConceptUUID(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) +// +// req := httptest.NewRequest( +// http.MethodDelete, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/bar", +// nil) +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyDeleteAnnotationsWhenRetrievingAnnotationsFails(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). +// Return([]interface{}{}, errors.New("sorry something failed")) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) +// +// req := httptest.NewRequest( +// http.MethodDelete, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", +// nil) +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +//} +// +//func TestUnHappyDeleteAnnotationsWhenNoAnnotationsFound(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// +// uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) +// +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). +// Return([]interface{}{}, uppErr) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/:uuid/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) +// +// req := httptest.NewRequest( +// http.MethodDelete, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", +// nil) +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusNotFound, resp.StatusCode) +//} +// +//func TestUnHappyDeleteAnnotationsWhenWritingAnnotationsFails(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWrite, "").Return(mock.Anything, errors.New("sorry something failed")) +// annAPI := new(AnnotationsAPIMock) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). +// Return(expectedAnnotations["annotations"], nil) +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) +// return expectedAnnotations["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) +// +// req := httptest.NewRequest( +// http.MethodDelete, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", +// nil) +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +//} +// +//func TestHappyAddAnnotation(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// +// oldHash := randomdata.RandStringRunes(56) +// newHash := randomdata.RandStringRunes(56) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, oldHash).Return(newHash, nil) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsAfterAdditon["annotations"], depletedAnnotations) +// return augmentedAnnotationsAfterAddition["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusOK, resp.StatusCode) +// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +// +// rw.AssertExpectations(t) +// annAPI.AssertExpectations(t) +// aug.AssertExpectations(t) +//} +// +//func TestHappyAddExistingAnnotation(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// +// oldHash := randomdata.RandStringRunes(56) +// newHash := randomdata.RandStringRunes(56) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBody, oldHash).Return(newHash, nil) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) +// return expectedAnnotations["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusOK, resp.StatusCode) +// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annAPI.AssertExpectations(t) +//} +// +//func TestHappyAddAnnotationWithExistingConceptIdDifferentPredicate(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// oldHash := randomdata.RandStringRunes(56) +// newHash := randomdata.RandStringRunes(56) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsSameConceptID, oldHash).Return(newHash, nil) +// annAPI := new(AnnotationsAPIMock) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsSameConceptID["annotations"], depletedAnnotations) +// return augmentedAnnotationsSameConceptID["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/838b3fbe-efbc-3cfe-b5c0-d38c046492a4", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusOK, resp.StatusCode) +// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annAPI.AssertExpectations(t) +//} +// +//func TestUnHappyAddAnnotationInvalidContentId(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/foo/annotations", +// nil) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyAddAnnotationInvalidConceptIdPrefix(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/about", +// "id": "http://www.ft.com/thing//838b3fbe-efbc-3cfe-b5c0-d38c046492a4", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyAddAnnotationEmptyConceptId(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/about", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyAddAnnotationInvalidConceptUuid(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/about", +// "id": "http://www.ft.com/thing//838b3fbe", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyAddAnnotationInvalidPredicate(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/foobar", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnhappyAddAnnotationWhenWritingAnnotationsFails(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, errors.New("error writing annotations")) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsAfterAdditon["annotations"], depletedAnnotations) +// return augmentedAnnotationsAfterAddition["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +//} +// +//func TestUnhappyAddAnnotationWhenGettingAnnotationsFails(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], errors.New("error getting annotations")) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) +// r := mux.NewRouter() +// +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +//} +// +//func TestUnhappyAddAnnotationWhenNoAnnotationsFound(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], uppErr) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) +// r := mux.NewRouter() +// +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusNotFound, resp.StatusCode) +//} +// +//func TestHappyReplaceAnnotation(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// +// oldHash := randomdata.RandStringRunes(56) +// newHash := randomdata.RandStringRunes(56) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterReplace, oldHash).Return(newHash, nil) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsAfterReplace["annotations"], depletedAnnotations) +// return augmentedAnnotationsAfterReplace["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusOK, resp.StatusCode) +// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +//} +// +//func TestHappyReplaceAnnotationWithPredicate(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// +// oldHash := randomdata.RandStringRunes(56) +// newHash := randomdata.RandStringRunes(56) +// +// const contentID = "83a201c6-60cd-11e7-91a7-502f7ee26895" +// fromAnnotationAPI := []interface{}{ +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// "apiUrl": "http://api.ft.com/people/0a619d71-9af5-3755-90dd-f789b686c67a", +// "type": "http://www.ft.com/ontology/person/Person", +// "prefLabel": "Barack H. Obama", +// }, +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/about", +// "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", +// "apiUrl": "http://api.ft.com/concepts/9577c6d4-b09e-4552-b88f-e52745abe02b", +// "type": "http://www.ft.com/ontology/Topic", +// "prefLabel": "US interest rates", +// }, +// } +// augmentedAfterReplace := []interface{}{ +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// "apiUrl": "http://api.ft.com/people/0a619d71-9af5-3755-90dd-f789b686c67a", +// "type": "http://www.ft.com/ontology/person/Person", +// "prefLabel": "Barack H. Obama", +// }, +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/hasBrand", +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// "apiUrl": "http://api.ft.com/concepts/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// "type": "http://www.ft.com/ontology/product/Brand", +// "prefLabel": "Random Brand", +// }, +// } +// afterReplace := []interface{}{ +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/hasBrand", +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// }, +// } +// +// rw.On("Write", mock.Anything, contentID, map[string]interface{}{"annotations": afterReplace, "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}}, oldHash).Return(newHash, nil) +// annAPI.On("GetAllButV2", mock.Anything, contentID).Return(fromAnnotationAPI, nil) +// +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, afterReplace, depletedAnnotations) +// return augmentedAfterReplace, nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// "predicate": "http://www.ft.com/ontology/hasBrand", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusOK, resp.StatusCode) +// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +//} +// +//func TestHappyReplaceExistingAnnotation(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// +// oldHash := randomdata.RandStringRunes(56) +// newHash := randomdata.RandStringRunes(56) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedAnnotationsReplaceExisting, oldHash).Return(newHash, nil) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotationsReplace["annotations"], nil) +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedAnnotationsReplaceExisting["annotations"], depletedAnnotations) +// return expectedAnnotationsReplace["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/0a619d71-9af5-3755-90dd-f789b686c67a", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusOK, resp.StatusCode) +// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +// +// rw.AssertExpectations(t) +// aug.AssertExpectations(t) +// annAPI.AssertExpectations(t) +//} +// +//func TestUnHappyReplaceAnnotationsInvalidContentUUID(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "http://api.ft.com/draft-annotations/content/foo/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", +// nil) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyReplaceAnnotationInvalidConceptIdInURI(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// ann := map[string]interface{}{ +// "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/bar", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyReplaceAnnotationEmptyBody(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", +// nil) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyReplaceAnnotationInvalidConceptIdInBody(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "id": "foobar", +// }, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnHappyReplaceAnnotationInvalidPredicate(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, nil, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", +// "predicate": "foo", +// }, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +//} +// +//func TestUnhappyReplaceAnnotationWhenWritingAnnotationsFails(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterReplace, "").Return(mock.Anything, errors.New("error writing annotations")) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsAfterReplace["annotations"], depletedAnnotations) +// return augmentedAnnotationsAfterReplace["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +//} +// +//func TestUnhappyReplaceAnnotationWhenGettingAnnotationsFails(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], errors.New("error getting annotations")) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// "predicate": "http://www.ft.com/ontology/annotation/about", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +//} +// +//func TestUnhappyReplaceAnnotationWhenNoAnnotationsFound(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) +// +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], uppErr) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) +// +// ann := map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", +// "predicate": "http://www.ft.com/ontology/annotation/about", +// }, +// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// } +// b, _ := json.Marshal(ann) +// +// req := httptest.NewRequest( +// http.MethodPatch, +// "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", +// bytes.NewBuffer(b)) +// +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusNotFound, resp.StatusCode) +//} +// +//func TestUnHappyDeleteAnnotationsWhenAuthorizationFails(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// rw := new(RWMock) +// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWrite, "").Return(mock.Anything, errors.New("sorry something failed")) +// annAPI := new(AnnotationsAPIMock) +// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). +// Return(expectedAnnotations["annotations"], nil) +// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) +// +// aug := &AugmenterMock{ +// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { +// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) +// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) +// return expectedAnnotations["annotations"].([]interface{}), nil +// }, +// } +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) +// +// req := httptest.NewRequest( +// http.MethodDelete, +// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/0a619d71-9af5-3755-90dd-f789b686c67a", +// nil) +// req.Header.Set(tidutils.TransactionIDHeader, testTID) +// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) +// req.Header.Set("X-Policy", "PBLC_WRITE_8e6c705e-1132-42a2-8db0-c295e29e8658") +// req.Header.Set("Access-From", "API Gateway") +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, http.StatusForbidden, resp.StatusCode) +//} +// +//func TestValidate(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json;draft-annotations-ftpc-add.json;draft-annotations-ftpc-write.json") +// +// tests := []struct { +// name string +// requestBody map[string]interface{} +// header string +// expectedStatusCode int +// }{ +// { +// "Valid PAC annotations write request", +// map[string]interface{}{ +// "annotations": []interface{}{ +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// }, +// "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// }, +// "draft-annotations-pac-write.json", +// 200, +// }, +// { +// "Valid SV annotations write request", +// map[string]interface{}{ +// "annotations": []interface{}{ +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/about", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// }, +// "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// }, +// "draft-annotations-sv-write.json", +// 200, +// }, +// { +// "Valid FTPC annotations write request", +// map[string]interface{}{ +// "annotations": []interface{}{ +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/hasSource", +// "id": "http://api.ft.com/things/1541d7d1-6e2f-44ba-927a-f9b002d23715", +// }, +// }, +// "publication": []string{"724b5e36-6d45-4cf1-b1c2-3f676b21f21b"}, +// }, +// "draft-annotations-ftpc-write.json", +// 200, +// }, +// { +// "PAC annotations write request with missing publication array", +// map[string]interface{}{ +// "annotations": []interface{}{ +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// }, +// }, +// "draft-annotations-pac-write.json", +// 400, +// }, +// { +// "SV annotations write request with missing publication array", +// map[string]interface{}{ +// "annotations": []interface{}{ +// map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/about", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// }, +// }, +// "draft-annotations-sv-write.json", +// 400, +// }, +// { +// "Valid PAC annotations add request", +// map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// }, +// "draft-annotations-pac-add.json", +// 200, +// }, +// { +// "Valid SV annotations add request", +// map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/about", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, +// }, +// "draft-annotations-sv-add.json", +// 200, +// }, +// { +// "Valid FTPC annotations add request", +// map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/hasSource", +// "id": "http://api.ft.com/things/1541d7d1-6e2f-44ba-927a-f9b002d23715", +// }, +// "publication": []string{"724b5e36-6d45-4cf1-b1c2-3f676b21f21b"}, +// }, +// "draft-annotations-ftpc-add.json", +// 200, +// }, +// { +// "PAC annotations add request with missing publication", +// map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/mentions", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// }, +// "draft-annotations-pac-add.json", +// 400, +// }, +// { +// "SV annotations add request with missing publication", +// map[string]interface{}{ +// "annotation": map[string]interface{}{ +// "predicate": "http://www.ft.com/ontology/annotation/about", +// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", +// }, +// }, +// "draft-annotations-sv-add.json", +// 400, +// }, +// } +// +// for _, tt := range tests { +// rw := new(RWMock) +// annAPI := new(AnnotationsAPIMock) +// aug := new(AugmenterMock) +// +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// v := validator.NewSchemaValidator(log).GetJSONValidator() +// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) +// +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/validate", h.Validate).Methods(http.MethodPost) +// +// b, err := json.Marshal(tt.requestBody) +// require.NoError(t, err) +// +// req := httptest.NewRequest( +// http.MethodPost, +// "/draft-annotations/validate", +// bytes.NewBuffer(b)) +// req.Header.Set(SchemaNameHeader, tt.header) +// +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// assert.Equal(t, tt.expectedStatusCode, resp.StatusCode) +// } +//} +// +//func TestListSchemas(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// +// tests := []struct { +// name string +// expectedMessage string +// }{ +// { +// "List schemas", +// `{"_links":{"application/vnd.ft-upp-annotations-ftpc-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-ftpc-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-ftpc-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-write.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-write.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-write.json","name":"latest-version"}],"self":{"href":"/draft-annotations/schemas"}}}`, +// }, +// } +// +// for _, tt := range tests { +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// s := validator.NewSchemaValidator(log).GetSchemaHandler() +// +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/schemas", s.ListSchemas).Methods(http.MethodGet) +// +// req := httptest.NewRequest( +// http.MethodGet, +// "/draft-annotations/schemas", +// nil) +// +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// assert.Equal(t, tt.expectedMessage, strings.TrimSpace(w.Body.String())) +// } +//} +// +//func TestGetSchemas(t *testing.T) { +// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") +// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") +// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") +// +// tests := []struct { +// name string +// schemaName string +// expectedMessage string +// }{ +// { +// "Get Draft PAC Annotations Write Schema", +// "draft-annotations-pac-write.json", +// `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-pac-write+json","title":"Draft PAC Annotations Write Endpoint","type":"object","description":"Schema for Draft PAC Annotations","properties":{"annotations":{"type":"array","description":"Draft PAC annotations","items":{"$ref":"#/$defs/annotation"}},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotations","publication"],"additionalProperties":false,"$defs":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/mentions","http://www.ft.com/ontology/classification/isClassifiedBy","http://www.ft.com/ontology/implicitlyClassifiedBy","http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/isPrimarilyClassifiedBy","http://www.ft.com/ontology/majorMentions","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/hasContributor","http://www.ft.com/ontology/hasDisplayTag","http://www.ft.com/ontology/hasBrand"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id","predicate"],"additionalProperties":false}}}`, +// }, +// { +// "Get Draft SV Annotations Add Schema", +// "draft-annotations-sv-add.json", +// `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-sv-add+json","title":"Draft Sustainable Views Annotations Add Endpoint","type":"object","description":"Schema for Draft Sustainable Views Annotations","properties":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/annotation/hasReference"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id","predicate"],"additionalProperties":false},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotation","publication"],"additionalProperties":false}`, +// }, +// { +// "Get Draft SV Annotations Replace Schema", +// "draft-annotations-sv-replace.json", +// `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-sv-replace+json","title":"Draft Sustainable Views Annotations Replace Endpoint","type":"object","description":"Schema for Draft Sustainable Views Annotations","properties":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/annotation/hasReference"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id"],"additionalProperties":false},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotation","publication"],"additionalProperties":false}`, +// }, +// } +// +// for _, tt := range tests { +// log := logger.NewUPPLogger("draft-annotations-api", "INFO") +// s := validator.NewSchemaValidator(log).GetSchemaHandler() +// +// r := mux.NewRouter() +// r.HandleFunc("/draft-annotations/schemas/{schemaName}", s.GetSchema).Methods(http.MethodGet) +// +// req := httptest.NewRequest( +// http.MethodGet, +// "/draft-annotations/schemas/"+tt.schemaName, +// nil) +// +// w := httptest.NewRecorder() +// +// r.ServeHTTP(w, req) +// resp := w.Result() +// defer resp.Body.Close() +// +// body := &bytes.Buffer{} +// err := json.Compact(body, w.Body.Bytes()) +// require.NoError(t, err) +// +// assert.Equal(t, tt.expectedMessage, strings.TrimSpace(body.String())) +// } +//} type AugmenterMock struct { mock.Mock From f6e79c8535faf683d145a41d47cc60c5084dac14 Mon Sep 17 00:00:00 2001 From: angel raynov Date: Tue, 30 Jul 2024 15:54:22 +0300 Subject: [PATCH 5/8] uncomment --- handler/handler_test.go | 3699 ++++++++++++++++++++------------------- 1 file changed, 1852 insertions(+), 1847 deletions(-) diff --git a/handler/handler_test.go b/handler/handler_test.go index 22b56f5..6616938 100644 --- a/handler/handler_test.go +++ b/handler/handler_test.go @@ -5,11 +5,15 @@ import ( "context" "encoding/json" "errors" + "fmt" "io" + "net" "net/http" "net/http/httptest" + "net/url" "os" "strconv" + "strings" "testing" "time" @@ -22,6 +26,7 @@ import ( "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" ) const ( @@ -1308,1853 +1313,1853 @@ var augmentedAnnotationsSameConceptID = map[string]interface{}{ }, } -//func TestSaveAnnotations(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// oldHash := randomdata.RandStringRunes(56) -// newHash := randomdata.RandStringRunes(56) -// rw := new(RWMock) -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return(newHash, nil) -// -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// annotationsAPI := new(AnnotationsAPIMock) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) -// return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) -// -// entity := bytes.Buffer{} -// err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) -// if err != nil { -// t.Fatalf("failed to encode annotations: %v", err) -// } -// -// req := httptest.NewRequest( -// http.MethodPut, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// &entity) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// -// actual := make(map[string]interface{}) -// err = json.NewDecoder(resp.Body).Decode(&actual) -// assert.NoError(t, err) -// -// assert.Equal(t, expectedCanonicalisedAnnotationsBodyWriteWithPublication, actual) -// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annotationsAPI.AssertExpectations(t) -//} -// -//func TestSaveAnnotationsInvalidContentUUID(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// oldHash := randomdata.RandStringRunes(56) -// rw := new(RWMock) -// aug := new(AugmenterMock) -// annotationsAPI := new(AnnotationsAPIMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annotationsAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) -// -// req := httptest.NewRequest( -// http.MethodPut, -// "http://api.ft.com/draft-annotations/content/not-a-valid-uuid/annotations", -// strings.NewReader(expectedAnnotationsBody)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -// body, err := io.ReadAll(resp.Body) -// assert.NoError(t, err) -// assert.JSONEq(t, fmt.Sprintf(`{"message":"Invalid content UUID: invalid UUID length: %d"}`, len("not-a-valid-uuid")), string(body)) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annotationsAPI.AssertExpectations(t) -//} -// -//func TestSaveAnnotationsInvalidAnnotationsBody(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// oldHash := randomdata.RandStringRunes(56) -// rw := new(RWMock) -// aug := new(AugmenterMock) -// annotationsAPI := new(AnnotationsAPIMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annotationsAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) -// -// req := httptest.NewRequest( -// http.MethodPut, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// strings.NewReader(`{invalid-json}`)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -// body, err := io.ReadAll(resp.Body) -// assert.NoError(t, err) -// assert.JSONEq(t, `{"message":"Unable to unmarshal annotations body: invalid character 'i' looking for beginning of object key string"}`, string(body)) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annotationsAPI.AssertExpectations(t) -//} -// -//func TestSaveAnnotationsErrorFromRW(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// oldHash := randomdata.RandStringRunes(56) -// rw := new(RWMock) -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return("", errors.New("computer says no")) -// -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// annotationsAPI := new(AnnotationsAPIMock) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) -// return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) -// -// entity := bytes.Buffer{} -// err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) -// if err != nil { -// t.Fatalf("failed to encode annotations: %v", err) -// } -// -// req := httptest.NewRequest( -// http.MethodPut, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// &entity) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -// body, err := io.ReadAll(resp.Body) -// assert.NoError(t, err) -// assert.JSONEq(t, `{"message":"Error writing draft annotations: computer says no"}`, string(body)) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annotationsAPI.AssertExpectations(t) -//} -// -//func TestAnnotationsReadTimeoutGenericRW(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// rw.On("Read", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(nil, "", false, &url.Error{Err: context.DeadlineExceeded}) -// -// aug := new(AugmenterMock) -// annAPI := new(AnnotationsAPIMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) -// -// req := httptest.NewRequest(http.MethodGet, "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", nil) -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) -// assert.JSONEq(t, `{"message":"Timeout while reading annotations"}`, w.Body.String()) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annAPI.AssertExpectations(t) -//} -// -//func TestAnnotationsReadTimeoutUPP(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// rw.On("Read", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(nil, "", false, nil) -// -// aug := new(AugmenterMock) -// annAPI := new(AnnotationsAPIMock) -// annAPI.On("GetAll", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return([]interface{}{}, &url.Error{Err: context.DeadlineExceeded}) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) -// -// req := httptest.NewRequest(http.MethodGet, "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", nil) -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) -// assert.JSONEq(t, `{"message":"Timeout while reading annotations"}`, w.Body.String()) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annAPI.AssertExpectations(t) -//} -// -//func TestIsTimeoutErr(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// r := mux.NewRouter() -// r.HandleFunc("/", func(_ http.ResponseWriter, _ *http.Request) { -// time.Sleep(500 * time.Millisecond) -// }).Methods(http.MethodGet) -// -// s := httptest.NewServer(r) -// -// req, _ := http.NewRequest(http.MethodGet, s.URL+"/", nil) -// ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) -// defer cancel() -// -// resp, err := http.DefaultClient.Do(req.WithContext(ctx)) -// if err == nil { -// defer resp.Body.Close() -// } -// var e net.Error -// assert.True(t, errors.As(err, &e)) -// assert.True(t, e.Timeout()) -//} -// -//func TestAnnotationsWriteTimeout(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// oldHash := randomdata.RandStringRunes(56) -// rw := new(RWMock) -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return("", &url.Error{Err: context.DeadlineExceeded}) -// -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// annotationsAPI := new(AnnotationsAPIMock) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) -// return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) -// -// entity := bytes.Buffer{} -// err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) -// if err != nil { -// t.Fatalf("failed to encode annotations: %v", err) -// } -// -// req := httptest.NewRequest( -// http.MethodPut, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// &entity) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// -// w := httptest.NewRecorder() -// r.ServeHTTP(w, req) -// -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) -// -// body, err := io.ReadAll(resp.Body) -// assert.NoError(t, err) -// assert.JSONEq(t, `{"message":"Timeout while waiting to write draft annotations"}`, string(body)) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annotationsAPI.AssertExpectations(t) -//} -// -//func TestHappyDeleteAnnotations(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// oldHash := randomdata.RandStringRunes(56) -// newHash := randomdata.RandStringRunes(56) -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", -// expectedCanonicalisedAnnotationsAfterDelete, oldHash).Return(newHash, nil) -// annAPI := new(AnnotationsAPIMock) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). -// Return(expectedAnnotations["annotations"], nil) -// -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsAfterDelete["annotations"], depletedAnnotations) -// return augmentedAnnotationsAfterDelete["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) -// -// req := httptest.NewRequest( -// http.MethodDelete, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", -// nil) -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annAPI.AssertExpectations(t) -//} -// -//func TestUnHappyDeleteAnnotationsMissingContentUUID(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) -// -// req := httptest.NewRequest( -// http.MethodDelete, -// "http://api.ft.com/draft-annotations/content/foo/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", -// nil) -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyDeleteAnnotationsInvalidConceptUUID(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) -// -// req := httptest.NewRequest( -// http.MethodDelete, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/bar", -// nil) -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyDeleteAnnotationsWhenRetrievingAnnotationsFails(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). -// Return([]interface{}{}, errors.New("sorry something failed")) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) -// -// req := httptest.NewRequest( -// http.MethodDelete, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", -// nil) -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -//} -// -//func TestUnHappyDeleteAnnotationsWhenNoAnnotationsFound(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// -// uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) -// -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). -// Return([]interface{}{}, uppErr) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/:uuid/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) -// -// req := httptest.NewRequest( -// http.MethodDelete, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", -// nil) -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusNotFound, resp.StatusCode) -//} -// -//func TestUnHappyDeleteAnnotationsWhenWritingAnnotationsFails(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWrite, "").Return(mock.Anything, errors.New("sorry something failed")) -// annAPI := new(AnnotationsAPIMock) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). -// Return(expectedAnnotations["annotations"], nil) -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) -// return expectedAnnotations["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) -// -// req := httptest.NewRequest( -// http.MethodDelete, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", -// nil) -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -//} -// -//func TestHappyAddAnnotation(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// -// oldHash := randomdata.RandStringRunes(56) -// newHash := randomdata.RandStringRunes(56) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, oldHash).Return(newHash, nil) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsAfterAdditon["annotations"], depletedAnnotations) -// return augmentedAnnotationsAfterAddition["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -// -// rw.AssertExpectations(t) -// annAPI.AssertExpectations(t) -// aug.AssertExpectations(t) -//} -// -//func TestHappyAddExistingAnnotation(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// -// oldHash := randomdata.RandStringRunes(56) -// newHash := randomdata.RandStringRunes(56) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBody, oldHash).Return(newHash, nil) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) -// return expectedAnnotations["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annAPI.AssertExpectations(t) -//} -// -//func TestHappyAddAnnotationWithExistingConceptIdDifferentPredicate(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// oldHash := randomdata.RandStringRunes(56) -// newHash := randomdata.RandStringRunes(56) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsSameConceptID, oldHash).Return(newHash, nil) -// annAPI := new(AnnotationsAPIMock) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsSameConceptID["annotations"], depletedAnnotations) -// return augmentedAnnotationsSameConceptID["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/838b3fbe-efbc-3cfe-b5c0-d38c046492a4", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annAPI.AssertExpectations(t) -//} -// -//func TestUnHappyAddAnnotationInvalidContentId(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/foo/annotations", -// nil) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyAddAnnotationInvalidConceptIdPrefix(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/about", -// "id": "http://www.ft.com/thing//838b3fbe-efbc-3cfe-b5c0-d38c046492a4", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyAddAnnotationEmptyConceptId(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/about", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyAddAnnotationInvalidConceptUuid(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/about", -// "id": "http://www.ft.com/thing//838b3fbe", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyAddAnnotationInvalidPredicate(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/foobar", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnhappyAddAnnotationWhenWritingAnnotationsFails(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, errors.New("error writing annotations")) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsAfterAdditon["annotations"], depletedAnnotations) -// return augmentedAnnotationsAfterAddition["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -//} -// -//func TestUnhappyAddAnnotationWhenGettingAnnotationsFails(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], errors.New("error getting annotations")) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) -// r := mux.NewRouter() -// -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -//} -// -//func TestUnhappyAddAnnotationWhenNoAnnotationsFound(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], uppErr) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) -// r := mux.NewRouter() -// -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusNotFound, resp.StatusCode) -//} -// -//func TestHappyReplaceAnnotation(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// -// oldHash := randomdata.RandStringRunes(56) -// newHash := randomdata.RandStringRunes(56) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterReplace, oldHash).Return(newHash, nil) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsAfterReplace["annotations"], depletedAnnotations) -// return augmentedAnnotationsAfterReplace["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -//} -// -//func TestHappyReplaceAnnotationWithPredicate(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// -// oldHash := randomdata.RandStringRunes(56) -// newHash := randomdata.RandStringRunes(56) -// -// const contentID = "83a201c6-60cd-11e7-91a7-502f7ee26895" -// fromAnnotationAPI := []interface{}{ -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// "apiUrl": "http://api.ft.com/people/0a619d71-9af5-3755-90dd-f789b686c67a", -// "type": "http://www.ft.com/ontology/person/Person", -// "prefLabel": "Barack H. Obama", -// }, -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/about", -// "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", -// "apiUrl": "http://api.ft.com/concepts/9577c6d4-b09e-4552-b88f-e52745abe02b", -// "type": "http://www.ft.com/ontology/Topic", -// "prefLabel": "US interest rates", -// }, -// } -// augmentedAfterReplace := []interface{}{ -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// "apiUrl": "http://api.ft.com/people/0a619d71-9af5-3755-90dd-f789b686c67a", -// "type": "http://www.ft.com/ontology/person/Person", -// "prefLabel": "Barack H. Obama", -// }, -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/hasBrand", -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// "apiUrl": "http://api.ft.com/concepts/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// "type": "http://www.ft.com/ontology/product/Brand", -// "prefLabel": "Random Brand", -// }, -// } -// afterReplace := []interface{}{ -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/hasBrand", -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// }, -// } -// -// rw.On("Write", mock.Anything, contentID, map[string]interface{}{"annotations": afterReplace, "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}}, oldHash).Return(newHash, nil) -// annAPI.On("GetAllButV2", mock.Anything, contentID).Return(fromAnnotationAPI, nil) -// -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, afterReplace, depletedAnnotations) -// return augmentedAfterReplace, nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// "predicate": "http://www.ft.com/ontology/hasBrand", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -//} -// -//func TestHappyReplaceExistingAnnotation(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// -// oldHash := randomdata.RandStringRunes(56) -// newHash := randomdata.RandStringRunes(56) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedAnnotationsReplaceExisting, oldHash).Return(newHash, nil) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotationsReplace["annotations"], nil) -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedAnnotationsReplaceExisting["annotations"], depletedAnnotations) -// return expectedAnnotationsReplace["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/0a619d71-9af5-3755-90dd-f789b686c67a", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusOK, resp.StatusCode) -// assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) -// -// rw.AssertExpectations(t) -// aug.AssertExpectations(t) -// annAPI.AssertExpectations(t) -//} -// -//func TestUnHappyReplaceAnnotationsInvalidContentUUID(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "http://api.ft.com/draft-annotations/content/foo/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", -// nil) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyReplaceAnnotationInvalidConceptIdInURI(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// ann := map[string]interface{}{ -// "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/bar", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyReplaceAnnotationEmptyBody(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", -// nil) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyReplaceAnnotationInvalidConceptIdInBody(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "id": "foobar", -// }, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnHappyReplaceAnnotationInvalidPredicate(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, nil, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", -// "predicate": "foo", -// }, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusBadRequest, resp.StatusCode) -//} -// -//func TestUnhappyReplaceAnnotationWhenWritingAnnotationsFails(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterReplace, "").Return(mock.Anything, errors.New("error writing annotations")) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsAfterReplace["annotations"], depletedAnnotations) -// return augmentedAnnotationsAfterReplace["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -//} -// -//func TestUnhappyReplaceAnnotationWhenGettingAnnotationsFails(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], errors.New("error getting annotations")) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// "predicate": "http://www.ft.com/ontology/annotation/about", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) -//} -// -//func TestUnhappyReplaceAnnotationWhenNoAnnotationsFound(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) -// -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], uppErr) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) -// -// ann := map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", -// "predicate": "http://www.ft.com/ontology/annotation/about", -// }, -// "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// } -// b, _ := json.Marshal(ann) -// -// req := httptest.NewRequest( -// http.MethodPatch, -// "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", -// bytes.NewBuffer(b)) -// -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusNotFound, resp.StatusCode) -//} -// -//func TestUnHappyDeleteAnnotationsWhenAuthorizationFails(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// rw := new(RWMock) -// rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWrite, "").Return(mock.Anything, errors.New("sorry something failed")) -// annAPI := new(AnnotationsAPIMock) -// annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). -// Return(expectedAnnotations["annotations"], nil) -// canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) -// -// aug := &AugmenterMock{ -// augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { -// depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) -// assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) -// return expectedAnnotations["annotations"].([]interface{}), nil -// }, -// } -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) -// -// req := httptest.NewRequest( -// http.MethodDelete, -// "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/0a619d71-9af5-3755-90dd-f789b686c67a", -// nil) -// req.Header.Set(tidutils.TransactionIDHeader, testTID) -// req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) -// req.Header.Set("X-Policy", "PBLC_WRITE_8e6c705e-1132-42a2-8db0-c295e29e8658") -// req.Header.Set("Access-From", "API Gateway") -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, http.StatusForbidden, resp.StatusCode) -//} -// -//func TestValidate(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json;draft-annotations-ftpc-add.json;draft-annotations-ftpc-write.json") -// -// tests := []struct { -// name string -// requestBody map[string]interface{} -// header string -// expectedStatusCode int -// }{ -// { -// "Valid PAC annotations write request", -// map[string]interface{}{ -// "annotations": []interface{}{ -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// }, -// "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// }, -// "draft-annotations-pac-write.json", -// 200, -// }, -// { -// "Valid SV annotations write request", -// map[string]interface{}{ -// "annotations": []interface{}{ -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/about", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// }, -// "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// }, -// "draft-annotations-sv-write.json", -// 200, -// }, -// { -// "Valid FTPC annotations write request", -// map[string]interface{}{ -// "annotations": []interface{}{ -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/hasSource", -// "id": "http://api.ft.com/things/1541d7d1-6e2f-44ba-927a-f9b002d23715", -// }, -// }, -// "publication": []string{"724b5e36-6d45-4cf1-b1c2-3f676b21f21b"}, -// }, -// "draft-annotations-ftpc-write.json", -// 200, -// }, -// { -// "PAC annotations write request with missing publication array", -// map[string]interface{}{ -// "annotations": []interface{}{ -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// }, -// }, -// "draft-annotations-pac-write.json", -// 400, -// }, -// { -// "SV annotations write request with missing publication array", -// map[string]interface{}{ -// "annotations": []interface{}{ -// map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/about", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// }, -// }, -// "draft-annotations-sv-write.json", -// 400, -// }, -// { -// "Valid PAC annotations add request", -// map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// }, -// "draft-annotations-pac-add.json", -// 200, -// }, -// { -// "Valid SV annotations add request", -// map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/about", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, -// }, -// "draft-annotations-sv-add.json", -// 200, -// }, -// { -// "Valid FTPC annotations add request", -// map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/hasSource", -// "id": "http://api.ft.com/things/1541d7d1-6e2f-44ba-927a-f9b002d23715", -// }, -// "publication": []string{"724b5e36-6d45-4cf1-b1c2-3f676b21f21b"}, -// }, -// "draft-annotations-ftpc-add.json", -// 200, -// }, -// { -// "PAC annotations add request with missing publication", -// map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/mentions", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// }, -// "draft-annotations-pac-add.json", -// 400, -// }, -// { -// "SV annotations add request with missing publication", -// map[string]interface{}{ -// "annotation": map[string]interface{}{ -// "predicate": "http://www.ft.com/ontology/annotation/about", -// "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", -// }, -// }, -// "draft-annotations-sv-add.json", -// 400, -// }, -// } -// -// for _, tt := range tests { -// rw := new(RWMock) -// annAPI := new(AnnotationsAPIMock) -// aug := new(AugmenterMock) -// -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// v := validator.NewSchemaValidator(log).GetJSONValidator() -// h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) -// -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/validate", h.Validate).Methods(http.MethodPost) -// -// b, err := json.Marshal(tt.requestBody) -// require.NoError(t, err) -// -// req := httptest.NewRequest( -// http.MethodPost, -// "/draft-annotations/validate", -// bytes.NewBuffer(b)) -// req.Header.Set(SchemaNameHeader, tt.header) -// -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// assert.Equal(t, tt.expectedStatusCode, resp.StatusCode) -// } -//} -// -//func TestListSchemas(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// -// tests := []struct { -// name string -// expectedMessage string -// }{ -// { -// "List schemas", -// `{"_links":{"application/vnd.ft-upp-annotations-ftpc-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-ftpc-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-ftpc-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-write.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-write.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-write.json","name":"latest-version"}],"self":{"href":"/draft-annotations/schemas"}}}`, -// }, -// } -// -// for _, tt := range tests { -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// s := validator.NewSchemaValidator(log).GetSchemaHandler() -// -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/schemas", s.ListSchemas).Methods(http.MethodGet) -// -// req := httptest.NewRequest( -// http.MethodGet, -// "/draft-annotations/schemas", -// nil) -// -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// assert.Equal(t, tt.expectedMessage, strings.TrimSpace(w.Body.String())) -// } -//} -// -//func TestGetSchemas(t *testing.T) { -// _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") -// _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") -// _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") -// -// tests := []struct { -// name string -// schemaName string -// expectedMessage string -// }{ -// { -// "Get Draft PAC Annotations Write Schema", -// "draft-annotations-pac-write.json", -// `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-pac-write+json","title":"Draft PAC Annotations Write Endpoint","type":"object","description":"Schema for Draft PAC Annotations","properties":{"annotations":{"type":"array","description":"Draft PAC annotations","items":{"$ref":"#/$defs/annotation"}},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotations","publication"],"additionalProperties":false,"$defs":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/mentions","http://www.ft.com/ontology/classification/isClassifiedBy","http://www.ft.com/ontology/implicitlyClassifiedBy","http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/isPrimarilyClassifiedBy","http://www.ft.com/ontology/majorMentions","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/hasContributor","http://www.ft.com/ontology/hasDisplayTag","http://www.ft.com/ontology/hasBrand"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id","predicate"],"additionalProperties":false}}}`, -// }, -// { -// "Get Draft SV Annotations Add Schema", -// "draft-annotations-sv-add.json", -// `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-sv-add+json","title":"Draft Sustainable Views Annotations Add Endpoint","type":"object","description":"Schema for Draft Sustainable Views Annotations","properties":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/annotation/hasReference"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id","predicate"],"additionalProperties":false},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotation","publication"],"additionalProperties":false}`, -// }, -// { -// "Get Draft SV Annotations Replace Schema", -// "draft-annotations-sv-replace.json", -// `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-sv-replace+json","title":"Draft Sustainable Views Annotations Replace Endpoint","type":"object","description":"Schema for Draft Sustainable Views Annotations","properties":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/annotation/hasReference"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id"],"additionalProperties":false},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotation","publication"],"additionalProperties":false}`, -// }, -// } -// -// for _, tt := range tests { -// log := logger.NewUPPLogger("draft-annotations-api", "INFO") -// s := validator.NewSchemaValidator(log).GetSchemaHandler() -// -// r := mux.NewRouter() -// r.HandleFunc("/draft-annotations/schemas/{schemaName}", s.GetSchema).Methods(http.MethodGet) -// -// req := httptest.NewRequest( -// http.MethodGet, -// "/draft-annotations/schemas/"+tt.schemaName, -// nil) -// -// w := httptest.NewRecorder() -// -// r.ServeHTTP(w, req) -// resp := w.Result() -// defer resp.Body.Close() -// -// body := &bytes.Buffer{} -// err := json.Compact(body, w.Body.Bytes()) -// require.NoError(t, err) -// -// assert.Equal(t, tt.expectedMessage, strings.TrimSpace(body.String())) -// } -//} +func TestSaveAnnotations(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + oldHash := randomdata.RandStringRunes(56) + newHash := randomdata.RandStringRunes(56) + rw := new(RWMock) + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return(newHash, nil) + + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + annotationsAPI := new(AnnotationsAPIMock) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) + return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) + + entity := bytes.Buffer{} + err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) + if err != nil { + t.Fatalf("failed to encode annotations: %v", err) + } + + req := httptest.NewRequest( + http.MethodPut, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + &entity) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + + actual := make(map[string]interface{}) + err = json.NewDecoder(resp.Body).Decode(&actual) + assert.NoError(t, err) + + assert.Equal(t, expectedCanonicalisedAnnotationsBodyWriteWithPublication, actual) + assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annotationsAPI.AssertExpectations(t) +} + +func TestSaveAnnotationsInvalidContentUUID(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + oldHash := randomdata.RandStringRunes(56) + rw := new(RWMock) + aug := new(AugmenterMock) + annotationsAPI := new(AnnotationsAPIMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annotationsAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) + + req := httptest.NewRequest( + http.MethodPut, + "http://api.ft.com/draft-annotations/content/not-a-valid-uuid/annotations", + strings.NewReader(expectedAnnotationsBody)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + assert.NoError(t, err) + assert.JSONEq(t, fmt.Sprintf(`{"message":"Invalid content UUID: invalid UUID length: %d"}`, len("not-a-valid-uuid")), string(body)) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annotationsAPI.AssertExpectations(t) +} + +func TestSaveAnnotationsInvalidAnnotationsBody(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + oldHash := randomdata.RandStringRunes(56) + rw := new(RWMock) + aug := new(AugmenterMock) + annotationsAPI := new(AnnotationsAPIMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annotationsAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) + + req := httptest.NewRequest( + http.MethodPut, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + strings.NewReader(`{invalid-json}`)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + assert.NoError(t, err) + assert.JSONEq(t, `{"message":"Unable to unmarshal annotations body: invalid character 'i' looking for beginning of object key string"}`, string(body)) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annotationsAPI.AssertExpectations(t) +} + +func TestSaveAnnotationsErrorFromRW(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + oldHash := randomdata.RandStringRunes(56) + rw := new(RWMock) + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return("", errors.New("computer says no")) + + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + annotationsAPI := new(AnnotationsAPIMock) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) + return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) + + entity := bytes.Buffer{} + err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) + if err != nil { + t.Fatalf("failed to encode annotations: %v", err) + } + + req := httptest.NewRequest( + http.MethodPut, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + &entity) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + assert.NoError(t, err) + assert.JSONEq(t, `{"message":"Error writing draft annotations: computer says no"}`, string(body)) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annotationsAPI.AssertExpectations(t) +} + +func TestAnnotationsReadTimeoutGenericRW(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + rw.On("Read", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(nil, "", false, &url.Error{Err: context.DeadlineExceeded}) + + aug := new(AugmenterMock) + annAPI := new(AnnotationsAPIMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) + + req := httptest.NewRequest(http.MethodGet, "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) + assert.JSONEq(t, `{"message":"Timeout while reading annotations"}`, w.Body.String()) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annAPI.AssertExpectations(t) +} + +func TestAnnotationsReadTimeoutUPP(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + rw.On("Read", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(nil, "", false, nil) + + aug := new(AugmenterMock) + annAPI := new(AnnotationsAPIMock) + annAPI.On("GetAll", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return([]interface{}{}, &url.Error{Err: context.DeadlineExceeded}) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.ReadAnnotations).Methods(http.MethodGet) + + req := httptest.NewRequest(http.MethodGet, "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) + assert.JSONEq(t, `{"message":"Timeout while reading annotations"}`, w.Body.String()) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annAPI.AssertExpectations(t) +} + +func TestIsTimeoutErr(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + r := mux.NewRouter() + r.HandleFunc("/", func(_ http.ResponseWriter, _ *http.Request) { + time.Sleep(500 * time.Millisecond) + }).Methods(http.MethodGet) + + s := httptest.NewServer(r) + + req, _ := http.NewRequest(http.MethodGet, s.URL+"/", nil) + ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + + resp, err := http.DefaultClient.Do(req.WithContext(ctx)) + if err == nil { + defer resp.Body.Close() + } + var e net.Error + assert.True(t, errors.As(err, &e)) + assert.True(t, e.Timeout()) +} + +func TestAnnotationsWriteTimeout(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + oldHash := randomdata.RandStringRunes(56) + rw := new(RWMock) + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWriteWithPublication, oldHash).Return("", &url.Error{Err: context.DeadlineExceeded}) + + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + annotationsAPI := new(AnnotationsAPIMock) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) + return expectedAnnotationsWithPublication["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annotationsAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.WriteAnnotations).Methods(http.MethodPut) + + entity := bytes.Buffer{} + err := json.NewEncoder(&entity).Encode(&expectedAnnotationsWithPublication) + if err != nil { + t.Fatalf("failed to encode annotations: %v", err) + } + + req := httptest.NewRequest( + http.MethodPut, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + &entity) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + + w := httptest.NewRecorder() + r.ServeHTTP(w, req) + + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode) + + body, err := io.ReadAll(resp.Body) + assert.NoError(t, err) + assert.JSONEq(t, `{"message":"Timeout while waiting to write draft annotations"}`, string(body)) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annotationsAPI.AssertExpectations(t) +} + +func TestHappyDeleteAnnotations(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + oldHash := randomdata.RandStringRunes(56) + newHash := randomdata.RandStringRunes(56) + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", + expectedCanonicalisedAnnotationsAfterDelete, oldHash).Return(newHash, nil) + annAPI := new(AnnotationsAPIMock) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). + Return(expectedAnnotations["annotations"], nil) + + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsAfterDelete["annotations"], depletedAnnotations) + return augmentedAnnotationsAfterDelete["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) + + req := httptest.NewRequest( + http.MethodDelete, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", + nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annAPI.AssertExpectations(t) +} + +func TestUnHappyDeleteAnnotationsMissingContentUUID(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) + + req := httptest.NewRequest( + http.MethodDelete, + "http://api.ft.com/draft-annotations/content/foo/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", + nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyDeleteAnnotationsInvalidConceptUUID(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) + + req := httptest.NewRequest( + http.MethodDelete, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/bar", + nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyDeleteAnnotationsWhenRetrievingAnnotationsFails(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). + Return([]interface{}{}, errors.New("sorry something failed")) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) + + req := httptest.NewRequest( + http.MethodDelete, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", + nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +} + +func TestUnHappyDeleteAnnotationsWhenNoAnnotationsFound(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + + uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) + + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). + Return([]interface{}{}, uppErr) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/:uuid/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) + + req := httptest.NewRequest( + http.MethodDelete, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", + nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusNotFound, resp.StatusCode) +} + +func TestUnHappyDeleteAnnotationsWhenWritingAnnotationsFails(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWrite, "").Return(mock.Anything, errors.New("sorry something failed")) + annAPI := new(AnnotationsAPIMock) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). + Return(expectedAnnotations["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) + return expectedAnnotations["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) + + req := httptest.NewRequest( + http.MethodDelete, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", + nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +} + +func TestHappyAddAnnotation(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + + oldHash := randomdata.RandStringRunes(56) + newHash := randomdata.RandStringRunes(56) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, oldHash).Return(newHash, nil) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsAfterAdditon["annotations"], depletedAnnotations) + return augmentedAnnotationsAfterAddition["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) + + rw.AssertExpectations(t) + annAPI.AssertExpectations(t) + aug.AssertExpectations(t) +} + +func TestHappyAddExistingAnnotation(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + + oldHash := randomdata.RandStringRunes(56) + newHash := randomdata.RandStringRunes(56) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBody, oldHash).Return(newHash, nil) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) + return expectedAnnotations["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annAPI.AssertExpectations(t) +} + +func TestHappyAddAnnotationWithExistingConceptIdDifferentPredicate(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + oldHash := randomdata.RandStringRunes(56) + newHash := randomdata.RandStringRunes(56) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsSameConceptID, oldHash).Return(newHash, nil) + annAPI := new(AnnotationsAPIMock) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsSameConceptID["annotations"], depletedAnnotations) + return augmentedAnnotationsSameConceptID["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/838b3fbe-efbc-3cfe-b5c0-d38c046492a4", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annAPI.AssertExpectations(t) +} + +func TestUnHappyAddAnnotationInvalidContentId(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/foo/annotations", + nil) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyAddAnnotationInvalidConceptIdPrefix(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/about", + "id": "http://www.ft.com/thing//838b3fbe-efbc-3cfe-b5c0-d38c046492a4", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyAddAnnotationEmptyConceptId(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/about", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyAddAnnotationInvalidConceptUuid(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/about", + "id": "http://www.ft.com/thing//838b3fbe", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyAddAnnotationInvalidPredicate(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/foobar", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnhappyAddAnnotationWhenWritingAnnotationsFails(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, errors.New("error writing annotations")) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsAfterAdditon["annotations"], depletedAnnotations) + return augmentedAnnotationsAfterAddition["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +} + +func TestUnhappyAddAnnotationWhenGettingAnnotationsFails(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], errors.New("error getting annotations")) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) + r := mux.NewRouter() + + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +} + +func TestUnhappyAddAnnotationWhenNoAnnotationsFound(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], uppErr) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) + r := mux.NewRouter() + + r.HandleFunc("/draft-annotations/content/{uuid}/annotations", h.AddAnnotation).Methods(http.MethodPost) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPost, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusNotFound, resp.StatusCode) +} + +func TestHappyReplaceAnnotation(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + + oldHash := randomdata.RandStringRunes(56) + newHash := randomdata.RandStringRunes(56) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterReplace, oldHash).Return(newHash, nil) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsAfterReplace["annotations"], depletedAnnotations) + return augmentedAnnotationsAfterReplace["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPatch, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +} + +func TestHappyReplaceAnnotationWithPredicate(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + + oldHash := randomdata.RandStringRunes(56) + newHash := randomdata.RandStringRunes(56) + + const contentID = "83a201c6-60cd-11e7-91a7-502f7ee26895" + fromAnnotationAPI := []interface{}{ + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + "apiUrl": "http://api.ft.com/people/0a619d71-9af5-3755-90dd-f789b686c67a", + "type": "http://www.ft.com/ontology/person/Person", + "prefLabel": "Barack H. Obama", + }, + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/about", + "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", + "apiUrl": "http://api.ft.com/concepts/9577c6d4-b09e-4552-b88f-e52745abe02b", + "type": "http://www.ft.com/ontology/Topic", + "prefLabel": "US interest rates", + }, + } + augmentedAfterReplace := []interface{}{ + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + "apiUrl": "http://api.ft.com/people/0a619d71-9af5-3755-90dd-f789b686c67a", + "type": "http://www.ft.com/ontology/person/Person", + "prefLabel": "Barack H. Obama", + }, + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/hasBrand", + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + "apiUrl": "http://api.ft.com/concepts/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + "type": "http://www.ft.com/ontology/product/Brand", + "prefLabel": "Random Brand", + }, + } + afterReplace := []interface{}{ + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/hasBrand", + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + }, + } + + rw.On("Write", mock.Anything, contentID, map[string]interface{}{"annotations": afterReplace, "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}}, oldHash).Return(newHash, nil) + annAPI.On("GetAllButV2", mock.Anything, contentID).Return(fromAnnotationAPI, nil) + + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, afterReplace, depletedAnnotations) + return augmentedAfterReplace, nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + "predicate": "http://www.ft.com/ontology/hasBrand", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPatch, + "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) +} + +func TestHappyReplaceExistingAnnotation(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + + oldHash := randomdata.RandStringRunes(56) + newHash := randomdata.RandStringRunes(56) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedAnnotationsReplaceExisting, oldHash).Return(newHash, nil) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotationsReplace["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedAnnotationsReplaceExisting["annotations"], depletedAnnotations) + return expectedAnnotationsReplace["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + "predicate": "http://www.ft.com/ontology/annotation/mentions", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPatch, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/0a619d71-9af5-3755-90dd-f789b686c67a", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.PreviousDocumentHashHeader, oldHash) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, newHash, resp.Header.Get(annotations.DocumentHashHeader)) + + rw.AssertExpectations(t) + aug.AssertExpectations(t) + annAPI.AssertExpectations(t) +} + +func TestUnHappyReplaceAnnotationsInvalidContentUUID(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + req := httptest.NewRequest( + http.MethodPatch, + "http://api.ft.com/draft-annotations/content/foo/annotations/eccb0da2-54f3-4f9f-bafa-fcec10e1758c", + nil) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyReplaceAnnotationInvalidConceptIdInURI(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + ann := map[string]interface{}{ + "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPatch, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/bar", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyReplaceAnnotationEmptyBody(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + req := httptest.NewRequest( + http.MethodPatch, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", + nil) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyReplaceAnnotationInvalidConceptIdInBody(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "id": "foobar", + }, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPatch, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnHappyReplaceAnnotationInvalidPredicate(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, nil, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "id": "http://www.ft.com/thing/9577c6d4-b09e-4552-b88f-e52745abe02b", + "predicate": "foo", + }, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPatch, + "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) +} + +func TestUnhappyReplaceAnnotationWhenWritingAnnotationsFails(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterReplace, "").Return(mock.Anything, errors.New("error writing annotations")) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsAfterReplace["annotations"], depletedAnnotations) + return augmentedAnnotationsAfterReplace["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPatch, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +} + +func TestUnhappyReplaceAnnotationWhenGettingAnnotationsFails(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], errors.New("error getting annotations")) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + "predicate": "http://www.ft.com/ontology/annotation/about", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPatch, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) +} + +func TestUnhappyReplaceAnnotationWhenNoAnnotationsFound(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + uppErr := annotations.NewUPPError(annotations.UPPNotFoundMsg, http.StatusNotFound, nil) + + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsAfterAdditon, "").Return(mock.Anything, nil) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895").Return(expectedAnnotations["annotations"], uppErr) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.ReplaceAnnotation).Methods(http.MethodPatch) + + ann := map[string]interface{}{ + "annotation": map[string]interface{}{ + "id": "http://www.ft.com/thing/100e3cc0-aecc-4458-8ebd-6b1fbc7345ed", + "predicate": "http://www.ft.com/ontology/annotation/about", + }, + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + } + b, _ := json.Marshal(ann) + + req := httptest.NewRequest( + http.MethodPatch, + "/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/9577c6d4-b09e-4552-b88f-e52745abe02b", + bytes.NewBuffer(b)) + + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusNotFound, resp.StatusCode) +} + +func TestUnHappyDeleteAnnotationsWhenAuthorizationFails(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + rw := new(RWMock) + rw.On("Write", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895", expectedCanonicalisedAnnotationsBodyWrite, "").Return(mock.Anything, errors.New("sorry something failed")) + annAPI := new(AnnotationsAPIMock) + annAPI.On("GetAllButV2", mock.Anything, "83a201c6-60cd-11e7-91a7-502f7ee26895"). + Return(expectedAnnotations["annotations"], nil) + canonicalizer := annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter) + + aug := &AugmenterMock{ + augment: func(_ context.Context, depletedAnnotations []interface{}) ([]interface{}, error) { + depletedAnnotations = canonicalizer.Canonicalize(depletedAnnotations) + assert.Equal(t, expectedCanonicalisedAnnotationsBody["annotations"], depletedAnnotations) + return expectedAnnotations["annotations"].([]interface{}), nil + }, + } + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, canonicalizer, aug, v, time.Second, log) + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/content/{uuid}/annotations/{cuuid}", h.DeleteAnnotation).Methods(http.MethodDelete) + + req := httptest.NewRequest( + http.MethodDelete, + "http://api.ft.com/draft-annotations/content/83a201c6-60cd-11e7-91a7-502f7ee26895/annotations/0a619d71-9af5-3755-90dd-f789b686c67a", + nil) + req.Header.Set(tidutils.TransactionIDHeader, testTID) + req.Header.Set(annotations.OriginSystemIDHeader, annotations.PACOriginSystemID) + req.Header.Set("X-Policy", "PBLC_WRITE_8e6c705e-1132-42a2-8db0-c295e29e8658") + req.Header.Set("Access-From", "API Gateway") + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusForbidden, resp.StatusCode) +} + +func TestValidate(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json;draft-annotations-ftpc-add.json;draft-annotations-ftpc-write.json") + + tests := []struct { + name string + requestBody map[string]interface{} + header string + expectedStatusCode int + }{ + { + "Valid PAC annotations write request", + map[string]interface{}{ + "annotations": []interface{}{ + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + }, + "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + }, + "draft-annotations-pac-write.json", + 200, + }, + { + "Valid SV annotations write request", + map[string]interface{}{ + "annotations": []interface{}{ + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/about", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + }, + "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + }, + "draft-annotations-sv-write.json", + 200, + }, + { + "Valid FTPC annotations write request", + map[string]interface{}{ + "annotations": []interface{}{ + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/hasSource", + "id": "http://api.ft.com/things/1541d7d1-6e2f-44ba-927a-f9b002d23715", + }, + }, + "publication": []string{"724b5e36-6d45-4cf1-b1c2-3f676b21f21b"}, + }, + "draft-annotations-ftpc-write.json", + 200, + }, + { + "PAC annotations write request with missing publication array", + map[string]interface{}{ + "annotations": []interface{}{ + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + }, + }, + "draft-annotations-pac-write.json", + 400, + }, + { + "SV annotations write request with missing publication array", + map[string]interface{}{ + "annotations": []interface{}{ + map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/about", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + }, + }, + "draft-annotations-sv-write.json", + 400, + }, + { + "Valid PAC annotations add request", + map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + }, + "draft-annotations-pac-add.json", + 200, + }, + { + "Valid SV annotations add request", + map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/about", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + "publication": []string{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + }, + "draft-annotations-sv-add.json", + 200, + }, + { + "Valid FTPC annotations add request", + map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/hasSource", + "id": "http://api.ft.com/things/1541d7d1-6e2f-44ba-927a-f9b002d23715", + }, + "publication": []string{"724b5e36-6d45-4cf1-b1c2-3f676b21f21b"}, + }, + "draft-annotations-ftpc-add.json", + 200, + }, + { + "PAC annotations add request with missing publication", + map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/mentions", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + }, + "draft-annotations-pac-add.json", + 400, + }, + { + "SV annotations add request with missing publication", + map[string]interface{}{ + "annotation": map[string]interface{}{ + "predicate": "http://www.ft.com/ontology/annotation/about", + "id": "http://www.ft.com/thing/0a619d71-9af5-3755-90dd-f789b686c67a", + }, + }, + "draft-annotations-sv-add.json", + 400, + }, + } + + for _, tt := range tests { + rw := new(RWMock) + annAPI := new(AnnotationsAPIMock) + aug := new(AugmenterMock) + + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + v := validator.NewSchemaValidator(log).GetJSONValidator() + h := New(rw, annAPI, annotations.NewCanonicalizer(annotations.NewCanonicalAnnotationSorter), aug, v, time.Second, log) + + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/validate", h.Validate).Methods(http.MethodPost) + + b, err := json.Marshal(tt.requestBody) + require.NoError(t, err) + + req := httptest.NewRequest( + http.MethodPost, + "/draft-annotations/validate", + bytes.NewBuffer(b)) + req.Header.Set(SchemaNameHeader, tt.header) + + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + assert.Equal(t, tt.expectedStatusCode, resp.StatusCode) + } +} + +func TestListSchemas(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + + tests := []struct { + name string + expectedMessage string + }{ + { + "List schemas", + `{"_links":{"application/vnd.ft-upp-annotations-ftpc-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-ftpc-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-ftpc-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-ftpc-write.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-pac-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-pac-write.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-add.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-add.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-replace.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-replace.json","name":"latest-version"}],"application/vnd.ft-upp-annotations-sv-write.json":[{"href":"/draft-annotations/schemas/draft-annotations-sv-write.json","name":"latest-version"}],"self":{"href":"/draft-annotations/schemas"}}}`, + }, + } + + for _, tt := range tests { + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + s := validator.NewSchemaValidator(log).GetSchemaHandler() + + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/schemas", s.ListSchemas).Methods(http.MethodGet) + + req := httptest.NewRequest( + http.MethodGet, + "/draft-annotations/schemas", + nil) + + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, tt.expectedMessage, strings.TrimSpace(w.Body.String())) + } +} + +func TestGetSchemas(t *testing.T) { + _ = os.Setenv("JSON_SCHEMAS_PATH", "../schemas") + _ = os.Setenv("JSON_SCHEMAS_API_CONFIG_PATH", "../config/schemas-api-config.json") + _ = os.Setenv("JSON_SCHEMA_NAME", "draft-annotations-pac-add.json;draft-annotations-pac-replace.json;draft-annotations-pac-write.json;draft-annotations-sv-add.json;draft-annotations-sv-replace.json;draft-annotations-sv-write.json") + + tests := []struct { + name string + schemaName string + expectedMessage string + }{ + { + "Get Draft PAC Annotations Write Schema", + "draft-annotations-pac-write.json", + `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-pac-write+json","title":"Draft PAC Annotations Write Endpoint","type":"object","description":"Schema for Draft PAC Annotations","properties":{"annotations":{"type":"array","description":"Draft PAC annotations","items":{"$ref":"#/$defs/annotation"}},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotations","publication"],"additionalProperties":false,"$defs":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/mentions","http://www.ft.com/ontology/classification/isClassifiedBy","http://www.ft.com/ontology/implicitlyClassifiedBy","http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/isPrimarilyClassifiedBy","http://www.ft.com/ontology/majorMentions","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/hasContributor","http://www.ft.com/ontology/hasDisplayTag","http://www.ft.com/ontology/hasBrand"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id","predicate"],"additionalProperties":false}}}`, + }, + { + "Get Draft SV Annotations Add Schema", + "draft-annotations-sv-add.json", + `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-sv-add+json","title":"Draft Sustainable Views Annotations Add Endpoint","type":"object","description":"Schema for Draft Sustainable Views Annotations","properties":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/annotation/hasReference"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id","predicate"],"additionalProperties":false},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotation","publication"],"additionalProperties":false}`, + }, + { + "Get Draft SV Annotations Replace Schema", + "draft-annotations-sv-replace.json", + `{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://upp-publishing-prod.ft.com/schema/draft-annotations-sv-replace+json","title":"Draft Sustainable Views Annotations Replace Endpoint","type":"object","description":"Schema for Draft Sustainable Views Annotations","properties":{"annotation":{"type":"object","properties":{"id":{"type":"string","pattern":".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$","description":"ID of the related concept"},"predicate":{"type":"string","description":"Predicate of the annotation","enum":["http://www.ft.com/ontology/annotation/about","http://www.ft.com/ontology/annotation/hasAuthor","http://www.ft.com/ontology/annotation/hasReference"]},"apiUrl":{"type":"string","description":"API URL of the related concept"},"type":{"type":"string","description":"Type of the related concept"},"prefLabel":{"type":"string","description":"PrefLabel of the related concept"},"isFTAuthor":{"type":"boolean","description":"Indicates whether the related concept is an FT author"}},"required":["id"],"additionalProperties":false},"publication":{"type":"array","description":"Indicates which titles are aware of this content","items":{"type":"string","pattern":"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"}}},"required":["annotation","publication"],"additionalProperties":false}`, + }, + } + + for _, tt := range tests { + log := logger.NewUPPLogger("draft-annotations-api", "INFO") + s := validator.NewSchemaValidator(log).GetSchemaHandler() + + r := mux.NewRouter() + r.HandleFunc("/draft-annotations/schemas/{schemaName}", s.GetSchema).Methods(http.MethodGet) + + req := httptest.NewRequest( + http.MethodGet, + "/draft-annotations/schemas/"+tt.schemaName, + nil) + + w := httptest.NewRecorder() + + r.ServeHTTP(w, req) + resp := w.Result() + defer resp.Body.Close() + + body := &bytes.Buffer{} + err := json.Compact(body, w.Body.Bytes()) + require.NoError(t, err) + + assert.Equal(t, tt.expectedMessage, strings.TrimSpace(body.String())) + } +} type AugmenterMock struct { mock.Mock From 6a087b54ba1869e0e8cfdfd466e8182e3dd2f82f Mon Sep 17 00:00:00 2001 From: angel raynov Date: Tue, 30 Jul 2024 16:24:37 +0300 Subject: [PATCH 6/8] add TestIsAuthorizedForDelete --- handler/handler_test.go | 70 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/handler/handler_test.go b/handler/handler_test.go index 6616938..a90d8ad 100644 --- a/handler/handler_test.go +++ b/handler/handler_test.go @@ -3161,6 +3161,76 @@ func TestGetSchemas(t *testing.T) { } } +func TestIsAuthorizedForDelete(t *testing.T) { + tests := []struct { + name string + requestHeaders map[string]string + scheduledForDelete interface{} + expectedResult bool + }{ + { + name: "authorized deletion", + requestHeaders: map[string]string{ + "Access-From": "test", + "X-Policy": "PBLC_WRITE_88fdde6c-2aa4-4f78-af02-9f680097cfd6", + }, + scheduledForDelete: map[string]interface{}{ + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + }, + expectedResult: true, + }, + { + name: "authorized deletion no returned publication", + requestHeaders: map[string]string{ + "Access-From": "test", + "X-Policy": "PBLC_WRITE_88fdde6c-2aa4-4f78-af02-9f680097cfd6", + }, + scheduledForDelete: map[string]interface{}{}, + expectedResult: true, + }, + { + name: "unauthorized deletion with read policy", + requestHeaders: map[string]string{ + "Access-From": "test", + "X-Policy": "PBLC_READ_88fdde6c-2aa4-4f78-af02-9f680097cfd6", + }, + scheduledForDelete: map[string]interface{}{}, + expectedResult: false, + }, + { + name: "authorized deletion no access header", + requestHeaders: map[string]string{}, + scheduledForDelete: map[string]interface{}{ + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + }, + expectedResult: true, + }, + { + name: "unauthorized deletion", + requestHeaders: map[string]string{ + "Access-From": "API Gateway", + "X-Policy": "PBLC_WRITE_11fdde6c-2aa4-4f78-af02-9f680097cfd6", + }, + scheduledForDelete: map[string]interface{}{ + "publication": []interface{}{"88fdde6c-2aa4-4f78-af02-9f680097cfd6"}, + }, + expectedResult: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req, _ := http.NewRequest("GET", "/", nil) + for k, v := range tt.requestHeaders { + req.Header.Set(k, v) + } + + result := isAuthorizedForDelete(req, tt.scheduledForDelete) + assert.Equal(t, tt.expectedResult, result) + }) + } +} + type AugmenterMock struct { mock.Mock augment func(ctx context.Context, depletedAnnotations []interface{}) ([]interface{}, error) From daf7526aa3eef9436b3d6f387a5e9a453026ab4d Mon Sep 17 00:00:00 2001 From: angel raynov Date: Tue, 30 Jul 2024 16:54:40 +0300 Subject: [PATCH 7/8] address linter --- annotations/annotations_api_test.go | 20 +++++++++++++++----- handler/handler.go | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/annotations/annotations_api_test.go b/annotations/annotations_api_test.go index a9f9b19..6ddf488 100644 --- a/annotations/annotations_api_test.go +++ b/annotations/annotations_api_test.go @@ -66,7 +66,7 @@ func TestAnnotationsAPIGTGInvalidURL(t *testing.T) { } func TestAnnotationsAPIGTGConnectionError(t *testing.T) { - annotationsServerMock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + annotationsServerMock := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})) annotationsServerMock.Close() log := logger.NewUPPLogger("draft-annotations-api", "INFO") @@ -87,6 +87,7 @@ func TestHappyAnnotationsAPI(t *testing.T) { annotationsAPI := NewUPPAnnotationsAPI(testClient, annotationsServerMock.URL+"/content/%v/annotations", testBasicAuthUsername, testBasicAuthPassword, log) resp, err := annotationsAPI.getUPPAnnotationsResponse(ctx, uuid) assert.NoError(t, err) + defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) } @@ -102,6 +103,7 @@ func TestHappyAnnotationsAPIWithLifecycles(t *testing.T) { annotationsAPI := NewUPPAnnotationsAPI(testClient, annotationsServerMock.URL+"/content/%v/annotations", testBasicAuthUsername, testBasicAuthPassword, log) resp, err := annotationsAPI.getUPPAnnotationsResponse(ctx, uuid, pacAnnotationLifecycle, v1AnnotationLifecycle, nextVideoAnnotationLifecycle) assert.NoError(t, err) + defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) } @@ -117,6 +119,7 @@ func TestUnhappyAnnotationsAPI(t *testing.T) { annotationsAPI := NewUPPAnnotationsAPI(testClient, annotationsServerMock.URL+"/content/%v/annotations", testBasicAuthUsername, testBasicAuthPassword, log) resp, err := annotationsAPI.getUPPAnnotationsResponse(ctx, uuid) assert.NoError(t, err) + defer resp.Body.Close() assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode) } @@ -129,6 +132,7 @@ func TestNoTIDAnnotationsAPI(t *testing.T) { annotationsAPI := NewUPPAnnotationsAPI(testClient, annotationsServerMock.URL+"/content/%v/annotations", testBasicAuthUsername, testBasicAuthPassword, log) resp, err := annotationsAPI.getUPPAnnotationsResponse(context.TODO(), uuid) assert.NoError(t, err) + defer resp.Body.Close() assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode) } @@ -136,7 +140,9 @@ func TestRequestFailsAnnotationsAPI(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") annotationsAPI := NewUPPAnnotationsAPI(testClient, ":#", testBasicAuthUsername, testBasicAuthPassword, log) resp, err := annotationsAPI.getUPPAnnotationsResponse(context.TODO(), "") - + if err == nil { + defer resp.Body.Close() + } assert.Error(t, err) assert.Nil(t, resp) } @@ -145,7 +151,9 @@ func TestResponseFailsAnnotationsAPI(t *testing.T) { log := logger.NewUPPLogger("draft-annotations-api", "INFO") annotationsAPI := NewUPPAnnotationsAPI(testClient, "#:", testBasicAuthUsername, testBasicAuthPassword, log) resp, err := annotationsAPI.getUPPAnnotationsResponse(context.TODO(), "") - + if err == nil { + defer resp.Body.Close() + } assert.Error(t, err) assert.Nil(t, resp) } @@ -163,13 +171,15 @@ func TestAnnotationsAPITimeout(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50) defer cancel() - _, err := annotationsAPI.getUPPAnnotationsResponse(ctx, testContentUUID) + resp, err := annotationsAPI.getUPPAnnotationsResponse(ctx, testContentUUID) + if err == nil { + defer resp.Body.Close() + } assert.Error(t, err) assert.True(t, (err.(net.Error)).Timeout()) } func TestGetAnnotationsHappy(t *testing.T) { - var testCases = []struct { name string annotationsStatus int diff --git a/handler/handler.go b/handler/handler.go index 0ddfdee..e5b1d8e 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -112,7 +112,7 @@ func (h *Handler) DeleteAnnotation(w http.ResponseWriter, r *http.Request) { if !isAuthorizedForDelete(r, scheduledForDelete) { writeLog.Infof("Not authorized to delete annotation with current policy: %s", r.Header.Get("X-Policy")) w.WriteHeader(http.StatusForbidden) - _, err = w.Write([]byte("Forbidden")) + _, _ = w.Write([]byte("Forbidden")) return } From 157860eb8ec2c71d35870de6edbb6b46c33e75af Mon Sep 17 00:00:00 2001 From: angel raynov Date: Wed, 31 Jul 2024 11:46:03 +0300 Subject: [PATCH 8/8] set publication when deleting annotations --- handler/handler.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/handler/handler.go b/handler/handler.go index e5b1d8e..3497690 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -118,6 +118,15 @@ func (h *Handler) DeleteAnnotation(w http.ResponseWriter, r *http.Request) { annotationsBody := make(map[string]interface{}) annotationsBody["annotations"] = uppList + + //if the policy and the publication from the annotation match, set the publication in the annotations body + if scheduledForDelete != nil { + pub, ok := scheduledForDelete.(map[string]interface{})["publication"] + if ok { + annotationsBody["publication"] = pub + } + } + _, newHash, err := h.saveAndReturnAnnotations(ctx, annotationsBody, writeLog, oldHash, contentUUID) if err != nil { handleWriteErrors("Error writing draft annotations", err, writeLog, w, http.StatusInternalServerError)