| package hcl |
| |
| import ( |
| "fmt" |
| "reflect" |
| "testing" |
| |
| "github.com/davecgh/go-spew/spew" |
| ) |
| |
| func TestMergedBodiesContent(t *testing.T) { |
| tests := []struct { |
| Bodies []Body |
| Schema *BodySchema |
| Want *BodyContent |
| DiagCount int |
| }{ |
| { |
| []Body{}, |
| &BodySchema{}, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| }, |
| 0, |
| }, |
| { |
| []Body{}, |
| &BodySchema{ |
| Attributes: []AttributeSchema{ |
| { |
| Name: "name", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| }, |
| 0, |
| }, |
| { |
| []Body{}, |
| &BodySchema{ |
| Attributes: []AttributeSchema{ |
| { |
| Name: "name", |
| Required: true, |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| }, |
| 1, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| HasAttributes: []string{"name"}, |
| }, |
| }, |
| &BodySchema{ |
| Attributes: []AttributeSchema{ |
| { |
| Name: "name", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{ |
| "name": &Attribute{ |
| Name: "name", |
| }, |
| }, |
| }, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasAttributes: []string{"name"}, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasAttributes: []string{"name"}, |
| }, |
| }, |
| &BodySchema{ |
| Attributes: []AttributeSchema{ |
| { |
| Name: "name", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{ |
| "name": &Attribute{ |
| Name: "name", |
| NameRange: Range{Filename: "first"}, |
| }, |
| }, |
| }, |
| 1, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasAttributes: []string{"name"}, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasAttributes: []string{"age"}, |
| }, |
| }, |
| &BodySchema{ |
| Attributes: []AttributeSchema{ |
| { |
| Name: "name", |
| }, |
| { |
| Name: "age", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{ |
| "name": &Attribute{ |
| Name: "name", |
| NameRange: Range{Filename: "first"}, |
| }, |
| "age": &Attribute{ |
| Name: "age", |
| NameRange: Range{Filename: "second"}, |
| }, |
| }, |
| }, |
| 0, |
| }, |
| { |
| []Body{}, |
| &BodySchema{ |
| Blocks: []BlockHeaderSchema{ |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| }, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| HasBlocks: map[string]int{ |
| "pizza": 1, |
| }, |
| }, |
| }, |
| &BodySchema{ |
| Blocks: []BlockHeaderSchema{ |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| Blocks: Blocks{ |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| HasBlocks: map[string]int{ |
| "pizza": 2, |
| }, |
| }, |
| }, |
| &BodySchema{ |
| Blocks: []BlockHeaderSchema{ |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| Blocks: Blocks{ |
| { |
| Type: "pizza", |
| }, |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasBlocks: map[string]int{ |
| "pizza": 1, |
| }, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasBlocks: map[string]int{ |
| "pizza": 1, |
| }, |
| }, |
| }, |
| &BodySchema{ |
| Blocks: []BlockHeaderSchema{ |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| Blocks: Blocks{ |
| { |
| Type: "pizza", |
| DefRange: Range{Filename: "first"}, |
| }, |
| { |
| Type: "pizza", |
| DefRange: Range{Filename: "second"}, |
| }, |
| }, |
| }, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasBlocks: map[string]int{ |
| "pizza": 2, |
| }, |
| }, |
| }, |
| &BodySchema{ |
| Blocks: []BlockHeaderSchema{ |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| Blocks: Blocks{ |
| { |
| Type: "pizza", |
| DefRange: Range{Filename: "second"}, |
| }, |
| { |
| Type: "pizza", |
| DefRange: Range{Filename: "second"}, |
| }, |
| }, |
| }, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasBlocks: map[string]int{ |
| "pizza": 2, |
| }, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| }, |
| }, |
| &BodySchema{ |
| Blocks: []BlockHeaderSchema{ |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| Blocks: Blocks{ |
| { |
| Type: "pizza", |
| DefRange: Range{Filename: "first"}, |
| }, |
| { |
| Type: "pizza", |
| DefRange: Range{Filename: "first"}, |
| }, |
| }, |
| }, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| }, |
| }, |
| &BodySchema{ |
| Blocks: []BlockHeaderSchema{ |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| }, |
| 0, |
| }, |
| } |
| |
| for i, test := range tests { |
| t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { |
| merged := MergeBodies(test.Bodies) |
| got, diags := merged.Content(test.Schema) |
| |
| if len(diags) != test.DiagCount { |
| t.Errorf("Wrong number of diagnostics %d; want %d", len(diags), test.DiagCount) |
| for _, diag := range diags { |
| t.Logf(" - %s", diag) |
| } |
| } |
| |
| if !reflect.DeepEqual(got, test.Want) { |
| t.Errorf("wrong result\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.Want)) |
| } |
| }) |
| } |
| } |
| |
| func TestMergeBodiesPartialContent(t *testing.T) { |
| tests := []struct { |
| Bodies []Body |
| Schema *BodySchema |
| WantContent *BodyContent |
| WantRemain Body |
| DiagCount int |
| }{ |
| { |
| []Body{}, |
| &BodySchema{}, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| }, |
| mergedBodies{}, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasAttributes: []string{"name", "age"}, |
| }, |
| }, |
| &BodySchema{ |
| Attributes: []AttributeSchema{ |
| { |
| Name: "name", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{ |
| "name": &Attribute{ |
| Name: "name", |
| NameRange: Range{Filename: "first"}, |
| }, |
| }, |
| }, |
| mergedBodies{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasAttributes: []string{"age"}, |
| }, |
| }, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasAttributes: []string{"name", "age"}, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasAttributes: []string{"name", "pizza"}, |
| }, |
| }, |
| &BodySchema{ |
| Attributes: []AttributeSchema{ |
| { |
| Name: "name", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{ |
| "name": &Attribute{ |
| Name: "name", |
| NameRange: Range{Filename: "first"}, |
| }, |
| }, |
| }, |
| mergedBodies{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasAttributes: []string{"age"}, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasAttributes: []string{"pizza"}, |
| }, |
| }, |
| 1, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasAttributes: []string{"name", "age"}, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasAttributes: []string{"pizza", "soda"}, |
| }, |
| }, |
| &BodySchema{ |
| Attributes: []AttributeSchema{ |
| { |
| Name: "name", |
| }, |
| { |
| Name: "soda", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{ |
| "name": &Attribute{ |
| Name: "name", |
| NameRange: Range{Filename: "first"}, |
| }, |
| "soda": &Attribute{ |
| Name: "soda", |
| NameRange: Range{Filename: "second"}, |
| }, |
| }, |
| }, |
| mergedBodies{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasAttributes: []string{"age"}, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasAttributes: []string{"pizza"}, |
| }, |
| }, |
| 0, |
| }, |
| { |
| []Body{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasBlocks: map[string]int{ |
| "pizza": 1, |
| }, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasBlocks: map[string]int{ |
| "pizza": 1, |
| "soda": 2, |
| }, |
| }, |
| }, |
| &BodySchema{ |
| Blocks: []BlockHeaderSchema{ |
| { |
| Type: "pizza", |
| }, |
| }, |
| }, |
| &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| Blocks: Blocks{ |
| { |
| Type: "pizza", |
| DefRange: Range{Filename: "first"}, |
| }, |
| { |
| Type: "pizza", |
| DefRange: Range{Filename: "second"}, |
| }, |
| }, |
| }, |
| mergedBodies{ |
| &testMergedBodiesVictim{ |
| Name: "first", |
| HasAttributes: []string{}, |
| HasBlocks: map[string]int{}, |
| }, |
| &testMergedBodiesVictim{ |
| Name: "second", |
| HasAttributes: []string{}, |
| HasBlocks: map[string]int{ |
| "soda": 2, |
| }, |
| }, |
| }, |
| 0, |
| }, |
| } |
| |
| for i, test := range tests { |
| t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { |
| merged := MergeBodies(test.Bodies) |
| got, gotRemain, diags := merged.PartialContent(test.Schema) |
| |
| if len(diags) != test.DiagCount { |
| t.Errorf("Wrong number of diagnostics %d; want %d", len(diags), test.DiagCount) |
| for _, diag := range diags { |
| t.Logf(" - %s", diag) |
| } |
| } |
| |
| if !reflect.DeepEqual(got, test.WantContent) { |
| t.Errorf("wrong content result\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.WantContent)) |
| } |
| |
| if !reflect.DeepEqual(gotRemain, test.WantRemain) { |
| t.Errorf("wrong remaining result\ngot: %s\nwant: %s", spew.Sdump(gotRemain), spew.Sdump(test.WantRemain)) |
| } |
| }) |
| } |
| } |
| |
| type testMergedBodiesVictim struct { |
| Name string |
| HasAttributes []string |
| HasBlocks map[string]int |
| DiagCount int |
| } |
| |
| func (v *testMergedBodiesVictim) Content(schema *BodySchema) (*BodyContent, Diagnostics) { |
| c, _, d := v.PartialContent(schema) |
| return c, d |
| } |
| |
| func (v *testMergedBodiesVictim) PartialContent(schema *BodySchema) (*BodyContent, Body, Diagnostics) { |
| remain := &testMergedBodiesVictim{ |
| Name: v.Name, |
| HasAttributes: []string{}, |
| } |
| |
| hasAttrs := map[string]struct{}{} |
| for _, n := range v.HasAttributes { |
| hasAttrs[n] = struct{}{} |
| |
| var found bool |
| for _, attrS := range schema.Attributes { |
| if n == attrS.Name { |
| found = true |
| break |
| } |
| } |
| if !found { |
| remain.HasAttributes = append(remain.HasAttributes, n) |
| } |
| } |
| |
| content := &BodyContent{ |
| Attributes: map[string]*Attribute{}, |
| } |
| |
| rng := Range{ |
| Filename: v.Name, |
| } |
| |
| for _, attrS := range schema.Attributes { |
| _, has := hasAttrs[attrS.Name] |
| if has { |
| content.Attributes[attrS.Name] = &Attribute{ |
| Name: attrS.Name, |
| NameRange: rng, |
| } |
| } |
| } |
| |
| if v.HasBlocks != nil { |
| for _, blockS := range schema.Blocks { |
| num := v.HasBlocks[blockS.Type] |
| for i := 0; i < num; i++ { |
| content.Blocks = append(content.Blocks, &Block{ |
| Type: blockS.Type, |
| DefRange: rng, |
| }) |
| } |
| } |
| |
| remain.HasBlocks = map[string]int{} |
| for n := range v.HasBlocks { |
| var found bool |
| for _, blockS := range schema.Blocks { |
| if blockS.Type == n { |
| found = true |
| break |
| } |
| } |
| if !found { |
| remain.HasBlocks[n] = v.HasBlocks[n] |
| } |
| } |
| } |
| |
| diags := make(Diagnostics, v.DiagCount) |
| for i := range diags { |
| diags[i] = &Diagnostic{ |
| Severity: DiagError, |
| Summary: fmt.Sprintf("Fake diagnostic %d", i), |
| Detail: "For testing only.", |
| Context: &rng, |
| } |
| } |
| |
| return content, remain, diags |
| } |
| |
| func (v *testMergedBodiesVictim) JustAttributes() (Attributes, Diagnostics) { |
| attrs := make(map[string]*Attribute) |
| |
| rng := Range{ |
| Filename: v.Name, |
| } |
| |
| for _, name := range v.HasAttributes { |
| attrs[name] = &Attribute{ |
| Name: name, |
| NameRange: rng, |
| } |
| } |
| |
| diags := make(Diagnostics, v.DiagCount) |
| for i := range diags { |
| diags[i] = &Diagnostic{ |
| Severity: DiagError, |
| Summary: fmt.Sprintf("Fake diagnostic %d", i), |
| Detail: "For testing only.", |
| Context: &rng, |
| } |
| } |
| |
| return attrs, diags |
| } |
| |
| func (v *testMergedBodiesVictim) MissingItemRange() Range { |
| return Range{ |
| Filename: v.Name, |
| } |
| } |