package nginx import ( "errors" "strings" "testing" ) func TestEachLineType(t *testing.T) { tests := []struct { Raw string Components []string Type LineType }{ {"key value;\n", []string{"key", "value"}, ASSIGNMENT}, {"key value;\n", []string{"key", "value"}, ASSIGNMENT}, {"key \t value;\n", []string{"key", "value"}, ASSIGNMENT}, {"key\tvalue;\n", []string{"key", "value"}, ASSIGNMENT}, {"ke-y value;\n", []string{"ke-y", "value"}, ASSIGNMENT}, {"ke_y value;\n", []string{"ke_y", "value"}, ASSIGNMENT}, {"key value; \n", []string{"key", "value"}, ASSIGNMENT}, {"key value;\t\n", []string{"key", "value"}, ASSIGNMENT}, {"\tkey value;\n", []string{"key", "value"}, ASSIGNMENT}, {" \t key value;\n", []string{"key", "value"}, ASSIGNMENT}, {"include ./file/*.conf;\n", []string{"./file/*.conf"}, INCLUDE}, {"include ./file/*.conf; \n", []string{"./file/*.conf"}, INCLUDE}, {"include ./file/*.conf;\t\n", []string{"./file/*.conf"}, INCLUDE}, {"\tinclude ./file/*.conf;\n", []string{"./file/*.conf"}, INCLUDE}, {" \t include ./file/*.conf;\n", []string{"./file/*.conf"}, INCLUDE}, {"sectionname {\n}\n", []string{"sectionname"}, SECTION}, {"section-name {\n}\n", []string{"section-name"}, SECTION}, {"section_name {\n}\n", []string{"section_name"}, SECTION}, {"sectionname { \n}\n", []string{"sectionname"}, SECTION}, {"sectionname {\t\n}\n", []string{"sectionname"}, SECTION}, {"sectionname ~ with-args {\t\n}\n", []string{"sectionname", "~ with-args"}, SECTION}, {"\tsectionname {\n}\n", []string{"sectionname"}, SECTION}, {" \t sectionname {\n}\n", []string{"sectionname"}, SECTION}, {"#some comment\n", []string{"some comment"}, COMMENT}, {"#some\tcomment\n", []string{"some\tcomment"}, COMMENT}, {"# some comment \n", []string{" some comment"}, COMMENT}, {"# some comment \t\n", []string{" some comment"}, COMMENT}, {"\t# some comment {\n", []string{" some comment {"}, COMMENT}, {";some comment\n", []string{"some comment"}, COLONCOMMENT}, {"; some\tcomment\n", []string{" some\tcomment"}, COLONCOMMENT}, {"; some comment\n", []string{" some comment"}, COLONCOMMENT}, {"; some comment \n", []string{" some comment"}, COLONCOMMENT}, {"; some comment \t\n", []string{" some comment"}, COLONCOMMENT}, {"\t; some comment {\n", []string{" some comment {"}, COLONCOMMENT}, } for i, test := range tests { // 1. create reader decoder := NewDecoder(strings.NewReader(test.Raw)) // 2. Decode receiver := new(Line) err := decoder.Decode(receiver) if err != nil { t.Errorf("[%d] unexpected error <%s>", i, err) continue } if len(receiver.Lines) != 1 { t.Errorf("[%d] expected only 1 element, got %d", i, len(receiver.Lines)) continue } if receiver.Lines[0].Type != test.Type { t.Errorf("[%d] expected type %d, got %d", i, test.Type, receiver.Lines[0].Type) continue } if receiver.Lines[0].Components == nil && test.Components != nil { t.Errorf("[%d] expected components not to be null", i) continue } if len(receiver.Lines[0].Components) != len(test.Components) { t.Errorf("[%d] expected %d components, got %d", i, len(test.Components), len(receiver.Lines[0].Components)) continue } // check each component individually for c, comp := range receiver.Lines[0].Components { if comp != test.Components[c] { t.Errorf("[%d] expected component %d to be '%s', got '%s'", i, c, test.Components[c], comp) } } } } func TestNestedSections(t *testing.T) { tests := []struct { Raw string SectionChain []string }{ {"a{\n}\n", []string{"a"}}, {"a{\n\n}\n", []string{"a"}}, {"a{\n; some comment\n}\n", []string{"a"}}, {"a{\n; some comment\n #another comment\n}\n", []string{"a"}}, {"a{\nb{\n}\n}\n", []string{"a", "b"}}, {"a{\n\tb{\n\t}\n}\n", []string{"a", "b"}}, {"a{\n\tb{\n\t\tc{\n\t\t}\n\t}\n}\n", []string{"a", "b", "c"}}, } for i, test := range tests { // 1. create reader decoder := NewDecoder(strings.NewReader(test.Raw)) // 2. Decode receiver := new(Line) err := decoder.Decode(receiver) if err != nil { t.Errorf("[%d] unexpected error <%s>", i, err) continue } if len(receiver.Lines) != 1 { t.Errorf("[%d] expected only 1 element, got %d", i, len(receiver.Lines)) continue } if receiver.Lines[0].Type != SECTION { t.Errorf("[%d] expected type %d, got %d", i, SECTION, receiver.Lines[0].Type) continue } // check each component individually current := receiver.Lines for s, sec := range test.SectionChain { // check that section exists in found := false for _, line := range current { // found if line.Type == SECTION && line.Components[0] == sec { found = true current = line.Lines break } } if !found { t.Errorf("[%d] key '%s' not found\n", i, strings.Join(test.SectionChain[:s+1], "/")) break } } } } func TestReceiverAndSyntaxErrors(t *testing.T) { tests := []struct { Receiver interface{} Input string Err error }{ {new(Line), "", nil}, {nil, "", ErrNullReceiver}, {[]byte{}, "", ErrInvalidReceiver}, {new(Line), "}", &LineError{0, ErrUnexpectedSectionClose}}, {new(Line), "key valuewithoutsemicolon", &LineError{0, ErrInvalidSyntax}}, {new(Line), "section {\nkey value;", ErrUnclosedSection}, } for i, test := range tests { // create reader r := strings.NewReader(test.Input) // parse input var receiver interface{} = test.Receiver decoder := NewDecoder(r) err := decoder.Decode(receiver) if err == nil { if test.Err != nil { t.Errorf("[%d] expected error", i) } continue } if err.Error() != test.Err.Error() { t.Errorf("[%d] expected error <%s>, got <%s>", i, test.Err, err) continue } } } var readerError = errors.New("error") type defectiveReader struct{} func (d defectiveReader) Read(buf []byte) (int, error) { return 0, readerError } func TestReadErrors(t *testing.T) { // create reader r := &defectiveReader{} // parse input receiver := new(Line) decoder := NewDecoder(r) err := decoder.Decode(receiver) if err == nil { t.Fatalf("expected error") } if err.Error() != (&LineError{0, readerError}).Error() { t.Fatalf("expected error <%s>, got <%s>", &LineError{0, readerError}, err) } }