marty 2.4.1 → 2.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|