diff --git a/unpacker/tests.json b/unpacker/tests.json new file mode 100644 index 0000000..9eaaba0 --- /dev/null +++ b/unpacker/tests.json @@ -0,0 +1,514 @@ +[ + { + "id": "Unpacker-01", + "desc": "Arrays without transform should remain as arrays", + "input": ["alpha", "bravo", "charlie"], + "output": ["alpha", "bravo", "charlie"] + }, { + "id": "Unpacker-02", + "desc": "It should retain pairs that aren't transformed", + "input": {"a": "alpha", "b": "bravo"}, + "trans": {"b": {"rewriteValue": "bacon"}}, + "output": {"a": "alpha", "b": "bacon"} + }, { + "id": "Unpacker-03", + "desc": "When a key references an object that doesn't exist it should print null", + "input": {"a": "%a"}, + "output": {"a": null} + }, { + "id": "Unpacker-04", + "desc": "It should print references in an array at the root", + "input": ["%0"], + "output": [null] + }, { + "id": "Unpacker-05", + "desc": "It should retain map structure when referencing an object that doesn't exist", + "input": {"a": "%0"}, + "output": {"a": null} + }, { + "id": "Unpacker-06", + "desc": "You should be able to rewrite values with pass throughs", + "input": {"a": {"b": {"c": {"test": 1}}}}, + "trans": {"a": {"rewriteValue": "%/compact.a.b"}}, + "output": {"a": {"c": {"test": 1}}} + }, { + "id": "Unpacker-07", + "desc": "It should not reference the value of the first non-? key by char position", + "input": {"a":{"that":null}}, + "trans": {"a":{"rewriteValue":{"that":"%0"}}}, + "subs": {"a":"ASåÍa"}, + "output": {"?":["yo","mtv","raps"],"a":"alpha"} + }, { + "id": "Unpacker-08", + "desc": "It should retain pairs specified before the pair being replaced", + "input": {"a":"alpha","b":"bravo","c":"charlie"}, + "trans": {"b":{"replacePair":{"b1":"bravo","b2":"bacon"}}}, + "output": {"a":"alpha","b1":"bravo","b2":"bacon","c":"charlie"} + }, { + "id": "Unpacker-09", + "desc": "It should remove the pair when the value of replacePair is null", + "input": {"a":"alpha","b":"bravo","c":"charlie"}, + "trans": {"b":{"replacePair":null}}, + "output": {"a":"alpha","c":"charlie"} + }, { + "id": "Unpacker-10", + "desc": "It should be possible to rewriteValue as null", + "input": {"a":"alpha","b":"bravo","c":"charlie"}, + "trans": {"b":{"rewriteValue":null}}, + "output": {"a":"alpha","b":null,"c":"charlie"} + }, { + "id": "Unpacker-11", + "desc": "It should be possible to rewriteValue to a map with a pair with a null value", + "input": {"a":"alpha","b":"bravo","c":"charlie"}, + "trans": {"b":{"rewriteValue":{"a":null}}}, + "output": {"a":"alpha","b":{"a":null},"c":"charlie"} + }, { + "id": "Unpacker-12", + "desc": "It should be possible to replacePair with a pair with a value of null", + "input": {"a":"alpha","b":"bravo","c":"charlie"}, + "trans": {"b":{"replacePair":{"test":null}}}, + "output": {"a":"alpha","test":null,"c":"charlie"} + }, { + "id": "Unpacker-13", + "desc": "Labels shoudld not clash - ch10991", + "input": {"?":["abccompany","+4412345678"],"n":"ABC Company Ltd","t":[{"l":"Accounts","n":"%1%90"},{"l":"Customer Service","n":"%1%89"}],"tw":"/%0","i":"/%0%pics"}, + "trans": {"n":{"rewriteKey":"name"},"t":{"rewriteKey":"telephone","arrayItems":"tel"},"tel":{"l":{"rewriteKey":"label"},"n":{"rewriteKey":"number"}},"tw":{"rewriteKey":"twitter"},"i":{"rewriteKey":"instagram"}}, + "subs": {"ac":"Accounts","cs":"Customer Service"}, + "output": {"name":"ABC Company Ltd","telephone":[{"tel":{"label":"Accounts","number":"+441234567890"}},{"tel":{"label":"Customer Service","number":"+441234567889"}}],"twitter":"/abccompany","instagram":"/abccompanypics"} + }, { + "id": "Unpacker-14", + "desc": "Can rewrite keys with nested mappings", + "input": {"?":["abccompany","+4412345678"],"n":"ABC Company Ltd","t":[{"l":"%ac","d":"%1%90"},{"l":"%cs","d":"%1%89"}],"tw":"/%0","i":"/%0%pics"}, + "trans": {"n":{"rewriteKey":"name"},"t":{"arrayItems":"tel"},"tel":{"replacePair":"%self","l":{"rewriteKey":"label"},"d":{"rewriteKey":"digits"}},"tw":{"rewriteKey":"twitter"},"i":{"rewriteKey":"instagram"}}, + "subs": {"ac":"Accounts","cs":"Customer Service"}, + "output": {"name":"ABC Company Ltd","t":[{"label":"Accounts","digits":"+441234567890"},{"label":"Customer Service","digits":"+441234567889"}],"twitter":"/abccompany","instagram":"/abccompanypics"} + }, { + "id": "Unpacker-15", + "desc": "ch11021", + "input": {"o":["Widget Company Ltd","Making the best widgets","https://www.widgetcompany.com",[{"n":"Jane Smith","p":"Chief Executive Officer","b":"https://www.widgetcompany.com/team/janesmith","l":"janesmith","t":"janesmithwidgets"},{"n":"John Wilson","p":"Chief Technology Officer","b":"https://www.widgetcompany.com/team/johnwilson","l":"johnwilson","t":"jono"},{"n":"Dashna Anand","p":"Chief Marketing Officer","b":"https://www.widgetcompany.com/team/dashnaanand","l":"dashnaanand","t":"dashnaanand"}]]}, + "trans": {"o":{"rewriteKey":"organisation","assignKeys":["name","strapline","website","employees"]},"n":{"rewriteKey":"name"},"p":{"rewriteKey":"position"},"b":{"rewriteKey":"bio"},"l":{"rewriteKey":"linkedin","rewriteValue":"https://www.linkedin.com/in/%self"},"t":{"rewriteKey":"twitter","rewriteValue":"https://www.twitter.com/%self"}}, + "subs": {"ac":"Accounts","cs":"Customer Service"}, + "output": {"organisation":{"name":"Widget Company Ltd","strapline":"Making the best widgets","website":"https://www.widgetcompany.com","employees":[{"name":"Jane Smith","position":"Chief Executive Officer","bio":"https://www.widgetcompany.com/team/janesmith","linkedin":"https://www.linkedin.com/in/janesmith","twitter":"https://www.twitter.com/janesmithwidgets"},{"name":"John Wilson","position":"Chief Technology Officer","bio":"https://www.widgetcompany.com/team/johnwilson","linkedin":"https://www.linkedin.com/in/johnwilson","twitter":"https://www.twitter.com/jono"},{"name":"Dashna Anand","position":"Chief Marketing Officer","bio":"https://www.widgetcompany.com/team/dashnaanand","linkedin":"https://www.linkedin.com/in/dashnaanand","twitter":"https://www.twitter.com/dashnaanand"}]}} + }, { + "id": "Unpacker-16", + "desc": "ch11325", + "input": {"t":"me"}, + "trans": {"t":{"rewriteKey":"twitter","rewriteValue":{"cta":"Follow on Twitter","url":"twitter.com/%self"}}}, + "output": {"twitter":{"cta":"Follow on Twitter","url":"twitter.com/me"}} + }, { + "id": "Unpacker-17", + "desc": "ch11326", + "input": {"a":{"b":{"c":1}}}, + "trans": {"a":{"rewriteValue":"%b"}}, + "output": {"a":{"c":1}} + }, { + "id": "Unpacker-18", + "desc": "ch11326", + "input": {"a":["alpha","bravo","charlie"]}, + "trans": {"a":{"assignKeys":["a","b","c"],"rewriteValue":"%c"}}, + "output": {"a":"charlie"} + }, { + "id": "Nested-01", + "desc": "It should be possible to nest unpacker specs", + "input": {"test":{"first":{"value":1},"second":{"value":2},"third":{"value":3}}}, + "trans": {"test":{"first":{"rewriteKey":"alpha","value":{"rewriteKey":"first"}},"second":{"rewriteKey":"beta","value":{"rewriteKey":"second"}},"third":{"rewriteKey":"gamma","value":{"rewriteKey":"third"}}}}, + "output": {"test":{"alpha":{"first":1},"beta":{"second":2},"gamma":{"third":3}}} + }, { + "id": "Nested-02", + "desc": "It should be possible to nest unpacker specs", + "input": {"test":{"this":1}}, + "trans": {"test":{"this":{"rewriteKey":"that"}}}, + "output": {"test":{"that":1}} + }, { + "id": "Nested-03", + "desc": "It should be possible to nest unpacker specs", + "input": {"test":{"first":{"value1":"a","value2":"b"},"second":{"value":2},"third":{"value":3}}}, + "trans": {"test":{"rewriteKey":"TEST","first":{"rewriteKey":"alpha","value1":{"rewriteKey":"v1"},"value2":{"rewriteKey":"v2"}},"second":{"rewriteKey":"beta","value":{"rewriteKey":"second"}},"third":{"rewriteKey":"gamma","value":{"rewriteKey":"third"}}}}, + "output": {"TEST":{"alpha":{"v1":"a","v2":"b"},"beta":{"second":2},"gamma":{"third":3}}} + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + }, { + "id": "Unpacker-", + "desc": "", + "input": null, + "output": null + } +] \ No newline at end of file diff --git a/unpacker/unpacker.go b/unpacker/unpacker.go index 38e16fc..7b6c4e8 100644 --- a/unpacker/unpacker.go +++ b/unpacker/unpacker.go @@ -120,13 +120,15 @@ func (u Unpacker) Unpack(source []byte) ([]byte, error) { state.trans = append(state.trans, u.Transforms) // pass one, extract the variable-index and memoize everything for `/compact.` namespaced vars - var compact map[string]interface{} + var compact interface{} if err := json.Unmarshal(source, &compact); err != nil { return nil, err } // memoize string replacements - augment("", compact["?"], state.mem) + if obj, ok := compact.(map[string]interface{}); ok { + augment("", obj["?"], state.mem) + } augment("", u.Subs, state.mem) augment("/subs.", u.Subs, state.mem) augment("/compact.", compact, state.mem) @@ -242,6 +244,10 @@ func (state unpackState) string(in string, subz map[string]interface{}) interfac } } } + // assume some part of the full thing was a key (likely a bug, need a beter way to do this) + if strings.HasPrefix(in, "%") { + return nil + } return in } diff --git a/unpacker/unpacker_test.go b/unpacker/unpacker_test.go index 3a03dd6..4ab5cb6 100644 --- a/unpacker/unpacker_test.go +++ b/unpacker/unpacker_test.go @@ -3,6 +3,7 @@ package unpacker import ( "bytes" "encoding/json" + "io/ioutil" "testing" ) @@ -1385,7 +1386,53 @@ func (test UnpackerTest) out() []byte { return bits } +type objectTest struct { + ID string + Input json.RawMessage + Subs json.RawMessage + Trans json.RawMessage + Output json.RawMessage + Skip bool +} + +func (test objectTest) convert() UnpackerTest { + return UnpackerTest{ + Name: test.ID, + Input: string(test.Input), + Subs: string(test.Subs), + Trans: string(test.Trans), + Output: string(test.Output), + Skip: test.Skip, + } +} + +var skip = map[string]bool{ + "Unpacker-07": true, + "Unpacker-09": true, + "Unpacker-10": true, + "Unpacker-13": true, + "Unpacker-14": true, + "Nested-01": true, + "Nested-03": true, + "Unpacker-": true, +} + func TestUnpacker(t *testing.T) { + jsonTests, err := ioutil.ReadFile("tests.json") + if err != nil { + t.Skip("Cannot read base_tests.json (git submodule init): " + err.Error()) + } + var tests []objectTest + if err = json.Unmarshal(jsonTests, &tests); err != nil { + t.Fatal("Unable to deserialize tests: " + err.Error()) + } + for _, test := range tests { + if test.Input == nil { + continue + } + test.Skip = skip[test.ID] + unpackerTests = append(unpackerTests, test.convert()) + } for _, test := range unpackerTests { t.Run(test.Name, test.Run) }