github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/metrics/pipeline/type_test.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package pipeline 22 23 import ( 24 "encoding/json" 25 "testing" 26 27 "github.com/m3db/m3/src/metrics/aggregation" 28 "github.com/m3db/m3/src/metrics/generated/proto/pipelinepb" 29 "github.com/m3db/m3/src/metrics/generated/proto/transformationpb" 30 "github.com/m3db/m3/src/metrics/transformation" 31 "github.com/m3db/m3/src/metrics/x/bytes" 32 "github.com/m3db/m3/src/x/test/testmarshal" 33 34 "github.com/stretchr/testify/require" 35 "gopkg.in/yaml.v2" 36 ) 37 38 var ( 39 testTransformationOp = TransformationOp{ 40 Type: transformation.PerSecond, 41 } 42 testTransformationOpProto = pipelinepb.TransformationOp{ 43 Type: transformationpb.TransformationType_PERSECOND, 44 } 45 testBadTransformationOpProto = pipelinepb.TransformationOp{ 46 Type: transformationpb.TransformationType_UNKNOWN, 47 } 48 ) 49 50 func TestAggregationOpEqual(t *testing.T) { 51 inputs := []struct { 52 a1 AggregationOp 53 a2 AggregationOp 54 expected bool 55 }{ 56 { 57 a1: AggregationOp{aggregation.Count}, 58 a2: AggregationOp{aggregation.Count}, 59 expected: true, 60 }, 61 { 62 a1: AggregationOp{aggregation.Count}, 63 a2: AggregationOp{aggregation.Sum}, 64 expected: false, 65 }, 66 } 67 68 for _, input := range inputs { 69 require.Equal(t, input.expected, input.a1.Equal(input.a2)) 70 require.Equal(t, input.expected, input.a2.Equal(input.a1)) 71 } 72 } 73 74 func TestAggregationOpMarshalling(t *testing.T) { 75 examples := []AggregationOp{{aggregation.Count}} 76 77 t.Run("roundtrips", func(t *testing.T) { 78 testmarshal.TestMarshalersRoundtrip(t, examples, []testmarshal.Marshaler{testmarshal.JSONMarshaler, testmarshal.YAMLMarshaler, testmarshal.TextMarshaler}) 79 }) 80 81 t.Run("marshals", func(t *testing.T) { 82 cases := []struct { 83 Example AggregationOp 84 YAML string 85 JSON string 86 Text string 87 }{{ 88 Example: AggregationOp{aggregation.Count}, 89 90 Text: "Count", 91 JSON: `"Count"`, 92 YAML: "Count\n", 93 }} 94 95 t.Run("text", func(t *testing.T) { 96 for _, tc := range cases { 97 testmarshal.Require(t, testmarshal.AssertUnmarshals(t, testmarshal.TextMarshaler, tc.Example, []byte(tc.Text))) 98 testmarshal.Require(t, testmarshal.AssertMarshals(t, testmarshal.TextMarshaler, tc.Example, []byte(tc.Text))) 99 } 100 }) 101 102 t.Run("json", func(t *testing.T) { 103 for _, tc := range cases { 104 testmarshal.Require(t, testmarshal.AssertUnmarshals(t, testmarshal.JSONMarshaler, tc.Example, []byte(tc.JSON))) 105 testmarshal.Require(t, testmarshal.AssertMarshals(t, testmarshal.JSONMarshaler, tc.Example, []byte(tc.JSON))) 106 } 107 }) 108 109 t.Run("yaml", func(t *testing.T) { 110 for _, tc := range cases { 111 testmarshal.Require(t, testmarshal.AssertMarshals(t, testmarshal.YAMLMarshaler, tc.Example, []byte(tc.YAML))) 112 } 113 }) 114 }) 115 } 116 117 func TestTransformationOpEqual(t *testing.T) { 118 inputs := []struct { 119 a1 TransformationOp 120 a2 TransformationOp 121 expected bool 122 }{ 123 { 124 a1: TransformationOp{transformation.Absolute}, 125 a2: TransformationOp{transformation.Absolute}, 126 expected: true, 127 }, 128 { 129 a1: TransformationOp{transformation.Absolute}, 130 a2: TransformationOp{transformation.PerSecond}, 131 expected: false, 132 }, 133 } 134 135 for _, input := range inputs { 136 require.Equal(t, input.expected, input.a1.Equal(input.a2)) 137 require.Equal(t, input.expected, input.a2.Equal(input.a1)) 138 } 139 } 140 141 func TestTransformationOpClone(t *testing.T) { 142 source := TransformationOp{transformation.Absolute} 143 clone := source.Clone() 144 require.Equal(t, source, clone) 145 clone.Type = transformation.PerSecond 146 require.Equal(t, transformation.Absolute, source.Type) 147 } 148 149 func TestPipelineString(t *testing.T) { 150 inputs := []struct { 151 p Pipeline 152 expected string 153 }{ 154 { 155 p: Pipeline{ 156 operations: []OpUnion{ 157 { 158 Type: AggregationOpType, 159 Aggregation: AggregationOp{Type: aggregation.Last}, 160 }, 161 { 162 Type: TransformationOpType, 163 Transformation: TransformationOp{Type: transformation.PerSecond}, 164 }, 165 { 166 Type: RollupOpType, 167 Rollup: RollupOp{ 168 newName: b("foo"), 169 Tags: [][]byte{b("tag1"), b("tag2")}, 170 AggregationID: aggregation.MustCompressTypes(aggregation.Sum), 171 }, 172 }, 173 }, 174 }, 175 expected: "{operations: [{aggregation: Last}, {transformation: PerSecond}, " + 176 "{rollup: {name: foo, type: 0, tags: [tag1, tag2], aggregation: Sum}}]}", 177 }, 178 { 179 p: Pipeline{ 180 operations: []OpUnion{ 181 { 182 Type: OpType(10), 183 }, 184 }, 185 }, 186 expected: "{operations: [{unknown op type: OpType(10)}]}", 187 }, 188 } 189 190 for _, input := range inputs { 191 require.Equal(t, input.expected, input.p.String()) 192 } 193 } 194 195 func TestTransformationOpToProto(t *testing.T) { 196 var pb pipelinepb.TransformationOp 197 require.NoError(t, testTransformationOp.ToProto(&pb)) 198 require.Equal(t, testTransformationOpProto, pb) 199 } 200 201 func TestTransformationOpFromProto(t *testing.T) { 202 var res TransformationOp 203 require.NoError(t, res.FromProto(testTransformationOpProto)) 204 require.Equal(t, testTransformationOp, res) 205 } 206 207 func TestTransformationOpFromProtoBadProto(t *testing.T) { 208 var res TransformationOp 209 require.Error(t, res.FromProto(testBadTransformationOpProto)) 210 } 211 212 func TestTransformationOpRoundTrip(t *testing.T) { 213 var ( 214 pb pipelinepb.TransformationOp 215 res TransformationOp 216 ) 217 require.NoError(t, testTransformationOp.ToProto(&pb)) 218 require.NoError(t, res.FromProto(pb)) 219 require.Equal(t, testTransformationOp, res) 220 } 221 222 func TestRollupOpEqual(t *testing.T) { 223 inputs := []struct { 224 a1 RollupOp 225 a2 RollupOp 226 expected bool 227 }{ 228 { 229 a1: RollupOp{Type: GroupByRollupType}, 230 a2: RollupOp{Type: GroupByRollupType}, 231 expected: true, 232 }, 233 { 234 a1: RollupOp{Type: ExcludeByRollupType}, 235 a2: RollupOp{Type: ExcludeByRollupType}, 236 expected: true, 237 }, 238 { 239 a1: RollupOp{Type: GroupByRollupType}, 240 a2: RollupOp{Type: ExcludeByRollupType}, 241 expected: false, 242 }, 243 } 244 245 for _, input := range inputs { 246 require.Equal(t, input.expected, input.a1.Equal(input.a2)) 247 require.Equal(t, input.expected, input.a2.Equal(input.a1)) 248 } 249 } 250 251 func TestRollupOpSameTransform(t *testing.T) { 252 rollupOp := RollupOp{ 253 newName: b("foo"), 254 Tags: bs("bar1", "bar2"), 255 } 256 inputs := []struct { 257 op RollupOp 258 result bool 259 }{ 260 { 261 op: RollupOp{newName: b("foo"), Tags: bs("bar1", "bar2")}, 262 result: true, 263 }, 264 { 265 op: RollupOp{newName: b("foo"), Tags: bs("bar2", "bar1")}, 266 result: true, 267 }, 268 { 269 op: RollupOp{newName: b("foo"), Tags: bs("bar1")}, 270 result: false, 271 }, 272 { 273 op: RollupOp{newName: b("foo"), Tags: bs("bar1", "bar2", "bar3")}, 274 result: false, 275 }, 276 { 277 op: RollupOp{newName: b("foo"), Tags: bs("bar1", "bar3")}, 278 result: false, 279 }, 280 { 281 op: RollupOp{newName: b("baz"), Tags: bs("bar1", "bar2")}, 282 result: false, 283 }, 284 { 285 op: RollupOp{newName: b("baz"), Tags: bs("bar2", "bar1")}, 286 result: false, 287 }, 288 } 289 for _, input := range inputs { 290 require.Equal(t, input.result, rollupOp.SameTransform(input.op)) 291 } 292 } 293 294 func TestOpUnionMarshalJSON(t *testing.T) { 295 inputs := []struct { 296 op OpUnion 297 expected string 298 }{ 299 { 300 op: OpUnion{ 301 Type: AggregationOpType, 302 Aggregation: AggregationOp{Type: aggregation.Sum}, 303 }, 304 expected: `{"aggregation":"Sum"}`, 305 }, 306 { 307 op: OpUnion{ 308 Type: TransformationOpType, 309 Transformation: TransformationOp{Type: transformation.PerSecond}, 310 }, 311 expected: `{"transformation":"PerSecond"}`, 312 }, 313 { 314 op: OpUnion{ 315 Type: RollupOpType, 316 Rollup: RollupOp{ 317 Type: ExcludeByRollupType, 318 newName: b("testRollup"), 319 Tags: bs("tag1", "tag2"), 320 AggregationID: aggregation.MustCompressTypes(aggregation.Min, aggregation.Max), 321 }, 322 }, 323 expected: `{"rollup":{"type":1,"newName":"testRollup","tags":["tag1","tag2"],"aggregation":["Min","Max"]}}`, 324 }, 325 { 326 op: OpUnion{ 327 Type: RollupOpType, 328 Rollup: RollupOp{ 329 newName: b("testRollup"), 330 Tags: bs("tag1", "tag2"), 331 AggregationID: aggregation.DefaultID, 332 }, 333 }, 334 expected: `{"rollup":{"type":0,"newName":"testRollup","tags":["tag1","tag2"],"aggregation":null}}`, 335 }, 336 } 337 338 for _, input := range inputs { 339 b, err := json.Marshal(input.op) 340 require.NoError(t, err) 341 require.Equal(t, input.expected, string(b)) 342 } 343 } 344 345 func TestOpUnionMarshalJSONError(t *testing.T) { 346 op := OpUnion{} 347 _, err := json.Marshal(op) 348 require.Error(t, err) 349 } 350 351 func TestOpUnionMarshalRoundtrip(t *testing.T) { 352 ops := []OpUnion{ 353 { 354 Type: AggregationOpType, 355 Aggregation: AggregationOp{Type: aggregation.Sum}, 356 }, 357 { 358 Type: TransformationOpType, 359 Transformation: TransformationOp{Type: transformation.PerSecond}, 360 }, 361 { 362 Type: RollupOpType, 363 Rollup: RollupOp{ 364 newName: b("testRollup"), 365 Tags: bs("tag1", "tag2"), 366 AggregationID: aggregation.MustCompressTypes(aggregation.Min, aggregation.Max), 367 }, 368 }, 369 { 370 Type: RollupOpType, 371 Rollup: RollupOp{ 372 newName: b("testRollup"), 373 Tags: bs("tag1", "tag2"), 374 AggregationID: aggregation.DefaultID, 375 }, 376 }, 377 } 378 379 testmarshal.TestMarshalersRoundtrip(t, ops, []testmarshal.Marshaler{testmarshal.JSONMarshaler, testmarshal.YAMLMarshaler}) 380 } 381 382 func TestPipelineMarshalJSON(t *testing.T) { 383 p := NewPipeline([]OpUnion{ 384 { 385 Type: AggregationOpType, 386 Aggregation: AggregationOp{Type: aggregation.Sum}, 387 }, 388 { 389 Type: TransformationOpType, 390 Transformation: TransformationOp{Type: transformation.PerSecond}, 391 }, 392 { 393 Type: RollupOpType, 394 Rollup: RollupOp{ 395 newName: b("testRollup"), 396 Tags: bs("tag1", "tag2"), 397 AggregationID: aggregation.MustCompressTypes(aggregation.Min, aggregation.Max), 398 }, 399 }, 400 { 401 Type: RollupOpType, 402 Rollup: RollupOp{ 403 newName: b("testRollup"), 404 Tags: bs("tag1", "tag2"), 405 AggregationID: aggregation.DefaultID, 406 }, 407 }, 408 }) 409 b, err := json.Marshal(p) 410 require.NoError(t, err) 411 412 expected := `[{"aggregation":"Sum"},` + 413 `{"transformation":"PerSecond"},` + 414 `{"rollup":{"type":0,"newName":"testRollup","tags":["tag1","tag2"],"aggregation":["Min","Max"]}},` + 415 `{"rollup":{"type":0,"newName":"testRollup","tags":["tag1","tag2"],"aggregation":null}}]` 416 require.Equal(t, expected, string(b)) 417 } 418 419 func TestPipelineMarshalRoundtrip(t *testing.T) { 420 p := NewPipeline([]OpUnion{ 421 { 422 Type: AggregationOpType, 423 Aggregation: AggregationOp{Type: aggregation.Sum}, 424 }, 425 { 426 Type: TransformationOpType, 427 Transformation: TransformationOp{Type: transformation.PerSecond}, 428 }, 429 { 430 Type: RollupOpType, 431 Rollup: RollupOp{ 432 newName: b("testRollup"), 433 Tags: bs("tag1", "tag2"), 434 AggregationID: aggregation.MustCompressTypes(aggregation.Min, aggregation.Max), 435 }, 436 }, 437 { 438 Type: RollupOpType, 439 Rollup: RollupOp{ 440 newName: b("testRollup"), 441 Tags: bs("tag1", "tag2"), 442 AggregationID: aggregation.DefaultID, 443 }, 444 }, 445 }) 446 447 testmarshal.TestMarshalersRoundtrip(t, []Pipeline{p}, []testmarshal.Marshaler{testmarshal.YAMLMarshaler, testmarshal.JSONMarshaler}) 448 } 449 450 func TestPipelineUnmarshalYAML(t *testing.T) { 451 input := ` 452 - aggregation: Sum 453 - transformation: PerSecond 454 - rollup: 455 newName: testRollup 456 tags: 457 - tag1 458 - tag2 459 aggregation: 460 - Min 461 - Max 462 - rollup: 463 newName: testRollup2 464 tags: 465 - tag3 466 - tag4 467 ` 468 469 var pipeline Pipeline 470 require.NoError(t, yaml.Unmarshal([]byte(input), &pipeline)) 471 472 expected := NewPipeline([]OpUnion{ 473 { 474 Type: AggregationOpType, 475 Aggregation: AggregationOp{Type: aggregation.Sum}, 476 }, 477 { 478 Type: TransformationOpType, 479 Transformation: TransformationOp{Type: transformation.PerSecond}, 480 }, 481 { 482 Type: RollupOpType, 483 Rollup: RollupOp{ 484 newName: b("testRollup"), 485 Tags: bs("tag1", "tag2"), 486 AggregationID: aggregation.MustCompressTypes(aggregation.Min, aggregation.Max), 487 }, 488 }, 489 { 490 Type: RollupOpType, 491 Rollup: RollupOp{ 492 newName: b("testRollup2"), 493 Tags: bs("tag3", "tag4"), 494 AggregationID: aggregation.DefaultID, 495 }, 496 }, 497 }) 498 require.Equal(t, expected, pipeline) 499 } 500 501 func b(v string) []byte { return []byte(v) } 502 func bs(v ...string) [][]byte { return bytes.ArraysFromStringArray(v) }