package ast import ( "testing" ) func Test_ConstructSlightlyComplexStructure(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.setKeyValuePair(newKey("ding"), newItem(tInteger, 10)) p.setKeyValuePair(newKey("dong"), newItem(tString, "not a song")) p.openTable(newKey("key1", "key2 a")) p.setKeyValuePair(newKey("dooh"), newItem(tBoolean, true)) p.setKeyValuePair(newKey("dah"), newItem(tBoolean, false)) p.openTable(newKey("key1", "key2 b")) p.setKeyValuePair(newKey("dieh"), newItem(tFloat, 1.111)) p.setKeyValuePair(newKey("duh"), newItem(tFloat, 1.18e-12)) p.setKeyValuePair(newKey("foo", "bar"), newItem(tArrayOfTables, newItem(tInteger, 1), newItem(tInteger, 2))) p.openArrayOfTables(newKey("aaah", "table array")) p.setKeyValuePair(newKey("a"), newItem(tFloat, 1.234)) p.openArrayOfTables(newKey("aaah", "table array")) p.setKeyValuePair(newKey("b"), newItem(tFloat, 2.345)) p.setKeyValuePair(newKey("c"), newItem(tString, "bingo!")) p.openArrayOfTables(newKey("aaah", "table array")) return nil, p }, "", `{"aaah": {"table array": [{"a": 1.234}, {"b": 2.345, "c": "bingo!"}, {}]}, `+ `"ding": 10, "dong": "not a song", `+ `"key1": {"key2 a": {"dah": false, "dooh": true}, "key2 b": {"dieh": 1.111, "duh": 1.18e-12, "foo": {"bar": [1, 2]}}}}`) } func Test_EmptyKeyForCreatingTablePath_Panics(t *testing.T) { defer func() { r := recover() if r.(string) != "makeTablePath(): empty key provided; a key must have at least one key part" { t.Fatalf("Did not get the expected panic message") } }() p := newParser() p.openTable(newKey()) } func Test_StoreValueInRootTable(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.setKeyValuePair(newKey("key1"), newItem(tString, "value1")) return p.setKeyValuePair(newKey("key2"), newItem(tString, "value2")), p }, "", `{"key1": "value1", "key2": "value2"}`) } func Test_StoreValueWithMultipartKey_CreatesTableHierarchy(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() return p.setKeyValuePair(newKey("key1", "key2", "key3"), newItem(tString, "value")), p }, "", `{"key1": {"key2": {"key3": "value"}}}`) } func Test_StoreDuplicateKeyInRootTable_ReturnsError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.setKeyValuePair(newKey("key"), newItem(tString, "value")) return p.setKeyValuePair(newKey("key"), newItem(tInteger, 321)), p }, `invalid key/value pair: string item already exists at key [key]`, `{"key": "value"}`) } func Test_StoreValueWithMultipartKey_UnderSubtable_CreatesTableHierarchy(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.openTable(newKey("tablekey1", "tablekey2")) return p.setKeyValuePair(newKey("valuekey1", "valuekey2", "valuekey3"), newItem(tString, "value")), p }, "", `{"tablekey1": {"tablekey2": {"valuekey1": {"valuekey2": {"valuekey3": "value"}}}}}`) } func Test_StoreKeyPathWherePathContainsNonTableAlready_ReturnsError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.openTable(newKey("key1")) p.setKeyValuePair(newKey("key2"), newItem(tInteger, 0)) return p.setKeyValuePair(newKey("key2", "key3"), newItem(tString, "value")), p }, `invalid key/value pair: integer item already exists at key [key1->key2]`, `{"key1": {"key2": 0}}`) } func Test_GivenExistingTableAtKey_CreatingTableAtSameKey_ReturnsError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.openTable(newKey("key1", "key2")) return p.openTable(newKey("key1", "key2")), p }, `invalid table: table item already exists at key [key1->key2]`, `{"key1": {"key2": {}}}`) } func Test_GivenExistingItemAtKey_CreatingTableAtSameKey_ReturnsError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.setKeyValuePair(newKey("key"), newItem(tString, "value")) return p.openTable(newKey("key")), p }, `invalid table: string item already exists at key [key]`, `{"key": "value"}`) } func Test_GivenExistingItemInKeyPath_CreatingTable_ReturnsError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.setKeyValuePair(newKey("key"), newItem(tString, "value")) return p.openTable(newKey("key", "subkey")), p }, `invalid table: string item already exists at key [key]`, `{"key": "value"}`) } func Test_GivenExistingItemAtDeepKey_CreatingTableAtSameKey_ReturnsError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.openTable(newKey("deep", "table")) p.setKeyValuePair(newKey("key"), newItem(tInteger, 0)) return p.openTable(newKey("deep", "table", "key")), p }, `invalid table: integer item already exists at key [deep->table->key]`, `{"deep": {"table": {"key": 0}}}`) } func Test_GivenExistingItemAtDeepKeyFromSubTable_CreatingTableAtSameKey_ReturnsError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.openTable(newKey("deep", "table")) p.setKeyValuePair(newKey("key1", "key2"), newItem(tInteger, 0)) return p.setKeyValuePair(newKey("key1", "key2"), newItem(tBoolean, true)), p }, // This test mainly tests the formatting of [deep->table->key1->key2], being a concatenation // of the currently active table plus the multipart key for setKeyValuePair(). `invalid key/value pair: integer item already exists at key [deep->table->key1->key2]`, `{"deep": {"table": {"key1": {"key2": 0}}}}`) } func Test_FormattingOfQuotedPathPartInError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.openTable(newKey("must be quoted")) p.setKeyValuePair(newKey("this one too"), newItem(tInteger, 0)) return p.setKeyValuePair(newKey("this one too"), newItem(tInteger, 0)), p }, `invalid key/value pair: integer item already exists at key ["must be quoted"->"this one too"]`, `{"must be quoted": {"this one too": 0}}`) } func Test_GivenExistingItemAtKey_CreatingArrayOfTablesAtSameKey_ReturnsError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.Root["key"] = newItem(tString, "value") return p.openArrayOfTables(newKey("key")), p }, `invalid table array: string item already exists at key [key]`, `{"key": "value"}`) } func Test_GivenExistingItemInKeyPath_CreatingArrayOfTables_ReturnsError(t *testing.T) { testAST(t, func() (error, *parser) { p := newParser() p.Root["key"] = newItem(tString, "value") return p.openArrayOfTables(newKey("key", "subkey")), p }, `invalid table array: string item already exists at key [key]`, `{"key": "value"}`) } func testAST(t *testing.T, code func() (error, *parser), expectedError string, expectedData string) { err, p := code() if expectedError == "" { if err != nil { t.Fatalf("Unexpected error: %s", err) } } else { if err == nil { t.Fatalf("An error was expected, but no error was returned") } else if err.Error() != expectedError { t.Fatalf("Unexpected error:\nexpected: %s\nactual: %s\n", expectedError, err.Error()) } } if expectedData == "" { return } if expectedData != p.Root.String() { t.Fatalf("Unexpected data after parsing:\nexpected: %s\nactual: %s\n", expectedData, p.Root.String()) } }