func TestUnlock(t *testing.T) { l1 := &testUnlocker{ field1: nonce + payloadBase64Delimiter + string(cypherText), field2: nonce + payloadBase64Delimiter + string(cypherText), } l2 := &testUnlocker{ field1: nonce + payloadBase64Delimiter + string(cypherText), field2: nonce + payloadBase64Delimiter + string(cypherText), } context := ctx.NewContext() store := NewStore(context) store.Save(l1, l2) err := store.Unlock() if err != nil { t.Errorf("Unexpected error: %s", err) return } if !l1.called || !l2.called { t.Errorf("Unlocker not called") return } expected := string(plainText) if !(l1.field1 == expected && l1.field2 == expected && l2.field1 == expected && l2.field2 == expected) { t.Errorf("Unexpected field values: %s", l1.field1) } }
func TestHydrateEntitiesWithPreloaded(t *testing.T) { resetRegistry() context := ctx.NewContext() Preload(context, "users", map[string]interface{}{ "u2": u2, }) Preload(context, "products", map[string]interface{}{ "p2": p2, }) RegisterEntityHandler("users", func(_ *ctx.Context, ids []string) (map[string]interface{}, error) { return map[string]interface{}{ "u1": u1, }, nil }) RegisterEntityHandler("products", func(_ *ctx.Context, ids []string) (map[string]interface{}, error) { return map[string]interface{}{ "p1": p1, }, nil }) entities, err := hydrateEntitiesFromMap(context, map[string]map[string]bool{ "users": {"u1": true}, "products": {"p1": true}, }) if err != nil { t.Errorf("Unexpected error: %s", err) } if !reflect.DeepEqual(entities, map[string]map[string]interface{}{ "users": {"u1": u1, "u2": u2}, "products": {"p1": p1, "p2": p2}, }) { t.Errorf("Unexpected map received: %s", entities) } }
// No handler func TestHydrateEntitiesFromMapNoHandler(t *testing.T) { resetRegistry() _, err := hydrateEntitiesFromMap(ctx.NewContext(), map[string]map[string]bool{ "users": {"u1": true}, }) if err == nil { t.Errorf("Expected error, got none: %s", err) } }
func TestHydrateEntitiesFromMapEmptyMap(t *testing.T) { resetRegistry() entities, err := hydrateEntitiesFromMap(ctx.NewContext(), map[string]map[string]bool{}) if err != nil { t.Errorf("Expected no error: %s", err) } if !reflect.DeepEqual(entities, map[string]map[string]interface{}{}) { t.Errorf("Unexpected map received: %s", entities) } }
func TestHydrateEntitiesFromMapMultipleFailures(t *testing.T) { resetRegistry() RegisterEntityHandler("users", func(_ *ctx.Context, ids []string) (map[string]interface{}, error) { return nil, fmt.Errorf("Failure") }) RegisterEntityHandler("products", func(_ *ctx.Context, ids []string) (map[string]interface{}, error) { return nil, fmt.Errorf("Failure") }) _, err := hydrateEntitiesFromMap(ctx.NewContext(), map[string]map[string]bool{ "users": {"u1": true, "u2": true}, "products": {"p1": true, "p2": true}, }) if err == nil { t.Errorf("Expected an error") } }
func TestMultiError(t *testing.T) { newNonce, _ := NewNonce() // Both of these unlockers will fail since they have the wrong nonce. l1 := &testUnlocker{ field1: newNonce + payloadBase64Delimiter + string(cypherText), field2: newNonce + payloadBase64Delimiter + string(cypherText), } l2 := &testUnlocker{ field1: newNonce + payloadBase64Delimiter + string(cypherText), field2: newNonce + payloadBase64Delimiter + string(cypherText), } context := ctx.NewContext() store := NewStore(context) store.Save(l1, l2) err := store.Unlock() if err == nil { t.Errorf("Unexpected lack of an error") } }
func TestSingleError(t *testing.T) { newNonce, _ := NewNonce() // Supply one correct and one incorrect unlocker. l1 := &testUnlocker{ field1: newNonce + payloadBase64Delimiter + string(cypherText), field2: newNonce + payloadBase64Delimiter + string(cypherText), } l2 := &testUnlocker{ field1: nonce + payloadBase64Delimiter + string(cypherText), field2: nonce + payloadBase64Delimiter + string(cypherText), } context := ctx.NewContext() store := NewStore(context) store.Save(l1, l2) err := store.Unlock() if err == nil { t.Errorf("Unexpected lack of an error") } }
// HTTPHandler takes an ActionHandler and returns a http.Handler instance // that can be used. The type and action are used to determine the context in // several areas, such as transformers and metrics. func (p *ActionProcessor) HTTPHandler(typ, action string, handler ActionHandler) httprouter.Handle { // Create a new timer for timing this handler. timer := metrics.NewTimer() p.MetricsRegistry.Register(typ+"-"+action, timer) sideloadTimer := metrics.NewTimer() p.MetricsRegistry.Register(typ+"-"+action+"-sideload", sideloadTimer) unlockTimer := metrics.NewTimer() p.MetricsRegistry.Register(typ+"-"+action+"-unlock", unlockTimer) return httprouter.Handle(func(w http.ResponseWriter, r *http.Request, params httprouter.Params) { // At the end of this function, add a time metric. defer timer.UpdateSince(time.Now()) // Create a new context for this action. context := ctx.NewContext() context.Request = r context.EntityType = typ SetContextParams(context, params) // Get any criteria, and transform it if required. criteria := RequestCriteria(r) for _, transformer := range requestCriteriaTransformers { transformer(context, criteria) } // Make the criteria available on the content. SetContextCriteria(context, criteria) // Get the base payload back from the ActionHandler instance. payload, code, err := handler.HandleAction(context) if err != nil { RespondWithError(w, r, err) return } // If an empty response is required, return an empty response. if payload == EmptyResponse { RespondWithStatusCode(w, r, code) return } // Build up a response. response := Response{ Payload: payload, } if p.SideloadEnabled { start := time.Now() // Retrieve any sideloaded entities. sideloaded, err := sideload.Load(context, payload, criteria.Sideload) response.Sideload = &sideloaded if err != nil { timer.UpdateSince(start) RespondWithError(w, r, err) return } timer.UpdateSince(start) } // Unlock any entities registered for this request. unlockStartTime := time.Now() err = lynx.ContextStore(context).Unlock() unlockTimer.UpdateSince(unlockStartTime) if err != nil { RespondWithError(w, r, err) return } RespondWithData(w, r, response, code) }) }