wabur 0.2.0d1 → 0.4.0d1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +40 -14
  3. data/bin/wabur +103 -0
  4. data/lib/wab/controller.rb +133 -90
  5. data/lib/wab/data.rb +14 -27
  6. data/lib/wab/errors.rb +14 -8
  7. data/lib/wab/impl/bool_expr.rb +25 -0
  8. data/lib/wab/impl/configuration.rb +166 -0
  9. data/lib/wab/impl/data.rb +155 -232
  10. data/lib/wab/impl/expr.rb +26 -0
  11. data/lib/wab/impl/expr_parser.rb +55 -0
  12. data/lib/wab/impl/exprs/and.rb +29 -0
  13. data/lib/wab/impl/exprs/between.rb +41 -0
  14. data/lib/wab/impl/exprs/eq.rb +28 -0
  15. data/lib/wab/impl/exprs/gt.rb +30 -0
  16. data/lib/wab/impl/exprs/gte.rb +30 -0
  17. data/lib/wab/impl/exprs/has.rb +26 -0
  18. data/lib/wab/impl/exprs/in.rb +28 -0
  19. data/lib/wab/impl/exprs/lt.rb +30 -0
  20. data/lib/wab/impl/exprs/lte.rb +30 -0
  21. data/lib/wab/impl/exprs/not.rb +27 -0
  22. data/lib/wab/impl/exprs/or.rb +29 -0
  23. data/lib/wab/impl/exprs/regex.rb +28 -0
  24. data/lib/wab/impl/handler.rb +95 -0
  25. data/lib/wab/impl/model.rb +197 -0
  26. data/lib/wab/impl/path_expr.rb +14 -0
  27. data/lib/wab/impl/shell.rb +92 -7
  28. data/lib/wab/impl/utils.rb +110 -0
  29. data/lib/wab/impl.rb +24 -0
  30. data/lib/wab/io/call.rb +4 -7
  31. data/lib/wab/io/engine.rb +128 -51
  32. data/lib/wab/io/shell.rb +61 -64
  33. data/lib/wab/io.rb +0 -2
  34. data/lib/wab/open_controller.rb +43 -0
  35. data/lib/wab/shell.rb +46 -61
  36. data/lib/wab/shell_logger.rb +13 -0
  37. data/lib/wab/utils.rb +36 -0
  38. data/lib/wab/uuid.rb +3 -6
  39. data/lib/wab/version.rb +2 -2
  40. data/lib/wab.rb +3 -0
  41. data/pages/Plan.md +20 -14
  42. data/test/bench_io_shell.rb +49 -0
  43. data/test/{impl_test.rb → helper.rb} +2 -4
  44. data/test/mirror_controller.rb +16 -0
  45. data/test/test_configuration.rb +38 -0
  46. data/test/test_data.rb +207 -0
  47. data/test/test_expr.rb +35 -0
  48. data/test/test_expr_and.rb +24 -0
  49. data/test/test_expr_between.rb +43 -0
  50. data/test/test_expr_eq.rb +24 -0
  51. data/test/test_expr_gt.rb +24 -0
  52. data/test/test_expr_gte.rb +24 -0
  53. data/test/test_expr_has.rb +19 -0
  54. data/test/test_expr_in.rb +24 -0
  55. data/test/test_expr_lt.rb +24 -0
  56. data/test/test_expr_lte.rb +24 -0
  57. data/test/test_expr_not.rb +22 -0
  58. data/test/test_expr_or.rb +24 -0
  59. data/test/test_expr_regex.rb +30 -0
  60. data/test/test_impl.rb +38 -0
  61. data/test/test_io_shell.rb +189 -0
  62. data/test/test_model.rb +31 -0
  63. data/test/test_runner.rb +177 -0
  64. data/test/tests.rb +3 -8
  65. metadata +91 -18
  66. data/lib/wab/model.rb +0 -136
  67. data/lib/wab/view.rb +0 -21
  68. data/test/data_test.rb +0 -253
  69. data/test/ioshell_test.rb +0 -461
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'helper'
5
+
6
+ class TestExprNot < TestImpl
7
+
8
+ def test_not_native
9
+ x = WAB::Impl::Not.new(WAB::Impl::Eq.new('num', 7))
10
+ assert_equal(['NOT', ['EQ', 'num', 7]], x.native, 'NOT native mismatch')
11
+ end
12
+
13
+ def test_not
14
+ d = make_sample_data
15
+ x = WAB::Impl::Not.new(WAB::Impl::Eq.new('num', 8))
16
+ assert(x.eval(d), 'checking NOT match')
17
+
18
+ x = WAB::Impl::Not.new(WAB::Impl::Eq.new('num', 7))
19
+ refute(x.eval(d), 'checking NOT mismatch')
20
+ end
21
+
22
+ end # TestExprNot
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'helper'
5
+
6
+ class TestExprOr < TestImpl
7
+
8
+ def test_or_native
9
+ x = WAB::Impl::Or.new(WAB::Impl::Has.new('num'), WAB::Impl::Eq.new('num', 7))
10
+ assert_equal(['OR', ['HAS', 'num'], ['EQ', 'num', 7]], x.native, 'OR native mismatch')
11
+ end
12
+
13
+ def test_or
14
+ d = make_sample_data
15
+ x = WAB::Impl::Or.new(WAB::Impl::Has.new('str'), WAB::Impl::Eq.new('num', 8))
16
+ assert(x.eval(d), 'checking OR match')
17
+
18
+ x = WAB::Impl::Or.new(WAB::Impl::Has.new('none'), WAB::Impl::Eq.new('num', 8))
19
+ refute(x.eval(d), 'checking OR mismatch')
20
+ end
21
+
22
+ # TBD tests with no args, with one, with more than 2, test using append_arg
23
+
24
+ end # TestExprOr
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'helper'
5
+
6
+ require 'wab'
7
+ require 'wab/impl'
8
+
9
+ class TestExprRegex < TestImpl
10
+
11
+ def test_regex_native
12
+ x = WAB::Impl::Regex.new('str', '^a *.')
13
+ assert_equal(['REGEX', 'str', '^a *.'], x.native, 'REGEX native mismatch')
14
+ end
15
+
16
+ def test_regex_int
17
+ d = make_sample_data
18
+ x = WAB::Impl::Regex.new('str', '^a .*')
19
+ assert(x.eval(d), 'checking REGEX match with string arg')
20
+
21
+ x = WAB::Impl::Regex.new('str', '^x .*')
22
+ refute(x.eval(d), 'checking REGEX mismatch with string arg')
23
+
24
+ x = WAB::Impl::Regex.new('num', 8)
25
+ refute(x.eval(d), 'checking REGEX mismatch with an integer arg')
26
+ end
27
+
28
+ # TBD more tests for each type, Float, boolean, String, URI, UUID, Time, BigDecimal, nil
29
+
30
+ end # TestExprRegex
data/test/test_impl.rb ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'helper'
5
+
6
+ require 'wab/impl'
7
+
8
+ class TestImpl < Minitest::Test
9
+
10
+ def setup
11
+ @shell = WAB::Impl::Shell.new(
12
+ WAB::Impl::Configuration.new('',
13
+ 'base' => File.join(__dir__, 'tmp')
14
+ )
15
+ )
16
+ end
17
+
18
+ def make_sample_data()
19
+ @shell.data({
20
+ boo: true,
21
+ n: nil,
22
+ num: 7,
23
+ float: 7.654,
24
+ str: 'a string',
25
+ t: Time.gm(2017, 1, 5, 15, 4, 33.123456789),
26
+ big: BigDecimal('63.21'),
27
+ uri: URI('http://opo.technology/sample'),
28
+ uuid: WAB::UUID.new('b0ca922d-372e-41f4-8fea-47d880188ba3'),
29
+ a: [],
30
+ h: {},
31
+ })
32
+ end
33
+ end # TestImpl
34
+
35
+ require 'test_configuration'
36
+ require 'test_data'
37
+ require 'test_expr'
38
+ require 'test_model'
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'helper'
5
+
6
+ require 'wab/impl'
7
+ require 'wab/io'
8
+ require 'mirror_controller'
9
+
10
+ class TestIoShell < Minitest::Test
11
+
12
+ def test_default_controller
13
+ run_fork_test([
14
+ [{rid: 'rid-mirror', api: 1, body: {kind: 'sample', num: 7}},
15
+ {rid: 'rid-mirror', api: 2, body: {kind: 'sample', num: 7}}]
16
+ ])
17
+ end
18
+
19
+ def test_create
20
+ run_fork_test([
21
+ [{rid: 'rid-create', api: 1, body: {op: 'NEW', path: ['sample'], content: {kind: 'sample', num: 7}}},
22
+ {rid: '1', api: 3, body: { insert:{ kind: 'sample', num: 7}}}],
23
+ [{rid: '1', api: 4, body: {ref: 123, code: 0}},
24
+ {rid: 'rid-create', api: 2, body: { ref: 123, code: 0}}]
25
+ ])
26
+ end
27
+
28
+ def test_create_with_where
29
+ run_fork_test([
30
+ [{rid: 'rid-create-where', api: 1, body: {op: 'NEW', path: ['sample'], query: {id: 12345}, content: {kind: 'sample', id: 12345, num: 7}}},
31
+ {rid: '1', api: 3, body: {where: ['AND', ['EQ', 'kind', "'sample"],['EQ', 'id', 12345]], insert:{ kind: 'sample', id: 12345, num: 7}}}],
32
+ [{rid: '1', api: 4, body: {ref: 123, code: 0}},
33
+ {rid: 'rid-create-where', api: 2, body: { ref: 123, code: 0 }}]
34
+ ])
35
+ end
36
+
37
+ def test_create_error
38
+ run_fork_test([
39
+ [{rid: 'rid-create-error', api: 1, body: {op: 'NEW', path: ['sample'], content: {kind: 'sample', num: 7}}},
40
+ {rid: '1', api: 3, body: { insert:{ kind: 'sample', num: 7}}}],
41
+ [{rid: '1', api: 4, body: {ref: 123, code: -1, error: 'something went wrong'}},
42
+ {rid: 'rid-create-error', api: 2, body: { code: -1, error: 'WAB::Error: error on sample create. something went wrong'}}]
43
+ ])
44
+ end
45
+
46
+ def test_read_by_id
47
+ run_fork_test([
48
+ [{rid: 'rid-read-id', api: 1, body: {op: 'GET', path: ['sample', '12345']}},
49
+ {rid: '1', api: 3, body: {where: 12345, select: '$'}}],
50
+ [{rid: '1', api: 4, body: {code: 0, results:[{kind: 'sample', id: 12345, num: 7}]}},
51
+ {rid: 'rid-read-id', api: 2, body: { code: 0, results:[{ id: 12345, data: { kind: 'sample', id: 12345, num: 7}}]}}]
52
+ ])
53
+ end
54
+
55
+ def test_read_by_attrs
56
+ run_fork_test([
57
+ [{rid: 'rid-read-attrs', api: 1, body: {op: 'GET', path: ['sample'], query: {id: 12345}}},
58
+ {rid: '1', api: 3, body: {where: ['AND', ['EQ', 'kind', "'sample"], ['EQ', 'id', 12345]], select: {id: '$ref', data: '$' }}}],
59
+ [{rid: '1', api: 4, body: {code: 0, results: [{id: 12345, data: {kind: 'sample', id: 12345, num: 7}}]}},
60
+ {rid: 'rid-read-attrs', api: 2, body: {code: 0, results:[{id: 12345, data: {kind: 'sample', id: 12345, num: 7}}]}}]
61
+ ])
62
+ end
63
+
64
+ def test_read_list
65
+ run_fork_test([
66
+ [{rid: 'rid-read-list', api: 1, body: {op: 'GET', path: ['sample']}},
67
+ {rid: '1', api: 3, body: {where: ['EQ', 'kind', "'sample"], select: { id: '$ref', data: '$'}}}],
68
+ [{rid: '1', api: 4, body: {code: 0, results: [12345]}},
69
+ {rid: 'rid-read-list', api: 2, body: {code: 0, results:[12345]}}]
70
+ ])
71
+ end
72
+
73
+ def test_read_select
74
+ run_fork_test([
75
+ [{rid: 'rid-read-select', api: 1, body: {op: 'GET', path: ['sample', 'list'], query: { Age: 'num' }}},
76
+ {rid: '1', api: 3, body: {where: ['EQ', 'kind', "'sample"], select: { ref: '$ref', Age: 'num'}}}],
77
+ [{rid: '1', api: 4, body: {code: 0, results: [{ref: 12345, Age: 7 }]}},
78
+ {rid: 'rid-read-select', api: 2, body: {code: 0, results:[{ref: 12345, Age: 7 }]}}]
79
+ ])
80
+ end
81
+
82
+ def test_update_by_id
83
+ run_fork_test([
84
+ [{rid: 'rid-update-id', api: 1, body: {op: 'MOD', path: ['sample', '12345'], content: {kind: 'sample', id: 12345, num: 7}}},
85
+ {rid: '1', api: 3, body: {where: 12345, update: { kind: 'sample', id: 12345, num: 7}}}],
86
+ [{rid: '1', api: 4, body: {code: 0, updated: [12345]}},
87
+ {rid: 'rid-update-id', api: 2, body: { code: 0, updated: [12345]}}]
88
+ ])
89
+ end
90
+
91
+ def test_update_by_attrs
92
+ run_fork_test([
93
+ [{rid: 'rid-update-attrs', api: 1, body: {op: 'MOD', path: ['sample'], query: {id: 12345}, content: {kind: 'sample', id: 12345, num: 7}}},
94
+ {rid: '1', api: 3, body: {where: ['AND', ['EQ', 'kind', "'sample"],['EQ', 'id', 12345]], update: { kind: 'sample', id: 12345, num: 7}}}],
95
+ [{rid: '1', api: 4, body: {code: 0, updated: [12345]}},
96
+ {rid: 'rid-update-attrs', api: 2, body: { code: 0, updated: [12345]}}]
97
+ ])
98
+ end
99
+
100
+ def test_delete_by_id
101
+ run_fork_test([
102
+ [{rid: 'rid-delete-id', api: 1, body: {op: 'DEL', path: ['sample', '12345']}},
103
+ {rid: '1', api: 3, body: {where: 12345, delete: nil}}],
104
+ [{rid: '1', api: 4, body: {code: 0, deleted: [12345]}},
105
+ {rid: 'rid-delete-id', api: 2, body: { code: 0, deleted: [12345]}}]
106
+ ])
107
+ end
108
+
109
+ def test_delete_by_attrs
110
+ run_fork_test([
111
+ [{rid: 'rid-delete-attrs', api: 1, body: {op: 'DEL', path: ['sample'], query: {id: 12345}}},
112
+ {rid: '1', api: 3, body: {where: ['AND', ['EQ', 'kind', "'sample"],['EQ', 'id', 12345]], delete: nil}}],
113
+ [{rid: '1', api: 4, body: {code: 0, deleted: [12345]}},
114
+ {rid: 'rid-delete-attrs', api: 2, body: { code: 0, deleted: [12345]}}]
115
+ ])
116
+ end
117
+
118
+ def test_timeout
119
+ run_fork_test([
120
+ [{rid: 'rid-timeout', api: 1, body: {op: 'NEW', path: ['sample'], content: {kind: 'sample', num: 7}}},
121
+ {rid: '1', api: 3, body: { insert:{ kind: 'sample', num: 7}}}],
122
+ [nil,
123
+ {rid: 'rid-timeout', api: 2, body: { code: -1, error: 'WAB::Error: error on sample create. Timed out waiting for 1.'}}]
124
+ ])
125
+ end
126
+
127
+ # Fork and create a shell in the child. For each pair in the script send the
128
+ # first message and wait for the second. Compare second for test success of
129
+ # failure.
130
+ def run_fork_test(script)
131
+ # Windows does not support fork
132
+ return if RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/
133
+
134
+ to_r, to_w = IO.pipe
135
+ from_r, from_w = IO.pipe
136
+
137
+ if fork.nil? # child
138
+ $stdin = to_r
139
+ to_w.close
140
+ $stdout = from_w
141
+ from_r.close
142
+
143
+ shell = WAB::IO::Shell.new(2, 'kind', 0)
144
+ shell.timeout = 0.5
145
+ shell.logger.level = Logger::WARN # change to Logger::INFO for debugging
146
+ shell.register_controller(nil, MirrorController.new(shell))
147
+ shell.start
148
+
149
+ Process.exit(0)
150
+ else
151
+ to_r.close
152
+ from_w.close
153
+
154
+ # Shell#data should be called instead but for this test a shell is not
155
+ # desired.
156
+ script.each { |pair|
157
+ unless pair[0].nil?
158
+ to_w.puts(WAB::Impl::Data.new(pair[0], false).json)
159
+ to_w.flush
160
+ end
161
+
162
+ reply = nil
163
+ Oj.strict_load(from_r, symbol_keys: true) { |msg| reply = msg; break }
164
+
165
+ # remove backtrace if an error
166
+ bt = nil
167
+ if !reply.nil? && !reply[:body].nil?
168
+ bt = reply[:body].delete(:backtrace)
169
+ end
170
+ begin
171
+ assert_equal(pair[1], reply)
172
+ rescue Exception => e
173
+ puts ''
174
+ puts bt
175
+ raise e
176
+ end
177
+ }
178
+ # On error the pipe may be closed from the child side so the write will
179
+ # fail. Thats okay as the write is only to tell the child to shutdown
180
+ # and it already has.
181
+ begin
182
+ to_w.puts(WAB::Impl::Data.new({ api: -2 }, false).json)
183
+ to_w.flush
184
+ rescue Exception
185
+ end
186
+ end
187
+ end
188
+
189
+ end
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'helper'
5
+
6
+ require 'wab/impl/model'
7
+
8
+ class TestModel < TestImpl
9
+
10
+ def test_model_create
11
+ # nil arg indicates don't save to disk
12
+ model = WAB::Impl::Model.new(nil)
13
+ tql = {
14
+ rid: 12345,
15
+ insert: {
16
+ kind: 'Person',
17
+ name: 'Peter',
18
+ age: 63
19
+ }
20
+ }
21
+ result = model.query(tql)
22
+ assert_equal(0, result[:code], 'expected a 0 result code')
23
+ assert_equal(12345, result[:rid], 'expected correct rid')
24
+ ref = result[:ref]
25
+ # get and verify object was stored.
26
+ obj = model.get(ref)
27
+
28
+ assert_equal({kind: 'Person', name: 'Peter', age: 63}, obj.native, 'obj get mismatch')
29
+ end
30
+
31
+ end # TestModel
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative 'helper'
5
+
6
+ require 'net/http'
7
+ require 'oj'
8
+
9
+ require 'wab/impl'
10
+ require 'wab/io'
11
+
12
+ # -----------------------------------------------------------------------------
13
+ # This is to verify that WAB-Runners behave as expected.
14
+ #
15
+ # Requirements:
16
+ # - The Runner (e.g. OpO daemon) should be started with the MirrorController
17
+ # class.
18
+ # - The host and port must be the last argument on the command line when
19
+ # invoking the test.
20
+ #
21
+ # Example usage:
22
+ # runner_test.rb localhost:6363
23
+ # -----------------------------------------------------------------------------
24
+
25
+ raise ArgumentError, 'Host and port not supplied.' if ARGV.empty?
26
+
27
+ $host, $port = ARGV[-1].split(':')
28
+ $port = $port.to_i
29
+
30
+ class TestRunner < Minitest::Test
31
+
32
+ # The Runner or rather it's storage is stateful so all steps in the test
33
+ # must be made in order to keep the test self contained. Each step is a
34
+ # separate function though.
35
+ def test_runner_basics
36
+ http = Net::HTTP.new($host, $port)
37
+
38
+ # Delete all records to start with a clean database
39
+ clear_records(http)
40
+ ref = create_record(http)
41
+ read_record(http, ref)
42
+ list_records(http, ref)
43
+
44
+ ref = update_record(http, ref)
45
+ read_after_update(http, ref)
46
+
47
+ delete_record(http, ref)
48
+ read_after_delete(http, ref)
49
+ end
50
+
51
+ def clear_records(http)
52
+ resp = http.send_request('DELETE', '/v1/Article')
53
+ # Response should be an OK with a JSON body.
54
+ assert_equal(Net::HTTPOK, resp.class, 'response not an OK')
55
+ reply = Oj.strict_load(resp.body, symbol_keys: true)
56
+ end
57
+
58
+ # Returns the reference of the created record.
59
+ def create_record(http)
60
+ json = %|{
61
+ "kind": "Article",
62
+ "title": "Sample",
63
+ "text": "Just some random text."
64
+ }|
65
+ resp = http.send_request('PUT', '/v1/Article', json, { 'Content-Type' => 'application/json' })
66
+ # Response should be an OK with a JSON body.
67
+ assert_equal(Net::HTTPOK, resp.class, 'response not an OK')
68
+ reply = Oj.strict_load(resp.body, symbol_keys: true)
69
+
70
+ # Make sure the message has the correct fields and values.
71
+ assert_equal(0, reply[:code], 'create reply.code should be 0 meaning no error')
72
+ ref = reply[:ref]
73
+ refute_equal(nil, ref, 'create reply record reference can not be nil')
74
+ refute_equal(0, ref, 'create reply record reference can not be 0')
75
+ ref
76
+ end
77
+
78
+ def read_record(http, ref)
79
+ resp = http.send_request('GET', "/v1/Article/#{ref}")
80
+ assert_equal(Net::HTTPOK, resp.class, 'response not an OK')
81
+ reply = Oj.strict_load(resp.body, symbol_keys: true)
82
+
83
+ # Make sure the message has the correct fields and values.
84
+ assert_equal(0, reply[:code], 'read reply.code should be 0 meaning no error')
85
+ results = reply[:results]
86
+ assert_equal(1, results.length, 'read reply.results should contain exactly one member')
87
+ record = results[0]
88
+ assert_equal(ref, record[:id], 'read reply.results[0].id should match the record reference')
89
+ obj = record[:data]
90
+ assert_equal('Article', obj[:kind], 'read reply obj.kind incorrect')
91
+ assert_equal('Sample', obj[:title], 'read reply obj.title incorrect')
92
+ end
93
+
94
+ def list_records(http, ref)
95
+ resp = http.send_request('GET', '/v1/Article')
96
+ assert_equal(Net::HTTPOK, resp.class, 'response not an OK')
97
+ reply = Oj.strict_load(resp.body, symbol_keys: true)
98
+
99
+ # Make sure the message has the correct fields and values.
100
+ assert_equal(0, reply[:code], 'read reply.code should be 0 meaning no error')
101
+ results = reply[:results]
102
+ assert_equal(1, results.length, 'read reply.results should contain exactly one member')
103
+ record = results[0]
104
+ assert_equal(ref, record[:id], 'read reply.results[0].id should match the record reference')
105
+ obj = record[:data]
106
+ assert_equal('Article', obj[:kind], 'read reply obj.kind incorrect')
107
+ assert_equal('Sample', obj[:title], 'read reply obj.title incorrect')
108
+ end
109
+
110
+ # Returns the reference of the updated record. There is no requirement that
111
+ # the reference will not change.
112
+ def update_record(http, ref)
113
+ json = %|{
114
+ "kind": "Article",
115
+ "title": "Sample",
116
+ "text": "Updated text."
117
+ }|
118
+ resp = http.send_request('POST', "/v1/Article/#{ref}", json, { 'Content-Type' => 'application/json' })
119
+ # Response should be an OK with a JSON body.
120
+ assert_equal(Net::HTTPOK, resp.class, 'response not an OK')
121
+ reply = Oj.strict_load(resp.body, symbol_keys: true)
122
+
123
+ # Make sure the message has the correct fields and values.
124
+ assert_equal(0, reply[:code], 'update reply.code should be 0 meaning no error')
125
+ updated = reply[:updated]
126
+ assert_equal(1, updated.length, 'update reply.updated should contain exactly one member')
127
+ ref = updated[0]
128
+ refute_equal(nil, ref, 'update reply record reference can not be nil')
129
+ refute_equal(0, ref, 'update reply record reference can not be 0')
130
+ ref
131
+ end
132
+
133
+ def read_after_update(http, ref)
134
+ resp = http.send_request('GET', "/v1/Article/#{ref}")
135
+ assert_equal(Net::HTTPOK, resp.class, 'response not an OK')
136
+ reply = Oj.strict_load(resp.body, symbol_keys: true)
137
+
138
+ assert_equal(0, reply[:code], 'read after update reply.code should be 0 meaning no error')
139
+ results = reply[:results]
140
+ assert_equal(1, results.length, 'read after update reply.results should contain exactly one member')
141
+ record = results[0]
142
+ assert_equal(ref, record[:id], 'read after update reply.results[0].id should match the record reference')
143
+ obj = record[:data]
144
+ assert_equal('Updated text.', obj[:text], 'read after update reply obj.text incorrect')
145
+ end
146
+
147
+ def delete_record(http, ref)
148
+ resp = http.send_request('DELETE', "/v1/Article/#{ref}")
149
+ # Response should be an OK with a JSON body.
150
+ assert_equal(Net::HTTPOK, resp.class, 'response not an OK')
151
+ reply = Oj.strict_load(resp.body, symbol_keys: true)
152
+
153
+ # Make sure the message has the correct fields and values.
154
+ assert_equal(0, reply[:code], 'delete reply.code should be 0 meaning no error')
155
+ deleted = reply[:deleted]
156
+ assert_equal(1, deleted.length, 'delete reply.deleted should contain exactly one member')
157
+ ref = deleted[0]
158
+ refute_equal(nil, ref, 'delete reply record reference can not be nil')
159
+ refute_equal(0, ref, 'delete reply record reference can not be 0')
160
+ end
161
+
162
+ def read_after_delete(http, ref)
163
+ resp = http.send_request('GET', "/v1/Article/#{ref}")
164
+ assert_equal(Net::HTTPOK, resp.class, 'response not an OK')
165
+ reply = Oj.strict_load(resp.body, symbol_keys: true)
166
+
167
+ assert_equal(0, reply[:code], 'read after delete reply.code should be 0 meaning no error')
168
+ results = reply[:results]
169
+ assert_equal(0, results.length, 'read after delete reply.results should contain no members')
170
+ end
171
+
172
+ # TBD test failure modes as well
173
+ # TBD test multiple matches on update and delete
174
+ # TBD test no matches on update and delete
175
+ # TBD test queries
176
+
177
+ end # TestRunner
data/test/tests.rb CHANGED
@@ -1,12 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # encoding: UTF-8
3
3
 
4
- $: << File.dirname(__FILE__)
5
- $: << File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'lib')
4
+ require_relative 'helper'
6
5
 
7
- require 'minitest'
8
- require 'minitest/autorun'
9
-
10
- require 'impl_test'
11
-
12
- require 'ioshell_test'
6
+ require 'test_impl'
7
+ require 'test_io_shell'