func TestSimpleBlobStoreLiveWithoutSnapshot(t *testing.T) {
	s, indexer := simpleBlobStoreSetup()
	b := blob.NewBlob("event", "id")
	if err := s.Store(StoreLiveEvent, &testRepository, b); err != nil {
		t.Fatalf("failed to store blob: %v", err)
	}
	if indexer.Len() != 1 {
		t.Fatalf("indexer was called %d times, expected once", indexer.Len())
	}
}
func TestSimpleBlobStoreCascading(t *testing.T) {
	s, indexer := simpleBlobStoreSetup()

	// Create a snapshottable blob.
	b := blob.NewBlob("event", "id")
	b.Push(config.MetadataSnapshotID, "snapshot_id")
	b.Push(config.MetadataSnapshotField, "snapshot_field")

	// Verify that storing to StoreSnapshot doesn't cascade to any other store.
	if err := s.Store(StoreSnapshot, &testRepository, b); err != nil {
		t.Fatalf("failed to store blob: %v", err)
	}
	if indexer.Len() != 1 {
		t.Fatalf("indexer was called %d times, expected once", indexer.Len())
	}
	destSnapshot := (*indexer)[0].Destination

	// Verify that storing to StoreCurrentState cascades to StoreSnapshot using
	// the same destination than a direct call.
	indexer.Reset()
	if err := s.Store(StoreCurrentState, &testRepository, b); err != nil {
		t.Fatalf("failed to store blob: %v", err)
	}
	if indexer.Len() != 2 {
		t.Fatalf("indexer was called %d times, expected once", indexer.Len())
	}
	destCurrent := (*indexer)[0].Destination
	if cascading := (*indexer)[1].Destination; cascading != destSnapshot {
		t.Fatalf("cascading current snapshot to %q, expected %q", cascading, destSnapshot)
	}

	// Verify that storing to StoreLiveEvent cascades to StoreCurrentState using
	// the same destination than a direct call, and to StoreSnapshot using the
	// same destination than a direct call.
	indexer.Reset()
	if err := s.Store(StoreLiveEvent, &testRepository, b); err != nil {
		t.Fatalf("failed to store blob: %v", err)
	}
	if indexer.Len() != 3 {
		t.Fatalf("indexer was called %d times, expected once", indexer.Len())
	}
	if cascading := (*indexer)[1].Destination; cascading != destCurrent {
		t.Fatalf("cascading live event to %q, expected %q", cascading, destCurrent)
	}
	if cascading := (*indexer)[2].Destination; cascading != destSnapshot {
		t.Fatalf("cascading live event to %q, expected %q", cascading, destSnapshot)
	}
}
// Apply takes a serialized JSON payload and returns a Blob on which the
// transformation has been applied, as well as a collection of metadata
// corresponding to fields prefixed by an underscore.
func (t Transformation) Apply(b *blob.Blob) (*blob.Blob, error) {
	m, err := b.Data.Map()
	if err != nil {
		return nil, err
	}

	// Create the result blob, but inherit from the parent's metadata.
	res := blob.NewBlob(b.Type, b.ID)
	res.Timestamp = b.Timestamp

	// For each destination field defined in the transformation, apply the
	// associated template and store it in the output.
	for key, tmpl := range t {
		// Visit the template to extract the field values.
		vis := &visitor{}
		if err := tmpl.Execute(vis, m); err != nil {
			return nil, err
		}
		res.Push(key, vis.Value())
	}
	return res, nil
}