1// Copyright 2017, OpenCensus Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15
16package tag
17
18import (
19	"context"
20	"fmt"
21	"reflect"
22	"strings"
23	"testing"
24)
25
26var (
27	ttlUnlimitedPropMd = createMetadatas(WithTTL(TTLUnlimitedPropagation))
28	ttlNoPropMd        = createMetadatas(WithTTL(TTLNoPropagation))
29)
30
31func TestContext(t *testing.T) {
32	k1, _ := NewKey("k1")
33	k2, _ := NewKey("k2")
34
35	ctx := context.Background()
36	ctx, _ = New(ctx,
37		Insert(k1, "v1"),
38		Insert(k2, "v2"),
39	)
40	got := FromContext(ctx)
41	want := newMap()
42	want.insert(k1, "v1", ttlUnlimitedPropMd)
43	want.insert(k2, "v2", ttlUnlimitedPropMd)
44
45	if !reflect.DeepEqual(got, want) {
46		t.Errorf("Map = %#v; want %#v", got, want)
47	}
48}
49
50func TestDo(t *testing.T) {
51	k1, _ := NewKey("k1")
52	k2, _ := NewKey("k2")
53	ctx := context.Background()
54	ctx, _ = New(ctx,
55		Insert(k1, "v1"),
56		Insert(k2, "v2"),
57	)
58	got := FromContext(ctx)
59	want := newMap()
60	want.insert(k1, "v1", ttlUnlimitedPropMd)
61	want.insert(k2, "v2", ttlUnlimitedPropMd)
62	Do(ctx, func(ctx context.Context) {
63		got = FromContext(ctx)
64	})
65	if !reflect.DeepEqual(got, want) {
66		t.Errorf("Map = %#v; want %#v", got, want)
67	}
68}
69
70func TestNewMap(t *testing.T) {
71	k1, _ := NewKey("k1")
72	k2, _ := NewKey("k2")
73	k3, _ := NewKey("k3")
74	k4, _ := NewKey("k4")
75	k5, _ := NewKey("k5")
76
77	initial := makeTestTagMap(5)
78
79	tests := []struct {
80		name    string
81		initial *Map
82		mods    []Mutator
83		want    *Map
84	}{
85		{
86			name:    "from empty; insert",
87			initial: nil,
88			mods: []Mutator{
89				Insert(k5, "v5"),
90			},
91			want: makeTestTagMap(2, 4, 5),
92		},
93		{
94			name:    "from empty; insert existing",
95			initial: nil,
96			mods: []Mutator{
97				Insert(k1, "v1"),
98			},
99			want: makeTestTagMap(1, 2, 4),
100		},
101		{
102			name:    "from empty; update",
103			initial: nil,
104			mods: []Mutator{
105				Update(k1, "v1"),
106			},
107			want: makeTestTagMap(2, 4),
108		},
109		{
110			name:    "from empty; update unexisting",
111			initial: nil,
112			mods: []Mutator{
113				Update(k5, "v5"),
114			},
115			want: makeTestTagMap(2, 4),
116		},
117		{
118			name:    "from existing; upsert",
119			initial: initial,
120			mods: []Mutator{
121				Upsert(k5, "v5"),
122			},
123			want: makeTestTagMap(2, 4, 5),
124		},
125		{
126			name:    "from existing; delete",
127			initial: initial,
128			mods: []Mutator{
129				Delete(k2),
130			},
131			want: makeTestTagMap(4, 5),
132		},
133		{
134			name:    "from empty; invalid",
135			initial: nil,
136			mods: []Mutator{
137				Insert(k5, "v\x19"),
138				Upsert(k5, "v\x19"),
139				Update(k5, "v\x19"),
140			},
141			want: nil,
142		},
143		{
144			name:    "from empty; no partial",
145			initial: nil,
146			mods: []Mutator{
147				Insert(k5, "v1"),
148				Update(k5, "v\x19"),
149			},
150			want: nil,
151		},
152	}
153
154	for _, tt := range tests {
155		mods := []Mutator{
156			Insert(k1, "v1"),
157			Insert(k2, "v2"),
158			Update(k3, "v3"),
159			Upsert(k4, "v4"),
160			Insert(k2, "v2"),
161			Delete(k1),
162		}
163		mods = append(mods, tt.mods...)
164		ctx := NewContext(context.Background(), tt.initial)
165		ctx, err := New(ctx, mods...)
166		if tt.want != nil && err != nil {
167			t.Errorf("%v: New = %v", tt.name, err)
168		}
169
170		if got, want := FromContext(ctx), tt.want; !reflect.DeepEqual(got, want) {
171			t.Errorf("%v: got %v; want %v", tt.name, got, want)
172		}
173	}
174}
175
176func TestNewMapWithMetadata(t *testing.T) {
177	k3, _ := NewKey("k3")
178	k4, _ := NewKey("k4")
179	k5, _ := NewKey("k5")
180
181	tests := []struct {
182		name    string
183		initial *Map
184		mods    []Mutator
185		want    *Map
186	}{
187		{
188			name:    "from empty; insert",
189			initial: nil,
190			mods: []Mutator{
191				Insert(k5, "5", WithTTL(TTLNoPropagation)),
192				Insert(k4, "4"),
193			},
194			want: makeTestTagMapWithMetadata(
195				tagContent{"5", ttlNoPropMd},
196				tagContent{"4", ttlUnlimitedPropMd}),
197		},
198		{
199			name:    "from existing; insert existing",
200			initial: makeTestTagMapWithMetadata(tagContent{"5", ttlNoPropMd}),
201			mods: []Mutator{
202				Insert(k5, "5", WithTTL(TTLUnlimitedPropagation)),
203			},
204			want: makeTestTagMapWithMetadata(tagContent{"5", ttlNoPropMd}),
205		},
206		{
207			name:    "from existing; update non-existing",
208			initial: makeTestTagMapWithMetadata(tagContent{"5", ttlNoPropMd}),
209			mods: []Mutator{
210				Update(k4, "4", WithTTL(TTLUnlimitedPropagation)),
211			},
212			want: makeTestTagMapWithMetadata(tagContent{"5", ttlNoPropMd}),
213		},
214		{
215			name: "from existing; update existing",
216			initial: makeTestTagMapWithMetadata(
217				tagContent{"5", ttlUnlimitedPropMd},
218				tagContent{"4", ttlNoPropMd}),
219			mods: []Mutator{
220				Update(k5, "5"),
221				Update(k4, "4", WithTTL(TTLUnlimitedPropagation)),
222			},
223			want: makeTestTagMapWithMetadata(
224				tagContent{"5", ttlUnlimitedPropMd},
225				tagContent{"4", ttlUnlimitedPropMd}),
226		},
227		{
228			name: "from existing; upsert existing",
229			initial: makeTestTagMapWithMetadata(
230				tagContent{"5", ttlNoPropMd},
231				tagContent{"4", ttlNoPropMd}),
232			mods: []Mutator{
233				Upsert(k4, "4", WithTTL(TTLUnlimitedPropagation)),
234			},
235			want: makeTestTagMapWithMetadata(
236				tagContent{"5", ttlNoPropMd},
237				tagContent{"4", ttlUnlimitedPropMd}),
238		},
239		{
240			name: "from existing; upsert non-existing",
241			initial: makeTestTagMapWithMetadata(
242				tagContent{"5", ttlNoPropMd}),
243			mods: []Mutator{
244				Upsert(k4, "4", WithTTL(TTLUnlimitedPropagation)),
245				Upsert(k3, "3"),
246			},
247			want: makeTestTagMapWithMetadata(
248				tagContent{"5", ttlNoPropMd},
249				tagContent{"4", ttlUnlimitedPropMd},
250				tagContent{"3", ttlUnlimitedPropMd}),
251		},
252		{
253			name: "from existing; delete",
254			initial: makeTestTagMapWithMetadata(
255				tagContent{"5", ttlNoPropMd},
256				tagContent{"4", ttlNoPropMd}),
257			mods: []Mutator{
258				Delete(k5),
259			},
260			want: makeTestTagMapWithMetadata(
261				tagContent{"4", ttlNoPropMd}),
262		},
263		{
264			name:    "from non-existing; upsert with multiple-metadata",
265			initial: nil,
266			mods: []Mutator{
267				Upsert(k4, "4", WithTTL(TTLUnlimitedPropagation), WithTTL(TTLNoPropagation)),
268				Upsert(k5, "5", WithTTL(TTLNoPropagation), WithTTL(TTLUnlimitedPropagation)),
269			},
270			want: makeTestTagMapWithMetadata(
271				tagContent{"4", ttlNoPropMd},
272				tagContent{"5", ttlUnlimitedPropMd}),
273		},
274		{
275			name:    "from non-existing; insert with multiple-metadata",
276			initial: nil,
277			mods: []Mutator{
278				Insert(k5, "5", WithTTL(TTLNoPropagation), WithTTL(TTLUnlimitedPropagation)),
279			},
280			want: makeTestTagMapWithMetadata(
281				tagContent{"5", ttlUnlimitedPropMd}),
282		},
283		{
284			name: "from existing; update with multiple-metadata",
285			initial: makeTestTagMapWithMetadata(
286				tagContent{"5", ttlNoPropMd}),
287			mods: []Mutator{
288				Update(k5, "5", WithTTL(TTLNoPropagation), WithTTL(TTLUnlimitedPropagation)),
289			},
290			want: makeTestTagMapWithMetadata(
291				tagContent{"5", ttlUnlimitedPropMd}),
292		},
293		{
294			name:    "from empty; update invalid",
295			initial: nil,
296			mods: []Mutator{
297				Insert(k4, "4\x19", WithTTL(TTLUnlimitedPropagation)),
298				Upsert(k4, "4\x19", WithTTL(TTLUnlimitedPropagation)),
299				Update(k4, "4\x19", WithTTL(TTLUnlimitedPropagation)),
300			},
301			want: nil,
302		},
303		{
304			name:    "from empty; insert partial",
305			initial: nil,
306			mods: []Mutator{
307				Upsert(k3, "3", WithTTL(TTLUnlimitedPropagation)),
308				Upsert(k4, "4\x19", WithTTL(TTLUnlimitedPropagation)),
309			},
310			want: nil,
311		},
312	}
313
314	// Test api for insert, update, and upsert using metadata.
315	for _, tt := range tests {
316		ctx := NewContext(context.Background(), tt.initial)
317		ctx, err := New(ctx, tt.mods...)
318		if tt.want != nil && err != nil {
319			t.Errorf("%v: New = %v", tt.name, err)
320		}
321
322		if got, want := FromContext(ctx), tt.want; !reflect.DeepEqual(got, want) {
323			t.Errorf("%v: got %v; want %v", tt.name, got, want)
324		}
325	}
326}
327
328func TestNewValidation(t *testing.T) {
329	tests := []struct {
330		err  string
331		seed *Map
332	}{
333		// Key name validation in seed
334		{err: "invalid key", seed: &Map{m: map[Key]tagContent{{name: ""}: {"foo", ttlNoPropMd}}}},
335		{err: "", seed: &Map{m: map[Key]tagContent{{name: "key"}: {"foo", ttlNoPropMd}}}},
336		{err: "", seed: &Map{m: map[Key]tagContent{{name: strings.Repeat("a", 255)}: {"census", ttlNoPropMd}}}},
337		{err: "invalid key", seed: &Map{m: map[Key]tagContent{{name: strings.Repeat("a", 256)}: {"census", ttlNoPropMd}}}},
338		{err: "invalid key", seed: &Map{m: map[Key]tagContent{{name: "Приве́т"}: {"census", ttlNoPropMd}}}},
339
340		// Value validation
341		{err: "", seed: &Map{m: map[Key]tagContent{{name: "key"}: {"", ttlNoPropMd}}}},
342		{err: "", seed: &Map{m: map[Key]tagContent{{name: "key"}: {strings.Repeat("a", 255), ttlNoPropMd}}}},
343		{err: "invalid value", seed: &Map{m: map[Key]tagContent{{name: "key"}: {"Приве́т", ttlNoPropMd}}}},
344		{err: "invalid value", seed: &Map{m: map[Key]tagContent{{name: "key"}: {strings.Repeat("a", 256), ttlNoPropMd}}}},
345	}
346
347	for i, tt := range tests {
348		ctx := NewContext(context.Background(), tt.seed)
349		ctx, err := New(ctx)
350
351		if tt.err != "" {
352			if err == nil {
353				t.Errorf("#%d: got nil error; want %q", i, tt.err)
354				continue
355			} else if s, substr := err.Error(), tt.err; !strings.Contains(s, substr) {
356				t.Errorf("#%d:\ngot %q\nwant %q", i, s, substr)
357			}
358			continue
359		}
360		if err != nil {
361			t.Errorf("#%d: got %q want nil", i, err)
362			continue
363		}
364		m := FromContext(ctx)
365		if m == nil {
366			t.Errorf("#%d: got nil map", i)
367			continue
368		}
369	}
370}
371
372func makeTestTagMap(ids ...int) *Map {
373	m := newMap()
374	for _, v := range ids {
375		k, _ := NewKey(fmt.Sprintf("k%d", v))
376		m.m[k] = tagContent{fmt.Sprintf("v%d", v), ttlUnlimitedPropMd}
377	}
378	return m
379}
380
381func makeTestTagMapWithMetadata(tcs ...tagContent) *Map {
382	m := newMap()
383	for _, tc := range tcs {
384		k, _ := NewKey(fmt.Sprintf("k%s", tc.value))
385		m.m[k] = tc
386	}
387	return m
388}
389