marty 2.4.1 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/marty/json_schema.rb +64 -1
- data/lib/marty/version.rb +1 -1
- data/other/marty/api/base.rb +31 -6
- data/spec/controllers/rpc_controller_spec.rb +141 -9
- data/spec/fixtures/json/rpc_controller.json +175 -0
- data/spec/fixtures/misc/struct_compare_tests.txt +170 -0
- data/spec/lib/struct_compare_spec.rb +19 -0
- data/spec/support/structure_compare.rb +21 -12
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4069f0be0ac4faa38738a0aa5f1a76fa56fcbed5
|
4
|
+
data.tar.gz: 832fed4b13056de3a94fbcaebb9fa463fd475f0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57cd24df5c095785c4a22d8c801db32a1e504e773bf0d0edce681d53edd71c1b22e55925bcd1fe931dc71457d88b720ad1ad04f1010d50d07df0dfe93caf52e1
|
7
|
+
data.tar.gz: bbcb53ecee094328f5271f5f60b90daa4f606729b196389d0e2316cb14cb53318ef57b603626fca446521aea19a9f734b9b61f1014ec5a7c45e8f4b4bb05bcba
|
data/lib/marty/json_schema.rb
CHANGED
@@ -35,6 +35,69 @@ module Marty
|
|
35
35
|
end
|
36
36
|
|
37
37
|
JSON::Validator.register_validator(self.new)
|
38
|
-
end
|
39
38
|
|
39
|
+
def self.get_numbers(schema)
|
40
|
+
numbers = []
|
41
|
+
|
42
|
+
# traverse the schema, if we find a type: number, add to numbers []
|
43
|
+
trav = lambda { |tree, key, path=[]|
|
44
|
+
return tree.each do|k, v|
|
45
|
+
trav.call(v, k, path + [k])
|
46
|
+
end if tree.is_a?(Hash)
|
47
|
+
numbers << path[0..-2] if key == 'type' && tree == 'number'
|
48
|
+
}
|
49
|
+
trav.call(schema, nil, [])
|
50
|
+
|
51
|
+
# convert the array stuff [ie. "items", "properties"] to :array
|
52
|
+
numbers.map do |num|
|
53
|
+
num.delete("properties")
|
54
|
+
num.map{|n| n=="items" ? :array : n}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.fix_numbers(json, numbers)
|
59
|
+
|
60
|
+
# follow path to drill into json
|
61
|
+
drill = lambda {|tree, path|
|
62
|
+
return unless tree
|
63
|
+
key = path.first
|
64
|
+
val = val = tree.send(:[], key) unless key == :array
|
65
|
+
if key == :array
|
66
|
+
# if we are at an array of numbers, fix them
|
67
|
+
if path.length == 1
|
68
|
+
tree.each_with_index do |v, i|
|
69
|
+
tree[i] = v.to_f if v.is_a?(Numeric)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
# this is an array of object so continue to drill down
|
73
|
+
tree.each {|sub| drill.call(sub, path[1..-1])}
|
74
|
+
end
|
75
|
+
elsif path.length == 1
|
76
|
+
# fix a non array field
|
77
|
+
tree.send(:[]=, key, val.to_f) if val.is_a?(Numeric)
|
78
|
+
else
|
79
|
+
# continue drilling
|
80
|
+
drill.call(val, path[1..-1])
|
81
|
+
end
|
82
|
+
}
|
83
|
+
numbers.each {|number| drill.call(json, number)}
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.get_schema(tag, sname, node, attr)
|
87
|
+
begin
|
88
|
+
Marty::ScriptSet.new(tag).get_engine(sname+'Schemas').
|
89
|
+
evaluate(node, attr, {})
|
90
|
+
rescue => e
|
91
|
+
id = "#{sname}/#{node} attrs=#{attr}"
|
92
|
+
|
93
|
+
# the schema DL might not exist at all, or might not define the attr
|
94
|
+
# being requested
|
95
|
+
sch_not_found = ['No such script', "undefined method `#{attr}__D'",
|
96
|
+
"node #{node} is undefined"]
|
97
|
+
msg = sch_not_found.detect{|msg| e.message.starts_with?(msg)} ?
|
98
|
+
'Schema not defined' : "Problem with schema: #{e.message}"
|
99
|
+
return "Schema error for #{id}: #{msg}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
40
103
|
end
|
data/lib/marty/version.rb
CHANGED
data/other/marty/api/base.rb
CHANGED
@@ -30,6 +30,9 @@ class Marty::Api::Base
|
|
30
30
|
def self.after_evaluate api_params, result
|
31
31
|
end
|
32
32
|
|
33
|
+
@@numbers = {}
|
34
|
+
@@schemas = {}
|
35
|
+
|
33
36
|
def self.is_authorized? params
|
34
37
|
is_secured = Marty::ApiAuth.where(
|
35
38
|
script_name: params[:script],
|
@@ -47,16 +50,25 @@ class Marty::Api::Base
|
|
47
50
|
# prevent script evaluation from modifying passed in params
|
48
51
|
params = params.deep_dup
|
49
52
|
|
53
|
+
schema_key = [params[:tag], params[:script], params[:node], params[:attr]]
|
54
|
+
input_schema = nil
|
55
|
+
begin
|
56
|
+
# get_schema will either return a hash with the schema,
|
57
|
+
# or a string with the error
|
58
|
+
input_schema = @@schemas[schema_key] ||=
|
59
|
+
Marty::JsonSchema.get_schema(*schema_key)
|
60
|
+
rescue => e
|
61
|
+
return {error: e.message}
|
62
|
+
end
|
63
|
+
|
50
64
|
# validate input schema
|
51
65
|
if config[:input_validated]
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
return {error: e.message}
|
56
|
-
end
|
66
|
+
|
67
|
+
# must fail if schema not found or some other error
|
68
|
+
return {"error": input_schema} if input_schema.is_a?(String)
|
57
69
|
|
58
70
|
begin
|
59
|
-
res = SchemaValidator::validate_schema(
|
71
|
+
res = SchemaValidator::validate_schema(input_schema, params[:params])
|
60
72
|
rescue NameError
|
61
73
|
return {error: "Unrecognized PgEnum for attribute #{params[:attr]}"}
|
62
74
|
rescue => e
|
@@ -68,6 +80,19 @@ class Marty::Api::Base
|
|
68
80
|
schema_errors
|
69
81
|
end
|
70
82
|
|
83
|
+
# if schema was found
|
84
|
+
if input_schema.is_a?(Hash)
|
85
|
+
# fix numbers types
|
86
|
+
numbers = @@numbers[schema_key] ||=
|
87
|
+
Marty::JsonSchema.get_numbers(input_schema)
|
88
|
+
|
89
|
+
# modify params in place
|
90
|
+
Marty::JsonSchema.fix_numbers(params[:params], numbers)
|
91
|
+
elsif !input_schema.include?("Schema not defined")
|
92
|
+
# else if some error besides schema not defined, fail
|
93
|
+
return {error: input_schema}
|
94
|
+
end
|
95
|
+
|
71
96
|
# get script engine
|
72
97
|
begin
|
73
98
|
engine = Marty::ScriptSet.new(params[:tag]).get_engine(params[:script])
|
@@ -36,6 +36,21 @@ B: A
|
|
36
36
|
p =? 5
|
37
37
|
eof
|
38
38
|
|
39
|
+
sample_script3err = <<eof
|
40
|
+
A:
|
41
|
+
a = 2
|
42
|
+
p =?
|
43
|
+
c = a * 2
|
44
|
+
pc = p + c
|
45
|
+
lc = [pc, pc]
|
46
|
+
|
47
|
+
C: A
|
48
|
+
p =? 3
|
49
|
+
|
50
|
+
B: A
|
51
|
+
p =? 5
|
52
|
+
eof
|
53
|
+
|
39
54
|
sample_script4 = <<eof
|
40
55
|
import M3
|
41
56
|
A: M3::A
|
@@ -121,8 +136,37 @@ A:
|
|
121
136
|
else 'req3'
|
122
137
|
|
123
138
|
eof
|
124
|
-
|
139
|
+
sample_script11 = <<eof
|
140
|
+
A:
|
141
|
+
f1_string =?
|
142
|
+
f2_bool =?
|
143
|
+
f3_integer =?
|
144
|
+
f4_number =?
|
145
|
+
f5_number =?
|
146
|
+
f6_array =?
|
147
|
+
f7_array =?
|
148
|
+
|
149
|
+
v0 = _
|
150
|
+
v1 = {
|
151
|
+
"input": v0,
|
152
|
+
"calc1": f4_number / 7,
|
153
|
+
"calc1a": f5_number / 7,
|
154
|
+
"calc2": f3_integer / 7,
|
155
|
+
"calc3": [ f7 / 7 for f7 in f7_array],
|
156
|
+
"calc4": f6_array[0].f14_number / 7,
|
157
|
+
"calc5": f6_array[0].f13_integer / 7,
|
158
|
+
"calc6": [ f7 / 7 for f7 in f6_array[0].f16_array]
|
159
|
+
}
|
160
|
+
eof
|
125
161
|
script3_schema = <<eof
|
162
|
+
A:
|
163
|
+
pc = { "properties" : {
|
164
|
+
"p" : { "type" : "integer" },
|
165
|
+
}
|
166
|
+
}
|
167
|
+
eof
|
168
|
+
|
169
|
+
script3err_schema = <<eof
|
126
170
|
A:
|
127
171
|
pc = { "properties : {
|
128
172
|
"p" : { "type" : "integer" },
|
@@ -132,6 +176,7 @@ eof
|
|
132
176
|
|
133
177
|
script4_schema = <<eof
|
134
178
|
A:
|
179
|
+
a = {}
|
135
180
|
d = { "properties" : {
|
136
181
|
"p" : { "type" : "integer" },
|
137
182
|
}
|
@@ -333,7 +378,57 @@ A:
|
|
333
378
|
new_check
|
334
379
|
] }
|
335
380
|
eof
|
336
|
-
|
381
|
+
script11_schema = <<eof
|
382
|
+
A:
|
383
|
+
properties = {
|
384
|
+
"f1_string" : {"type": "string" },
|
385
|
+
"f2_bool" : {"type": "boolean" },
|
386
|
+
"f3_integer" : {"type": "integer" },
|
387
|
+
"f4_number" : {"type": "number" },
|
388
|
+
"f5_number" : {"type": "number", "minimum": 0 },
|
389
|
+
"f6_array" : {"type": "array",
|
390
|
+
"items": {
|
391
|
+
"type" : "object",
|
392
|
+
"properties": {
|
393
|
+
"f11_string" : {"type": "string" },
|
394
|
+
"f12_bool" : {"type": "boolean" },
|
395
|
+
"f13_integer" : {"type": "integer" },
|
396
|
+
"f14_number" : {"type": "number" },
|
397
|
+
"f15_number" : {"type": "number", "minimum": 0 },
|
398
|
+
"f16_array": {
|
399
|
+
"type" : "array",
|
400
|
+
"uniqueItems" : true,
|
401
|
+
"minItems" : 0,
|
402
|
+
"maxItems" : 1,
|
403
|
+
"items": {
|
404
|
+
"type" : "number",
|
405
|
+
"minimum" : 0,
|
406
|
+
"multipleOf" : 0.001,
|
407
|
+
"not": {
|
408
|
+
"type": "integer"
|
409
|
+
}}}}}},
|
410
|
+
"f7_array": {
|
411
|
+
"type" : "array",
|
412
|
+
"uniqueItems" : true,
|
413
|
+
"minItems" : 0,
|
414
|
+
"maxItems" : 1,
|
415
|
+
"items": {
|
416
|
+
"type" : "number",
|
417
|
+
"minimum" : 0,
|
418
|
+
"multipleOf" : 0.001,
|
419
|
+
"not": {
|
420
|
+
"type": "integer"
|
421
|
+
}}},
|
422
|
+
"f8_object": {
|
423
|
+
"type": "object",
|
424
|
+
"properties": {
|
425
|
+
"f81_number": {"type": "number"},
|
426
|
+
"f82_array": {"type" : "array",
|
427
|
+
"items": {"type": "number"}}}}}
|
428
|
+
|
429
|
+
v1 = { "properties": properties}
|
430
|
+
v0 = { "properties": properties}
|
431
|
+
eof
|
337
432
|
describe Marty::RpcController do
|
338
433
|
before(:each) {
|
339
434
|
@routes = Marty::Engine.routes
|
@@ -351,6 +446,7 @@ describe Marty::RpcController do
|
|
351
446
|
"M1" => sample_script,
|
352
447
|
"M2" => sample_script.gsub(/a/, "aa").gsub(/b/, "bb"),
|
353
448
|
"M3" => sample_script3,
|
449
|
+
"M3err" => sample_script3err,
|
354
450
|
"M4" => sample_script4,
|
355
451
|
"M5" => sample_script5,
|
356
452
|
"M6" => sample_script6,
|
@@ -358,7 +454,9 @@ describe Marty::RpcController do
|
|
358
454
|
"M8" => sample_script8,
|
359
455
|
"M9" => sample_script9,
|
360
456
|
"M10" => sample_script10,
|
457
|
+
"M11" => sample_script11,
|
361
458
|
"M3Schemas" => script3_schema,
|
459
|
+
"M3errSchemas" => script3err_schema,
|
362
460
|
"M4Schemas" => script4_schema,
|
363
461
|
"M5Schemas" => script5_schema,
|
364
462
|
"M6Schemas" => script6_schema,
|
@@ -366,6 +464,7 @@ describe Marty::RpcController do
|
|
366
464
|
"M8Schemas" => script8_schema,
|
367
465
|
"M9Schemas" => script9_schema,
|
368
466
|
"M10Schemas" => script10_schema,
|
467
|
+
"M11Schemas" => script11_schema,
|
369
468
|
}, Date.today + 1.minute)
|
370
469
|
|
371
470
|
@p1 = Marty::Posting.do_create("BASE", Date.today + 2.minute, 'a comment')
|
@@ -676,12 +775,12 @@ describe Marty::RpcController do
|
|
676
775
|
attrs: attr,
|
677
776
|
params: params
|
678
777
|
}
|
679
|
-
expect = "Schema error for M4/A attrs=h:
|
680
|
-
expect(response.body).to include(
|
778
|
+
expect = "error,Schema error for M4/A attrs=h: Schema not defined"
|
779
|
+
expect(response.body).to include(expect)
|
681
780
|
end
|
682
781
|
|
683
782
|
it "returns an error message on invalid schema" do
|
684
|
-
Marty::ApiConfig.create!(script: "
|
783
|
+
Marty::ApiConfig.create!(script: "M3err",
|
685
784
|
node: "A",
|
686
785
|
attr: nil,
|
687
786
|
logged: false,
|
@@ -690,13 +789,13 @@ describe Marty::RpcController do
|
|
690
789
|
params = {"p" => 5}.to_json
|
691
790
|
get 'evaluate', params: {
|
692
791
|
format: :csv,
|
693
|
-
script: "
|
792
|
+
script: "M3err",
|
694
793
|
node: "A",
|
695
794
|
attrs: attr,
|
696
795
|
params: params
|
697
796
|
}
|
698
|
-
expect = "Schema error for
|
699
|
-
"syntax error
|
797
|
+
expect = "Schema error for M3err/A attrs=pc: Problem with schema: "\
|
798
|
+
"syntax error M3errSchemas:2\r\n"
|
700
799
|
expect(response.body).to eq("error,#{expect}")
|
701
800
|
end
|
702
801
|
|
@@ -1132,6 +1231,39 @@ describe Marty::RpcController do
|
|
1132
1231
|
expect(response.body).to match(/"error":"Permission denied"/)
|
1133
1232
|
end
|
1134
1233
|
|
1234
|
+
it "should convert incoming ints in number fields to float" do
|
1235
|
+
api = Marty::ApiAuth.new
|
1236
|
+
api.app_name = 'TestApp'
|
1237
|
+
api.script_name = 'M11'
|
1238
|
+
api.save!
|
1239
|
+
p = File.expand_path('../../fixtures/json', __FILE__)
|
1240
|
+
f = "%s/%s" % [p, "rpc_controller.json"]
|
1241
|
+
begin
|
1242
|
+
tests = JSON.parse(File.read(f))
|
1243
|
+
rescue => e
|
1244
|
+
puts "Error parsing #{f}: #{e.message}"
|
1245
|
+
raise
|
1246
|
+
end
|
1247
|
+
aggregate_failures "input coercing" do
|
1248
|
+
tests.each_with_index do |t, idx|
|
1249
|
+
get 'evaluate', params: {
|
1250
|
+
format: :json,
|
1251
|
+
script: "M11",
|
1252
|
+
node: "A",
|
1253
|
+
attrs: t['attr'],
|
1254
|
+
api_key: api.api_key,
|
1255
|
+
params: t['request'].to_json
|
1256
|
+
}
|
1257
|
+
resp = JSON.parse(response.body)
|
1258
|
+
comp = struct_compare(resp, t['result'], {"float_int_nomatch"=>true})
|
1259
|
+
(puts "TEST=#{idx}\n#{comp}"; binding.pry) if comp &&
|
1260
|
+
ENV['PRY'] == 'true'
|
1261
|
+
|
1262
|
+
expect(comp).to be_nil, "input conversion test #{idx}:\n#{comp}"
|
1263
|
+
end
|
1264
|
+
end
|
1265
|
+
end
|
1266
|
+
|
1135
1267
|
context "conditional validation" do
|
1136
1268
|
before(:all) do
|
1137
1269
|
Marty::ApiConfig.create!(script: "M10",
|
@@ -1237,7 +1369,7 @@ describe Marty::RpcController do
|
|
1237
1369
|
|
1238
1370
|
it 'returns engine/tag lookup error if script not found' do
|
1239
1371
|
get :evaluate, params: {format: :json, script: 'M1', attrs: "e", tag: 'invalid'}
|
1240
|
-
expect(response.body).to match(/
|
1372
|
+
expect(response.body).to match(/bad tag identifier.*invalid/)
|
1241
1373
|
get :evaluate, params: {format: :json, script: 'Invalid', attrs: "e", tag: t1.name}
|
1242
1374
|
expect(response.body).to match(/"error":"Can't get engine:/)
|
1243
1375
|
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
[
|
2
|
+
{"attr": "v1",
|
3
|
+
"request": {
|
4
|
+
"f1_string" : "hi",
|
5
|
+
"f2_bool" : false,
|
6
|
+
"f3_integer" : 24,
|
7
|
+
"f4_number" : 24,
|
8
|
+
"f5_number" : 24.0,
|
9
|
+
"f6_array" : [
|
10
|
+
{"f11_string" : "bye",
|
11
|
+
"f12_bool" : true,
|
12
|
+
"f13_integer" : 24,
|
13
|
+
"f14_number" : 24,
|
14
|
+
"f15_number" : 24.0,
|
15
|
+
"f16_array" : [24,24,24.0]},
|
16
|
+
{"f11_string" : "yes",
|
17
|
+
"f12_bool" : true,
|
18
|
+
"f13_integer" : 24,
|
19
|
+
"f14_number" : 24,
|
20
|
+
"f15_number" : 24.0,
|
21
|
+
"f16_array" : [24]}],
|
22
|
+
"f7_array" : [24,24,24.0],
|
23
|
+
"f8_object": {"f81_number": 5}
|
24
|
+
},
|
25
|
+
"result": {
|
26
|
+
"input": {
|
27
|
+
"f1_string" : "hi",
|
28
|
+
"f2_bool" : false,
|
29
|
+
"f3_integer": 24,
|
30
|
+
"f4_number" : 24.0,
|
31
|
+
"f5_number" : 24.0,
|
32
|
+
"f6_array": [
|
33
|
+
{
|
34
|
+
"f11_string": "bye",
|
35
|
+
"f12_bool": true,
|
36
|
+
"f13_integer": 24,
|
37
|
+
"f14_number": 24.0,
|
38
|
+
"f15_number": 24.0,
|
39
|
+
"f16_array": [
|
40
|
+
24.0,
|
41
|
+
24.0,
|
42
|
+
24.0
|
43
|
+
]
|
44
|
+
},
|
45
|
+
{
|
46
|
+
"f11_string": "yes",
|
47
|
+
"f12_bool": true,
|
48
|
+
"f13_integer": 24,
|
49
|
+
"f14_number": 24.0,
|
50
|
+
"f15_number": 24.0,
|
51
|
+
"f16_array": [
|
52
|
+
24.0
|
53
|
+
]
|
54
|
+
}
|
55
|
+
],
|
56
|
+
"f7_array": [
|
57
|
+
24.0,
|
58
|
+
24.0,
|
59
|
+
24.0
|
60
|
+
],
|
61
|
+
"f8_object": {"f81_number": 5.0}
|
62
|
+
},
|
63
|
+
"calc1": 3.428571,
|
64
|
+
"calc1a": 3.428571,
|
65
|
+
"calc2": 3,
|
66
|
+
"calc3": [
|
67
|
+
3.428571,
|
68
|
+
3.428571,
|
69
|
+
3.428571
|
70
|
+
],
|
71
|
+
"calc4": 3.428571,
|
72
|
+
"calc5": 3,
|
73
|
+
"calc6": [
|
74
|
+
3.428571,
|
75
|
+
3.428571,
|
76
|
+
3.428571
|
77
|
+
]
|
78
|
+
}},
|
79
|
+
{"attr": "v1",
|
80
|
+
"request": {
|
81
|
+
"f1_string" : "hi",
|
82
|
+
"f2_bool" : false,
|
83
|
+
"f3_integer" : 1,
|
84
|
+
"f4_number" : 1,
|
85
|
+
"f5_number" : 1,
|
86
|
+
"f6_array" : [
|
87
|
+
{"f11_string" : "bye",
|
88
|
+
"f12_bool" : true,
|
89
|
+
"f13_integer" : 1,
|
90
|
+
"f14_number" : 1,
|
91
|
+
"f15_number" : 1,
|
92
|
+
"f16_array" : [1]}
|
93
|
+
],
|
94
|
+
"f7_array" : [1],
|
95
|
+
"f8_object": {"f82_array": [1]}
|
96
|
+
|
97
|
+
},
|
98
|
+
"result": {
|
99
|
+
"input": {
|
100
|
+
"f1_string": "hi",
|
101
|
+
"f2_bool": false,
|
102
|
+
"f3_integer": 1,
|
103
|
+
"f4_number": 1.0,
|
104
|
+
"f5_number": 1.0,
|
105
|
+
"f6_array": [
|
106
|
+
{
|
107
|
+
"f11_string": "bye",
|
108
|
+
"f12_bool": true,
|
109
|
+
"f13_integer": 1,
|
110
|
+
"f14_number": 1.0,
|
111
|
+
"f15_number": 1.0,
|
112
|
+
"f16_array": [
|
113
|
+
1.0
|
114
|
+
]
|
115
|
+
}],
|
116
|
+
"f7_array": [
|
117
|
+
1.0
|
118
|
+
],
|
119
|
+
"f8_object": {"f82_array": [1.0]}
|
120
|
+
},
|
121
|
+
"calc1": 0.142857,
|
122
|
+
"calc1a": 0.142857,
|
123
|
+
"calc2": 0,
|
124
|
+
"calc3": [
|
125
|
+
0.142857
|
126
|
+
],
|
127
|
+
"calc4": 0.142857,
|
128
|
+
"calc5": 0,
|
129
|
+
"calc6": [
|
130
|
+
0.142857
|
131
|
+
]
|
132
|
+
}},
|
133
|
+
{"attr": "v0",
|
134
|
+
"request": {"f4_number": 123},
|
135
|
+
"result": {"f4_number": 123.0}
|
136
|
+
},
|
137
|
+
{"attr": "v0",
|
138
|
+
"request": {"f7_array": [1,2.0,3]},
|
139
|
+
"result": {"f7_array": [1.0,2.0,3.0]}
|
140
|
+
},
|
141
|
+
{"attr": "v0",
|
142
|
+
"request": {"f6_array": [{"f16_array": [1]}]},
|
143
|
+
"result": {"f6_array": [{"f16_array": [1.0]}]}
|
144
|
+
},
|
145
|
+
{"attr": "v0",
|
146
|
+
"request": {"f6_array": [{"f16_array": []}],
|
147
|
+
"f7_array": []},
|
148
|
+
"result": {"f6_array": [{"f16_array": []}],
|
149
|
+
"f7_array": []}
|
150
|
+
},
|
151
|
+
{"attr": "v0",
|
152
|
+
"request": {},
|
153
|
+
"result": {}
|
154
|
+
},
|
155
|
+
{"attr": "v0",
|
156
|
+
"request": {"f_bool":true},
|
157
|
+
"result": {"f_bool":true}
|
158
|
+
},
|
159
|
+
{"attr": "v0",
|
160
|
+
"request": {"f4_number": "bad"},
|
161
|
+
"result": {"f4_number": "bad"}
|
162
|
+
},
|
163
|
+
{"attr": "v0",
|
164
|
+
"request": {"f7_array": [1,"bad",3]},
|
165
|
+
"result": {"f7_array": [1.0,"bad",3.0]}
|
166
|
+
},
|
167
|
+
{"attr": "v0",
|
168
|
+
"request": {"f4_number": true},
|
169
|
+
"result": {"f4_number": true}
|
170
|
+
},
|
171
|
+
{"attr": "v0",
|
172
|
+
"request": {"f7_array": [1,true,3]},
|
173
|
+
"result": {"f7_array": [1.0,true,3.0]}
|
174
|
+
}
|
175
|
+
]
|
@@ -0,0 +1,170 @@
|
|
1
|
+
[
|
2
|
+
{ "example_number": "0",
|
3
|
+
"v1" : [1],
|
4
|
+
"v2" : [1],
|
5
|
+
"res" : false
|
6
|
+
},
|
7
|
+
{ "example_number": "1",
|
8
|
+
"v1": [1],
|
9
|
+
"v2": [2],
|
10
|
+
"res": "path=[0] 1 != 2"
|
11
|
+
},
|
12
|
+
{ "example_number": "2",
|
13
|
+
"v1" : [1,2],
|
14
|
+
"v2" : [1,3],
|
15
|
+
"res" : "path=[1] 2 != 3"
|
16
|
+
},
|
17
|
+
{ "example_number": "3",
|
18
|
+
"v1": [1,[1,2]],
|
19
|
+
"v2": [1,[1]],
|
20
|
+
"res" : "path=[1] array size mismatch 2 != 1"
|
21
|
+
},
|
22
|
+
{ "example_number": "4",
|
23
|
+
"v1": [[1,2], "foo", { "abc" : [1,2] }],
|
24
|
+
"v2": [[1,2], "foo", { "abc" : [1,2] }],
|
25
|
+
"res" : false
|
26
|
+
},
|
27
|
+
{ "example_number": "5",
|
28
|
+
"v1": [[1,2], "foo", { "abc" : [1,2.0] }],
|
29
|
+
"v2": [[1,2], "foo", { "abc" : [1.000,2] }],
|
30
|
+
"res" : false
|
31
|
+
},
|
32
|
+
{ "example_number": "6",
|
33
|
+
"v1": [[1,2], "foo", { "abc" : [1,{"a": 2}] }],
|
34
|
+
"v2": [[1,2], "foo", { "abc" : [1,{"a": 2}] }],
|
35
|
+
"res" : false
|
36
|
+
},
|
37
|
+
{ "example_number": "7",
|
38
|
+
"v1": [[1,2], "foo", { "abc" : [1,{"a": 2}] }],
|
39
|
+
"v2": [[1,2], "foo", { "abc" : [1,{"a": 2, "b":3}] }],
|
40
|
+
"res" : "path=[2][\"abc\"][1] hash extra keys [\"b\"]"
|
41
|
+
},
|
42
|
+
{ "example_number": "8",
|
43
|
+
"v1": [[1,2], "foo", { "abc" : [1,{"a": 2}] }],
|
44
|
+
"v2": [[1,2], "foo", { "abc" : [1,{"b": 2}] }],
|
45
|
+
"res" : "path=[2][\"abc\"][1] hash extra keys [\"a\"]"
|
46
|
+
},
|
47
|
+
{ "example_number": "9",
|
48
|
+
"v1": [[[1,2,3], {"foo": "bar", "baz": true, "plugh": 2.434, "hash": { "value": [1,2] }}], 7, "value"],
|
49
|
+
"v2": [[[1,2,3], {"foo": "barz", "baz": true, "plugh": 2.434, "hash": { "value": [1,2] }}], 7, "value"],
|
50
|
+
"res": "path=[0][1][\"foo\"] bar != barz"
|
51
|
+
},
|
52
|
+
{ "example_number": "10",
|
53
|
+
"v1": [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
|
54
|
+
"v2": [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
|
55
|
+
"res": false
|
56
|
+
},
|
57
|
+
{ "example_number": "11",
|
58
|
+
"v1": [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
|
59
|
+
"v2": [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["one"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
|
60
|
+
"res": "path=[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0] class mismatch Integer String"
|
61
|
+
},
|
62
|
+
{ "example_number": "12",
|
63
|
+
"v1": [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
|
64
|
+
"v2": [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
|
65
|
+
"res": "path=[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0] class mismatch Array Integer"
|
66
|
+
},
|
67
|
+
{ "example_number": "13",
|
68
|
+
"v1": [{"a": 1, "b": 2, "c": 3, "d": 4},
|
69
|
+
{"a": 1, "b": 2, "c": 3, "d": 4},
|
70
|
+
{"a": 1, "b": 2, "c": 3, "d": 4},
|
71
|
+
{"a": 1, "b": 2, "c": 3, "d": 4}],
|
72
|
+
"v2": [{"a": 1, "b": 2, "c": 3, "d": 4},
|
73
|
+
{"a": 1, "b": 2, "c": 3, "d": 4},
|
74
|
+
{"a": 1, "b": 2, "c": 3, "d": 4}],
|
75
|
+
"res": "path= array size mismatch 4 != 3"
|
76
|
+
},
|
77
|
+
{ "example_number": "14",
|
78
|
+
"v1": [{"a": 1, "b": 2, "c": 3, "d": 4},
|
79
|
+
{"a": 1, "b": 2, "c": 3, "d": 4},
|
80
|
+
{"a": 1, "b": 2, "c": 3, "d": 4},
|
81
|
+
{"a": 1, "b": 2, "c": 3, "d": 4}],
|
82
|
+
"v2": [{"a": 1, "b": 2, "c": 3, "d": 4},
|
83
|
+
{"a": 1, "b": 2, "c": 3, "d": 4},
|
84
|
+
{"a": 1, "b": 2, "d": 4},
|
85
|
+
{"a": 1, "b": 2, "c": 3, "d": 4}],
|
86
|
+
"res": "path=[2] hash extra keys [\"c\"]"
|
87
|
+
},
|
88
|
+
{ "example_number": "15",
|
89
|
+
"v1": [{"a": [{"b": [{"c": [{"d": [1,2.00,3,4]}]}]}]}],
|
90
|
+
"v2": [{"a": [{"b": [{"c": [{"d": [1.00,2,3.00,4.00]}]}]}]}],
|
91
|
+
"res": false
|
92
|
+
},
|
93
|
+
{ "example_number": "16",
|
94
|
+
"v1": [{"a": [{"b": [{"c": [{"d": [1,2,3,4]}]}]}]}],
|
95
|
+
"v2": [{"a": [{"b": [{"c": [{"d": [1.00,2.10,3.00,4.00]}]}]}]}],
|
96
|
+
"res": "path=[0][\"a\"][0][\"b\"][0][\"c\"][0][\"d\"][1] 2 != 2.1"
|
97
|
+
},
|
98
|
+
{ "example_number": "17",
|
99
|
+
"v1": [{"a": [{"b": [{"c": [{"d": [1,2.10,3,4]}]}]}]}],
|
100
|
+
"v2": [{"a": [{"b": [{"c": [{"d": [1.00,2,3.00,4.00]}]}]}]}],
|
101
|
+
"res": "path=[0][\"a\"][0][\"b\"][0][\"c\"][0][\"d\"][1] 2.1 != 2"
|
102
|
+
},
|
103
|
+
{ "example_number": "18",
|
104
|
+
"v1": {"pi": 3.1415926, "e": 2.718281828459, "phi": 1.618034},
|
105
|
+
"v2": {"pi": 3.1415926, "e": 2.718281811111, "phi": 1.618034},
|
106
|
+
"res": false
|
107
|
+
},
|
108
|
+
{ "example_number": "19",
|
109
|
+
"v1": [[[1,2,3], {"foo": "bar", "baz": false, "plugh": 2.434, "hash": { "value": [1,2] }}], 7, "value"],
|
110
|
+
"v2": [[[1,2,3], {"foo": "bar", "baz": false, "plugh": 2.434, "hash": { "value": [1,2] }}], 7, "value"],
|
111
|
+
"res": false
|
112
|
+
},
|
113
|
+
{ "example_number": "20",
|
114
|
+
"v1": [[[1,2,3], {"foo": "bar", "baz": null, "plugh": 2.434, "hash": { "value": [1,2] }}], 7, "value"],
|
115
|
+
"v2": [[[1,2,3], {"foo": "bar", "baz": null, "plugh": 2.434, "hash": { "value": [1,2] }}], 7, "value"],
|
116
|
+
"res": false
|
117
|
+
},
|
118
|
+
{ "example_number": "21",
|
119
|
+
"v1": [[[1,2,3], {"foo": "bar", "baz": null, "plugh": 2.434, "hash": { "value": [1,2] }}], 7, "value"],
|
120
|
+
"v2": [[[1,2,3], {"foo": "bar", "baz": false, "plugh": 2.434, "hash": { "value": [1,2] }}], 7, "value"],
|
121
|
+
"res": "path=[0][1][\"baz\"] class mismatch NilClass FalseClass"
|
122
|
+
},
|
123
|
+
{ "example_number": "22",
|
124
|
+
"v1": [[[1,2,3], {"foo": "bar", "baz": true, "plugh": 2.434, "hash": { "value": [1,false] }}], 7, "value"],
|
125
|
+
"v2": [[[1,2,3], {"foo": "bar", "baz": true, "plugh": 2.434, "hash": { "value": [1,null] }}], 7, "value"],
|
126
|
+
"res": "path=[0][1][\"hash\"][\"value\"][1] class mismatch FalseClass NilClass"
|
127
|
+
},
|
128
|
+
{ "example_number": "23",
|
129
|
+
"v1": {"pi": 3.1415926, "e": 2.7182818, "phi": 1.618034},
|
130
|
+
"v2": {"pi": 3.1415926, "e": 2.7282818, "phi": 1.618034},
|
131
|
+
"res": "path=[\"e\"] 2.7182818 != 2.7282818"
|
132
|
+
},
|
133
|
+
{ "example_number": "24",
|
134
|
+
"v1": {"error": "Some error message returned"},
|
135
|
+
"v2": {"pi": 3.1415926, "e": 2.7282818, "phi": 1.618034},
|
136
|
+
"res": "path= hash extra keys [\"error\"]"
|
137
|
+
},
|
138
|
+
{ "example_number": "25",
|
139
|
+
"v1": {"pi": 3.1415926, "e": 2.7282818, "phi": 1.618034},
|
140
|
+
"v2": {"error": "Some error message returned"},
|
141
|
+
"res": "path= hash extra keys [\"pi\", \"e\", \"phi\"]"
|
142
|
+
},
|
143
|
+
{ "example_number": "26",
|
144
|
+
"v1": {"error": "Another error message returned"},
|
145
|
+
"v2": [{"pi": 3.1415926, "e": 2.7282818, "phi": 1.618034}],
|
146
|
+
"res": "Another error message returned"
|
147
|
+
},
|
148
|
+
{ "example_number": "27",
|
149
|
+
"v1": [{"pi": 3.1415926, "e": 2.7282818, "phi": 1.618034}],
|
150
|
+
"v2": {"error": "Yet another error message returned"},
|
151
|
+
"res": "Yet another error message returned"
|
152
|
+
},
|
153
|
+
{ "example_number": "28",
|
154
|
+
"v1": {"three": 3},
|
155
|
+
"v2": {"three": 3.0},
|
156
|
+
"res": false
|
157
|
+
},
|
158
|
+
{ "example_number": "29",
|
159
|
+
"v1": {"three": 3},
|
160
|
+
"v2": {"three": 3.0},
|
161
|
+
"res": "path=[\"three\"] class mismatch Integer Float",
|
162
|
+
"cmp_opts": {"float_int_nomatch": true}
|
163
|
+
},
|
164
|
+
{ "example_number": "30",
|
165
|
+
"v1": {"three": 3, "dummy": "hi", "dummy2": 1},
|
166
|
+
"v2": {"three": 3.0, "dummy": "bye", "dummy2": 2},
|
167
|
+
"res": false,
|
168
|
+
"cmp_opts": {"float_int_nomatch": false, "ignore": ["dummy", "dummy2"]}
|
169
|
+
}
|
170
|
+
]
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "StructCompare" do
|
4
|
+
it "compares correctly" do
|
5
|
+
p = File.expand_path('../../fixtures/misc', __FILE__)
|
6
|
+
fname = "%s/%s" % [p, 'struct_compare_tests.txt']
|
7
|
+
aggregate_failures 'struct_compare' do
|
8
|
+
data = JSON.parse(File.read(fname))
|
9
|
+
data.each do |ex|
|
10
|
+
args = [ex["v1"], ex["v2"], ex['cmp_opts']].compact
|
11
|
+
comp = struct_compare(*args)
|
12
|
+
comparison = comp.nil? ? false : comp
|
13
|
+
num = ex["example_number"]
|
14
|
+
binding.pry if comparison != ex['res']
|
15
|
+
expect(comparison).to eq(ex["res"]), "Test ##{num} failed: #{comparison}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Marty::RSpec::StructureCompare
|
2
|
-
def self.struct_compare_all(v1raw, v2raw, path=[], errs=[])
|
2
|
+
def self.struct_compare_all(v1raw, v2raw, key=nil, cmp_opts={}, path=[], errs=[])
|
3
3
|
pathstr = path.map(&:to_s).join
|
4
4
|
v1,v2 = [v1raw, v2raw].map { |v| v.class == ActiveSupport::TimeWithZone ?
|
5
5
|
DateTime.parse(v.to_s) : v }
|
@@ -12,18 +12,22 @@ module Marty::RSpec::StructureCompare
|
|
12
12
|
|
13
13
|
return errs + ["path=#{pathstr} class mismatch #{v1.class} #{v2.class}"] unless
|
14
14
|
v1.class == v2.class ||
|
15
|
-
[
|
15
|
+
(!cmp_opts["float_int_nomatch"] &&
|
16
|
+
[v1,v2].map(&:class).to_set == Set.new([Integer, Float]))
|
16
17
|
|
18
|
+
override = (cmp_opts["ignore"]||[]).include?(key)
|
17
19
|
case v1
|
18
20
|
when String
|
19
21
|
return errs + ["path=#{pathstr} #{v1} != #{v2}"] unless
|
20
22
|
v1 == v2 ||
|
21
23
|
Regexp.new('\A'+v1+'\z').match(v2) ||
|
22
|
-
Regexp.new('\A'+v2+'\z').match(v1)
|
24
|
+
Regexp.new('\A'+v2+'\z').match(v1) ||
|
25
|
+
override
|
23
26
|
when Integer, DateTime, TrueClass, FalseClass, NilClass
|
24
|
-
return errs + ["path=#{pathstr} #{v1} != #{v2}"] if v1 != v2
|
27
|
+
return errs + ["path=#{pathstr} #{v1} != #{v2}"] if v1 != v2 && !override
|
25
28
|
when Float
|
26
|
-
return errs + ["path=#{pathstr} #{v1} != #{v2}"] if
|
29
|
+
return errs + ["path=#{pathstr} #{v1} != #{v2}"] if
|
30
|
+
v1.round(6) != v2.round(6) && !override
|
27
31
|
when Hash
|
28
32
|
v1_v2, v2_v1 = v1.keys-v2.keys, v2.keys-v1.keys
|
29
33
|
|
@@ -31,13 +35,16 @@ module Marty::RSpec::StructureCompare
|
|
31
35
|
errs.append("path=#{pathstr} hash extra keys #{v2_v1}") unless v2_v1.empty?
|
32
36
|
|
33
37
|
return errs + v1.map do |childkey, childval|
|
34
|
-
struct_compare_all(childval, v2[childkey],
|
38
|
+
struct_compare_all(childval, v2[childkey], childkey, cmp_opts,
|
39
|
+
path + [[childkey]], [])
|
35
40
|
end.flatten
|
36
41
|
when Array
|
37
|
-
errs.append(
|
42
|
+
errs.append(
|
43
|
+
"path=#{pathstr} array size mismatch #{v1.size} != #{v2.size}") if
|
38
44
|
v1.size != v2.size
|
39
45
|
return errs + v1.each_with_index.map do |childval,index|
|
40
|
-
struct_compare_all(childval, v2[index], path + [[index]],
|
46
|
+
struct_compare_all(childval, v2[index], nil, cmp_opts, path + [[index]],
|
47
|
+
[])
|
41
48
|
end.flatten
|
42
49
|
else
|
43
50
|
raise "unhandled #{v1.class}"
|
@@ -46,16 +53,18 @@ module Marty::RSpec::StructureCompare
|
|
46
53
|
end
|
47
54
|
end
|
48
55
|
|
49
|
-
def struct_compare(v1raw, v2raw)
|
56
|
+
def struct_compare(v1raw, v2raw, cmp_opts={})
|
50
57
|
begin
|
51
|
-
res = Marty::RSpec::StructureCompare.struct_compare_all(v1raw, v2raw
|
58
|
+
res = Marty::RSpec::StructureCompare.struct_compare_all(v1raw, v2raw, nil,
|
59
|
+
cmp_opts).first
|
52
60
|
rescue => e
|
53
61
|
e.message
|
54
62
|
end
|
55
63
|
end
|
56
|
-
def struct_compare_all(v1raw, v2raw)
|
64
|
+
def struct_compare_all(v1raw, v2raw, cmp_opts={})
|
57
65
|
begin
|
58
|
-
Marty::RSpec::StructureCompare.struct_compare_all(v1raw, v2raw
|
66
|
+
Marty::RSpec::StructureCompare.struct_compare_all(v1raw, v2raw, nil,
|
67
|
+
cmp_opts)
|
59
68
|
rescue => e
|
60
69
|
e.message
|
61
70
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: marty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.4.
|
4
|
+
version: 2.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arman Bostani
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2018-11-
|
17
|
+
date: 2018-11-13 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: pg
|
@@ -1651,6 +1651,8 @@ files:
|
|
1651
1651
|
- spec/fixtures/csv/rule/DataGrid.csv
|
1652
1652
|
- spec/fixtures/csv/rule/MyRule.csv
|
1653
1653
|
- spec/fixtures/csv/rule/XyzRule.csv
|
1654
|
+
- spec/fixtures/json/rpc_controller.json
|
1655
|
+
- spec/fixtures/misc/struct_compare_tests.txt
|
1654
1656
|
- spec/fixtures/scripts/load_tests/script1.dl
|
1655
1657
|
- spec/fixtures/scripts/load_tests/script2.dl
|
1656
1658
|
- spec/job_helper.rb
|
@@ -1662,6 +1664,7 @@ files:
|
|
1662
1664
|
- spec/lib/mcfly_model_spec.rb
|
1663
1665
|
- spec/lib/migrations/vw_marty_postings.sql.expected
|
1664
1666
|
- spec/lib/migrations_spec.rb
|
1667
|
+
- spec/lib/struct_compare_spec.rb
|
1665
1668
|
- spec/lib/xl_spec.rb
|
1666
1669
|
- spec/lib/xl_styles_spec.rb
|
1667
1670
|
- spec/models/api_auth_spec.rb
|