tracestate_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. // Copyright The OpenTelemetry 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. package trace
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "testing"
  19. "github.com/stretchr/testify/assert"
  20. "github.com/stretchr/testify/require"
  21. )
  22. // Taken from the W3C tests:
  23. // https://github.com/w3c/trace-context/blob/dcd3ad9b7d6ac36f70ff3739874b73c11b0302a1/test/test_data.json
  24. var testcases = []struct {
  25. name string
  26. in string
  27. tracestate TraceState
  28. out string
  29. err error
  30. }{
  31. {
  32. name: "duplicate with the same value",
  33. in: "foo=1,foo=1",
  34. err: errDuplicate,
  35. },
  36. {
  37. name: "duplicate with different values",
  38. in: "foo=1,foo=2",
  39. err: errDuplicate,
  40. },
  41. {
  42. name: "improperly formatted key/value pair",
  43. in: "foo =1",
  44. err: errInvalidMember,
  45. },
  46. {
  47. name: "upper case key",
  48. in: "FOO=1",
  49. err: errInvalidMember,
  50. },
  51. {
  52. name: "key with invalid character",
  53. in: "foo.bar=1",
  54. err: errInvalidMember,
  55. },
  56. {
  57. name: "multiple keys, one with empty tenant key",
  58. in: "foo@=1,bar=2",
  59. err: errInvalidMember,
  60. },
  61. {
  62. name: "multiple keys, one with only tenant",
  63. in: "@foo=1,bar=2",
  64. err: errInvalidMember,
  65. },
  66. {
  67. name: "multiple keys, one with double tenant separator",
  68. in: "foo@@bar=1,bar=2",
  69. err: errInvalidMember,
  70. },
  71. {
  72. name: "multiple keys, one with multiple tenants",
  73. in: "foo@bar@baz=1,bar=2",
  74. err: errInvalidMember,
  75. },
  76. {
  77. name: "key too long",
  78. in: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1",
  79. err: errInvalidMember,
  80. },
  81. {
  82. name: "key too long, with tenant",
  83. in: "foo=1,tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@v=1",
  84. err: errInvalidMember,
  85. },
  86. {
  87. name: "tenant too long",
  88. in: "foo=1,t@vvvvvvvvvvvvvvv=1",
  89. err: errInvalidMember,
  90. },
  91. {
  92. name: "multiple values for a single key",
  93. in: "foo=bar=baz",
  94. err: errInvalidMember,
  95. },
  96. {
  97. name: "no value",
  98. in: "foo=,bar=3",
  99. err: errInvalidMember,
  100. },
  101. {
  102. name: "too many members",
  103. in: "bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10,bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20,bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30,bar31=31,bar32=32,bar33=33",
  104. err: errMemberNumber,
  105. },
  106. {
  107. name: "valid key/value list",
  108. in: "abcdefghijklmnopqrstuvwxyz0123456789_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
  109. out: "abcdefghijklmnopqrstuvwxyz0123456789_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
  110. tracestate: TraceState{list: []member{
  111. {
  112. Key: "abcdefghijklmnopqrstuvwxyz0123456789_-*/",
  113. Value: " !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
  114. },
  115. }},
  116. },
  117. {
  118. name: "valid key/value list with tenant",
  119. in: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
  120. out: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
  121. tracestate: TraceState{list: []member{
  122. {
  123. Key: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/",
  124. Value: " !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
  125. },
  126. }},
  127. },
  128. {
  129. name: "empty input",
  130. // Empty input should result in no error and a zero value
  131. // TraceState being returned, that TraceState should be encoded as an
  132. // empty string.
  133. },
  134. {
  135. name: "single key and value",
  136. in: "foo=1",
  137. out: "foo=1",
  138. tracestate: TraceState{list: []member{
  139. {Key: "foo", Value: "1"},
  140. }},
  141. },
  142. {
  143. name: "single key and value with empty separator",
  144. in: "foo=1,",
  145. out: "foo=1",
  146. tracestate: TraceState{list: []member{
  147. {Key: "foo", Value: "1"},
  148. }},
  149. },
  150. {
  151. name: "multiple keys and values",
  152. in: "foo=1,bar=2",
  153. out: "foo=1,bar=2",
  154. tracestate: TraceState{list: []member{
  155. {Key: "foo", Value: "1"},
  156. {Key: "bar", Value: "2"},
  157. }},
  158. },
  159. {
  160. name: "with a key at maximum length",
  161. in: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1",
  162. out: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1",
  163. tracestate: TraceState{list: []member{
  164. {
  165. Key: "foo",
  166. Value: "1",
  167. },
  168. {
  169. Key: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
  170. Value: "1",
  171. },
  172. }},
  173. },
  174. {
  175. name: "with a key and tenant at maximum length",
  176. in: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1",
  177. out: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1",
  178. tracestate: TraceState{list: []member{
  179. {
  180. Key: "foo",
  181. Value: "1",
  182. },
  183. {
  184. Key: "ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv",
  185. Value: "1",
  186. },
  187. }},
  188. },
  189. {
  190. name: "with maximum members",
  191. in: "bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10,bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20,bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30,bar31=31,bar32=32",
  192. out: "bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10,bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20,bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30,bar31=31,bar32=32",
  193. tracestate: TraceState{list: []member{
  194. {Key: "bar01", Value: "01"},
  195. {Key: "bar02", Value: "02"},
  196. {Key: "bar03", Value: "03"},
  197. {Key: "bar04", Value: "04"},
  198. {Key: "bar05", Value: "05"},
  199. {Key: "bar06", Value: "06"},
  200. {Key: "bar07", Value: "07"},
  201. {Key: "bar08", Value: "08"},
  202. {Key: "bar09", Value: "09"},
  203. {Key: "bar10", Value: "10"},
  204. {Key: "bar11", Value: "11"},
  205. {Key: "bar12", Value: "12"},
  206. {Key: "bar13", Value: "13"},
  207. {Key: "bar14", Value: "14"},
  208. {Key: "bar15", Value: "15"},
  209. {Key: "bar16", Value: "16"},
  210. {Key: "bar17", Value: "17"},
  211. {Key: "bar18", Value: "18"},
  212. {Key: "bar19", Value: "19"},
  213. {Key: "bar20", Value: "20"},
  214. {Key: "bar21", Value: "21"},
  215. {Key: "bar22", Value: "22"},
  216. {Key: "bar23", Value: "23"},
  217. {Key: "bar24", Value: "24"},
  218. {Key: "bar25", Value: "25"},
  219. {Key: "bar26", Value: "26"},
  220. {Key: "bar27", Value: "27"},
  221. {Key: "bar28", Value: "28"},
  222. {Key: "bar29", Value: "29"},
  223. {Key: "bar30", Value: "30"},
  224. {Key: "bar31", Value: "31"},
  225. {Key: "bar32", Value: "32"},
  226. }},
  227. },
  228. {
  229. name: "with several members",
  230. in: "foo=1,bar=2,rojo=1,congo=2,baz=3",
  231. out: "foo=1,bar=2,rojo=1,congo=2,baz=3",
  232. tracestate: TraceState{list: []member{
  233. {Key: "foo", Value: "1"},
  234. {Key: "bar", Value: "2"},
  235. {Key: "rojo", Value: "1"},
  236. {Key: "congo", Value: "2"},
  237. {Key: "baz", Value: "3"},
  238. }},
  239. },
  240. {
  241. name: "with tabs between members",
  242. in: "foo=1 \t , \t bar=2, \t baz=3",
  243. out: "foo=1,bar=2,baz=3",
  244. tracestate: TraceState{list: []member{
  245. {Key: "foo", Value: "1"},
  246. {Key: "bar", Value: "2"},
  247. {Key: "baz", Value: "3"},
  248. }},
  249. },
  250. {
  251. name: "with multiple tabs between members",
  252. in: "foo=1\t \t,\t \tbar=2,\t \tbaz=3",
  253. out: "foo=1,bar=2,baz=3",
  254. tracestate: TraceState{list: []member{
  255. {Key: "foo", Value: "1"},
  256. {Key: "bar", Value: "2"},
  257. {Key: "baz", Value: "3"},
  258. }},
  259. },
  260. {
  261. name: "with space at the end of the member",
  262. in: "foo=1 ",
  263. out: "foo=1",
  264. tracestate: TraceState{list: []member{
  265. {Key: "foo", Value: "1"},
  266. }},
  267. },
  268. {
  269. name: "with tab at the end of the member",
  270. in: "foo=1\t",
  271. out: "foo=1",
  272. tracestate: TraceState{list: []member{
  273. {Key: "foo", Value: "1"},
  274. }},
  275. },
  276. {
  277. name: "with tab and space at the end of the member",
  278. in: "foo=1 \t",
  279. out: "foo=1",
  280. tracestate: TraceState{list: []member{
  281. {Key: "foo", Value: "1"},
  282. }},
  283. },
  284. }
  285. var maxMembers = func() TraceState {
  286. members := make([]member, maxListMembers)
  287. for i := 0; i < maxListMembers; i++ {
  288. members[i] = member{
  289. Key: fmt.Sprintf("key%d", i+1),
  290. Value: fmt.Sprintf("value%d", i+1),
  291. }
  292. }
  293. return TraceState{list: members}
  294. }()
  295. func TestParseTraceState(t *testing.T) {
  296. for _, tc := range testcases {
  297. t.Run(tc.name, func(t *testing.T) {
  298. got, err := ParseTraceState(tc.in)
  299. assert.Equal(t, tc.tracestate, got)
  300. if tc.err != nil {
  301. assert.ErrorIs(t, err, tc.err, tc.in)
  302. } else {
  303. assert.NoError(t, err, tc.in)
  304. }
  305. })
  306. }
  307. }
  308. func TestTraceStateString(t *testing.T) {
  309. for _, tc := range testcases {
  310. if tc.err != nil {
  311. // Only test non-zero value TraceState.
  312. continue
  313. }
  314. t.Run(tc.name, func(t *testing.T) {
  315. assert.Equal(t, tc.out, tc.tracestate.String())
  316. })
  317. }
  318. }
  319. func TestTraceStateMarshalJSON(t *testing.T) {
  320. for _, tc := range testcases {
  321. if tc.err != nil {
  322. // Only test non-zero value TraceState.
  323. continue
  324. }
  325. t.Run(tc.name, func(t *testing.T) {
  326. // Encode UTF-8.
  327. expected, err := json.Marshal(tc.out)
  328. require.NoError(t, err)
  329. actual, err := json.Marshal(tc.tracestate)
  330. require.NoError(t, err)
  331. assert.Equal(t, expected, actual)
  332. })
  333. }
  334. }
  335. func TestTraceStateGet(t *testing.T) {
  336. testCases := []struct {
  337. name string
  338. key string
  339. expected string
  340. }{
  341. {
  342. name: "OK case",
  343. key: "key16",
  344. expected: "value16",
  345. },
  346. {
  347. name: "not found",
  348. key: "keyxx",
  349. expected: "",
  350. },
  351. {
  352. name: "invalid W3C key",
  353. key: "key!",
  354. expected: "",
  355. },
  356. }
  357. for _, tc := range testCases {
  358. t.Run(tc.name, func(t *testing.T) {
  359. assert.Equal(t, tc.expected, maxMembers.Get(tc.key))
  360. })
  361. }
  362. }
  363. func TestTraceStateDelete(t *testing.T) {
  364. ts := TraceState{list: []member{
  365. {Key: "key1", Value: "val1"},
  366. {Key: "key2", Value: "val2"},
  367. {Key: "key3", Value: "val3"},
  368. }}
  369. testCases := []struct {
  370. name string
  371. key string
  372. expected TraceState
  373. }{
  374. {
  375. name: "OK case",
  376. key: "key2",
  377. expected: TraceState{list: []member{
  378. {Key: "key1", Value: "val1"},
  379. {Key: "key3", Value: "val3"},
  380. }},
  381. },
  382. {
  383. name: "Non-existing key",
  384. key: "keyx",
  385. expected: TraceState{list: []member{
  386. {Key: "key1", Value: "val1"},
  387. {Key: "key2", Value: "val2"},
  388. {Key: "key3", Value: "val3"},
  389. }},
  390. },
  391. {
  392. name: "Invalid key",
  393. key: "in va lid",
  394. expected: TraceState{list: []member{
  395. {Key: "key1", Value: "val1"},
  396. {Key: "key2", Value: "val2"},
  397. {Key: "key3", Value: "val3"},
  398. }},
  399. },
  400. }
  401. for _, tc := range testCases {
  402. t.Run(tc.name, func(t *testing.T) {
  403. assert.Equal(t, tc.expected, ts.Delete(tc.key))
  404. })
  405. }
  406. }
  407. func TestTraceStateInsert(t *testing.T) {
  408. ts := TraceState{list: []member{
  409. {Key: "key1", Value: "val1"},
  410. {Key: "key2", Value: "val2"},
  411. {Key: "key3", Value: "val3"},
  412. }}
  413. testCases := []struct {
  414. name string
  415. tracestate TraceState
  416. key, value string
  417. expected TraceState
  418. err error
  419. }{
  420. {
  421. name: "add new",
  422. tracestate: ts,
  423. key: "key4@vendor",
  424. value: "val4",
  425. expected: TraceState{list: []member{
  426. {Key: "key4@vendor", Value: "val4"},
  427. {Key: "key1", Value: "val1"},
  428. {Key: "key2", Value: "val2"},
  429. {Key: "key3", Value: "val3"},
  430. }},
  431. },
  432. {
  433. name: "replace",
  434. tracestate: ts,
  435. key: "key2",
  436. value: "valX",
  437. expected: TraceState{list: []member{
  438. {Key: "key2", Value: "valX"},
  439. {Key: "key1", Value: "val1"},
  440. {Key: "key3", Value: "val3"},
  441. }},
  442. },
  443. {
  444. name: "invalid key",
  445. tracestate: ts,
  446. key: "key!",
  447. value: "val",
  448. expected: ts,
  449. err: errInvalidKey,
  450. },
  451. {
  452. name: "invalid value",
  453. tracestate: ts,
  454. key: "key",
  455. value: "v=l",
  456. expected: ts,
  457. err: errInvalidValue,
  458. },
  459. {
  460. name: "invalid key/value",
  461. tracestate: ts,
  462. key: "key!",
  463. value: "v=l",
  464. expected: ts,
  465. err: errInvalidKey,
  466. },
  467. {
  468. name: "drop the right-most member(oldest) in queue",
  469. tracestate: maxMembers,
  470. key: "keyx",
  471. value: "valx",
  472. expected: func() TraceState {
  473. // Prepend the new element and remove the oldest one, which is over capacity.
  474. return TraceState{
  475. list: append(
  476. []member{{Key: "keyx", Value: "valx"}},
  477. maxMembers.list[:len(maxMembers.list)-1]...,
  478. ),
  479. }
  480. }(),
  481. }}
  482. for _, tc := range testCases {
  483. t.Run(tc.name, func(t *testing.T) {
  484. actual, err := tc.tracestate.Insert(tc.key, tc.value)
  485. assert.ErrorIs(t, err, tc.err, tc.name)
  486. if tc.err != nil {
  487. assert.Equal(t, tc.tracestate, actual)
  488. } else {
  489. assert.Equal(t, tc.expected, actual)
  490. }
  491. })
  492. }
  493. }
  494. func TestTraceStateLen(t *testing.T) {
  495. ts := TraceState{}
  496. assert.Equal(t, 0, ts.Len(), "zero value TraceState is empty")
  497. key := "key"
  498. ts = TraceState{list: []member{{key, "value"}}}
  499. assert.Equal(t, 1, ts.Len(), "TraceState with one value")
  500. }
  501. func TestTraceStateImmutable(t *testing.T) {
  502. k0, v0 := "k0", "v0"
  503. ts0 := TraceState{list: []member{{k0, v0}}}
  504. assert.Equal(t, v0, ts0.Get(k0))
  505. // Insert should not modify the original.
  506. k1, v1 := "k1", "v1"
  507. ts1, err := ts0.Insert(k1, v1)
  508. require.NoError(t, err)
  509. assert.Equal(t, v0, ts0.Get(k0))
  510. assert.Equal(t, "", ts0.Get(k1))
  511. assert.Equal(t, v0, ts1.Get(k0))
  512. assert.Equal(t, v1, ts1.Get(k1))
  513. // Update should not modify the original.
  514. v2 := "v2"
  515. ts2, err := ts1.Insert(k1, v2)
  516. require.NoError(t, err)
  517. assert.Equal(t, v0, ts0.Get(k0))
  518. assert.Equal(t, "", ts0.Get(k1))
  519. assert.Equal(t, v0, ts1.Get(k0))
  520. assert.Equal(t, v1, ts1.Get(k1))
  521. assert.Equal(t, v0, ts2.Get(k0))
  522. assert.Equal(t, v2, ts2.Get(k1))
  523. // Delete should not modify the original.
  524. ts3 := ts2.Delete(k0)
  525. assert.Equal(t, v0, ts0.Get(k0))
  526. assert.Equal(t, v0, ts1.Get(k0))
  527. assert.Equal(t, v0, ts2.Get(k0))
  528. assert.Equal(t, "", ts3.Get(k0))
  529. }