1package storagepacker
2
3import (
4	"context"
5	"fmt"
6	"testing"
7
8	"github.com/golang/protobuf/proto"
9	"github.com/golang/protobuf/ptypes"
10	log "github.com/hashicorp/go-hclog"
11	uuid "github.com/hashicorp/go-uuid"
12	"github.com/hashicorp/vault/helper/identity"
13	"github.com/hashicorp/vault/sdk/logical"
14)
15
16func BenchmarkStoragePacker(b *testing.B) {
17	storagePacker, err := NewStoragePacker(&logical.InmemStorage{}, log.New(&log.LoggerOptions{Name: "storagepackertest"}), "")
18	if err != nil {
19		b.Fatal(err)
20	}
21
22	ctx := context.Background()
23
24	for i := 0; i < b.N; i++ {
25		itemID, err := uuid.GenerateUUID()
26		if err != nil {
27			b.Fatal(err)
28		}
29
30		item := &Item{
31			ID: itemID,
32		}
33
34		err = storagePacker.PutItem(ctx, item)
35		if err != nil {
36			b.Fatal(err)
37		}
38
39		fetchedItem, err := storagePacker.GetItem(itemID)
40		if err != nil {
41			b.Fatal(err)
42		}
43
44		if fetchedItem == nil {
45			b.Fatalf("failed to read stored item with ID: %q, iteration: %d", item.ID, i)
46		}
47
48		if fetchedItem.ID != item.ID {
49			b.Fatalf("bad: item ID; expected: %q\n actual: %q", item.ID, fetchedItem.ID)
50		}
51
52		err = storagePacker.DeleteItem(ctx, item.ID)
53		if err != nil {
54			b.Fatal(err)
55		}
56
57		fetchedItem, err = storagePacker.GetItem(item.ID)
58		if err != nil {
59			b.Fatal(err)
60		}
61		if fetchedItem != nil {
62			b.Fatalf("failed to delete item")
63		}
64	}
65}
66
67func TestStoragePacker(t *testing.T) {
68	storagePacker, err := NewStoragePacker(&logical.InmemStorage{}, log.New(&log.LoggerOptions{Name: "storagepackertest"}), "")
69	if err != nil {
70		t.Fatal(err)
71	}
72
73	ctx := context.Background()
74
75	// Persist a storage entry
76	item1 := &Item{
77		ID: "item1",
78	}
79
80	err = storagePacker.PutItem(ctx, item1)
81	if err != nil {
82		t.Fatal(err)
83	}
84
85	// Verify that it can be read
86	fetchedItem, err := storagePacker.GetItem(item1.ID)
87	if err != nil {
88		t.Fatal(err)
89	}
90	if fetchedItem == nil {
91		t.Fatalf("failed to read the stored item")
92	}
93
94	if item1.ID != fetchedItem.ID {
95		t.Fatalf("bad: item ID; expected: %q\n actual: %q\n", item1.ID, fetchedItem.ID)
96	}
97
98	// Delete item1
99	err = storagePacker.DeleteItem(ctx, item1.ID)
100	if err != nil {
101		t.Fatal(err)
102	}
103
104	// Check that the deletion was successful
105	fetchedItem, err = storagePacker.GetItem(item1.ID)
106	if err != nil {
107		t.Fatal(err)
108	}
109
110	if fetchedItem != nil {
111		t.Fatalf("failed to delete item")
112	}
113}
114
115func TestStoragePacker_SerializeDeserializeComplexItem(t *testing.T) {
116	storagePacker, err := NewStoragePacker(&logical.InmemStorage{}, log.New(&log.LoggerOptions{Name: "storagepackertest"}), "")
117	if err != nil {
118		t.Fatal(err)
119	}
120
121	ctx := context.Background()
122
123	timeNow := ptypes.TimestampNow()
124
125	alias1 := &identity.Alias{
126		ID:            "alias_id",
127		CanonicalID:   "canonical_id",
128		MountType:     "mount_type",
129		MountAccessor: "mount_accessor",
130		Metadata: map[string]string{
131			"aliasmkey": "aliasmvalue",
132		},
133		Name:                   "alias_name",
134		CreationTime:           timeNow,
135		LastUpdateTime:         timeNow,
136		MergedFromCanonicalIDs: []string{"merged_from_canonical_id"},
137	}
138
139	entity := &identity.Entity{
140		Aliases: []*identity.Alias{alias1},
141		ID:      "entity_id",
142		Name:    "entity_name",
143		Metadata: map[string]string{
144			"testkey1": "testvalue1",
145			"testkey2": "testvalue2",
146		},
147		CreationTime:    timeNow,
148		LastUpdateTime:  timeNow,
149		BucketKey:       "entity_hash",
150		MergedEntityIDs: []string{"merged_entity_id1", "merged_entity_id2"},
151		Policies:        []string{"policy1", "policy2"},
152	}
153
154	marshaledEntity, err := ptypes.MarshalAny(entity)
155	if err != nil {
156		t.Fatal(err)
157	}
158	err = storagePacker.PutItem(ctx, &Item{
159		ID:      entity.ID,
160		Message: marshaledEntity,
161	})
162	if err != nil {
163		t.Fatal(err)
164	}
165
166	itemFetched, err := storagePacker.GetItem(entity.ID)
167	if err != nil {
168		t.Fatal(err)
169	}
170
171	var itemDecoded identity.Entity
172	err = ptypes.UnmarshalAny(itemFetched.Message, &itemDecoded)
173	if err != nil {
174		t.Fatal(err)
175	}
176
177	if !proto.Equal(&itemDecoded, entity) {
178		t.Fatalf("bad: expected: %#v\nactual: %#v\n", entity, itemDecoded)
179	}
180}
181
182func TestStoragePacker_DeleteMultiple(t *testing.T) {
183	storagePacker, err := NewStoragePacker(&logical.InmemStorage{}, log.New(&log.LoggerOptions{Name: "storagepackertest"}), "")
184	if err != nil {
185		t.Fatal(err)
186	}
187
188	ctx := context.Background()
189
190	// Persist a storage entry
191	for i := 0; i < 100; i++ {
192		item := &Item{
193			ID: fmt.Sprintf("item%d", i),
194		}
195
196		err = storagePacker.PutItem(ctx, item)
197		if err != nil {
198			t.Fatal(err)
199		}
200
201		// Verify that it can be read
202		fetchedItem, err := storagePacker.GetItem(item.ID)
203		if err != nil {
204			t.Fatal(err)
205		}
206		if fetchedItem == nil {
207			t.Fatalf("failed to read the stored item")
208		}
209
210		if item.ID != fetchedItem.ID {
211			t.Fatalf("bad: item ID; expected: %q\n actual: %q\n", item.ID, fetchedItem.ID)
212		}
213	}
214
215	itemsToDelete := make([]string, 0, 50)
216	for i := 1; i < 100; i += 2 {
217		itemsToDelete = append(itemsToDelete, fmt.Sprintf("item%d", i))
218	}
219
220	err = storagePacker.DeleteMultipleItems(ctx, nil, itemsToDelete)
221	if err != nil {
222		t.Fatal(err)
223	}
224
225	// Check that the deletion was successful
226	for i := 0; i < 100; i++ {
227		fetchedItem, err := storagePacker.GetItem(fmt.Sprintf("item%d", i))
228		if err != nil {
229			t.Fatal(err)
230		}
231
232		if i%2 == 0 && fetchedItem == nil {
233			t.Fatal("expected item not found")
234		}
235		if i%2 == 1 && fetchedItem != nil {
236			t.Fatalf("failed to delete item")
237		}
238	}
239}
240
241func TestStoragePacker_DeleteMultiple_ALL(t *testing.T) {
242	storagePacker, err := NewStoragePacker(&logical.InmemStorage{}, log.New(&log.LoggerOptions{Name: "storagepackertest"}), "")
243	if err != nil {
244		t.Fatal(err)
245	}
246
247	ctx := context.Background()
248
249	// Persist a storage entry
250	itemsToDelete := make([]string, 0, 10000)
251	for i := 0; i < 10000; i++ {
252		item := &Item{
253			ID: fmt.Sprintf("item%d", i),
254		}
255
256		err = storagePacker.PutItem(ctx, item)
257		if err != nil {
258			t.Fatal(err)
259		}
260
261		// Verify that it can be read
262		fetchedItem, err := storagePacker.GetItem(item.ID)
263		if err != nil {
264			t.Fatal(err)
265		}
266		if fetchedItem == nil {
267			t.Fatalf("failed to read the stored item")
268		}
269
270		if item.ID != fetchedItem.ID {
271			t.Fatalf("bad: item ID; expected: %q\n actual: %q\n", item.ID, fetchedItem.ID)
272		}
273
274		itemsToDelete = append(itemsToDelete, fmt.Sprintf("item%d", i))
275	}
276
277	err = storagePacker.DeleteMultipleItems(ctx, nil, itemsToDelete)
278	if err != nil {
279		t.Fatal(err)
280	}
281
282	// Check that the deletion was successful
283	for _, item := range itemsToDelete {
284		fetchedItem, err := storagePacker.GetItem(item)
285		if err != nil {
286			t.Fatal(err)
287		}
288		if fetchedItem != nil {
289			t.Fatal("item not deleted")
290		}
291	}
292}
293