eval_in 0.1.6 → 0.2.0

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.
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ # This code is mostly tested through tests on the higher level things it depends on
4
+ # But for a few small helpers, I wanted to just hit a lot of edge cases to really
5
+ # give myself confidence they did what I was expecting.
6
+ RSpec.describe EvalIn::HTTP do
7
+ describe '.jsonify_url' do
8
+ def assert_transforms(initial_url, expected_url)
9
+ actual_url = EvalIn::HTTP.jsonify_url initial_url
10
+ expect(actual_url).to eq expected_url
11
+ end
12
+
13
+ it 'appends .json to a url that is missing it' do
14
+ assert_transforms 'http://eval.in/1', 'http://eval.in/1.json'
15
+ assert_transforms 'http://eval.in/1.json', 'http://eval.in/1.json'
16
+
17
+ assert_transforms 'http://eval.in/1?a=b', 'http://eval.in/1.json?a=b'
18
+ assert_transforms 'http://eval.in/1.json?a=b', 'http://eval.in/1.json?a=b'
19
+ end
20
+
21
+ it 'changes .not-json to .json' do
22
+ assert_transforms 'http://eval.in/1.xml', 'http://eval.in/1.json'
23
+ assert_transforms 'http://eval.in/1.html', 'http://eval.in/1.json'
24
+ assert_transforms 'http://eval.in/1.html?a=b', 'http://eval.in/1.json?a=b'
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+ require 'eval_in/mock'
3
+
4
+
5
+ # TODO: look at other reasons the thing could blow up, do we need to provide the mock a way to do these things?
6
+ RSpec.describe EvalIn::Mock do
7
+ def is_jruby?
8
+ defined? RUBY_ENGINE and RUBY_ENGINE == 'jruby'
9
+ end
10
+
11
+ specify 'instances provide the same methods with the same signatures as the EvalIn class' do
12
+ mock = described_class.new
13
+ EvalIn.methods(false).each do |method_name|
14
+ expected_signature = EvalIn.method(method_name).parameters
15
+ actual_signature = mock.method(method_name).parameters
16
+ expect(actual_signature).to eq expected_signature
17
+ end
18
+ end
19
+
20
+ describe '#call' do
21
+ context 'when initialized with a mock result' do
22
+ it 'returns the mock result' do
23
+ result = Object.new
24
+ mock = described_class.new result: result
25
+ expect(mock.call "code", language: '').to equal result
26
+ end
27
+ it 'raises an ArgumentError if no language is provided' do
28
+ mock = described_class.new result: Object.new
29
+ expect { mock.call "code" }.to raise_error ArgumentError
30
+ expect { mock.call "code", language: '' }.to_not raise_error
31
+ end
32
+ end
33
+
34
+ context 'when initialized with an on_call proc' do
35
+ it 'passes the code and options to the proc and returns the result' do
36
+ mock = described_class.new on_call: -> code, options {
37
+ expect(code).to eq 'on_call code'
38
+ expect(options).to eq language: 'l'
39
+ 123
40
+ }
41
+ expect(mock.call "on_call code", language: "l").to eq 123
42
+ end
43
+
44
+ it 'raises an ArgumentError if no language is provided' do
45
+ mock = described_class.new on_call: -> * {}
46
+ expect { mock.call "code" }.to raise_error ArgumentError
47
+ expect { mock.call "code", language: '' }.to_not raise_error
48
+ end
49
+ end
50
+
51
+ context 'when initialized without a a result or on_call proc' do
52
+ before do
53
+ if is_jruby?
54
+ pending "I can't figure out how to get JRuby to execute a fucking process"
55
+ raise "Why did RSpec remove the block form of pending?"
56
+ end
57
+ end
58
+
59
+ it 'executes the code with open3 against the list of language mappings' do
60
+ mock = described_class.new(languages: {
61
+ 'correct-lang' => {program: 'echo', args: ['RIGHT LANGUAGE']},
62
+ 'incorrect-lang' => {program: 'echo', args: ['WRONG LANGUAGE']},
63
+ })
64
+ result = mock.call 'some code', language: 'correct-lang'
65
+ expect(result.output).to start_with 'RIGHT LANGUAGE'
66
+ end
67
+
68
+ def result_from(options={})
69
+ language = options.fetch :language, 'dummy language'
70
+ code = options.fetch :code, 'dummy code'
71
+ program_code = options.fetch :program_code, '# noop'
72
+
73
+ described_class.new(languages: {
74
+ language => {
75
+ program: RbConfig.ruby,
76
+ args: ['-e', program_code]
77
+ }
78
+ }).call(code, language: language)
79
+ end
80
+
81
+ it 'records stderr and stdout' do
82
+ result = result_from program_code: '$stdout.print("STDOUT "); $stdout.flush; $stderr.print("STDERR")'
83
+ expect(result.output).to eq 'STDOUT STDERR'
84
+ end
85
+
86
+ it 'records the exit status' do
87
+ expect(result_from(program_code: 'exit 12').exitstatus).to eq 12
88
+ expect(result_from(program_code: 'exit 99').exitstatus).to eq 99
89
+ end
90
+
91
+ it 'sets the language and language_friendly to the provided language' do
92
+ result = result_from language: 'the-lang'
93
+ expect(result.language).to eq 'the-lang'
94
+ expect(result.language_friendly).to eq 'the-lang'
95
+ end
96
+
97
+ it 'sets the code to the provided code' do
98
+ expect(result_from(code: 'the-code').code).to eq 'the-code'
99
+ end
100
+
101
+ it 'sets the url to my mock result at https://eval.in/207744.json' do
102
+ expect(result_from().url).to eq 'https://eval.in/207744.json'
103
+ end
104
+
105
+ it 'sets the status to something looking like a real status' do
106
+ # in this case, just the status from https://eval.in/207744.json
107
+ expect(result_from().status).to match success_status_regex
108
+ end
109
+
110
+ it 'blows up if asked for a language it doesn\'t know how to evaluate' do
111
+ expect { described_class.new.call '', language: 'no-such-lang' }
112
+ .to raise_error KeyError, /no-such-lang/
113
+ end
114
+
115
+ it 'raises an ArgumentError if no language is provided' do
116
+ mock = described_class.new languages: {'l' => {program: 'echo', args: []}}
117
+ expect { mock.call "code" }.to raise_error ArgumentError
118
+ expect { mock.call "code", language: 'l' }.to_not raise_error
119
+ end
120
+ end
121
+ end
122
+
123
+ describe '#fetch_result' do
124
+ context 'when initialized with a mock result' do
125
+ it 'returns the mock result' do
126
+ result = Object.new
127
+ mock = described_class.new(result: result)
128
+ expect(mock.fetch_result "code").to equal result
129
+ end
130
+ end
131
+
132
+ context 'when initialized with an on_fetch_result proc' do
133
+ it 'passes the url and options to the proc and returns the result' do
134
+ mock = described_class.new on_fetch_result: -> url, options {
135
+ expect(url).to eq 'some url'
136
+ expect(options).to eq a: 'b'
137
+ 123
138
+ }
139
+ expect(mock.fetch_result 'some url', a: 'b').to eq 123
140
+ end
141
+ end
142
+
143
+ context 'when initialized without a result or on_fetch_result proc' do
144
+ include WebMock::API
145
+
146
+ it 'delegates to the real implementation' do
147
+ url = "http://example.com/some-result.json"
148
+ result_hash = {'lang_friendly' => 'some lang friendly',
149
+ 'lang' => 'some lang',
150
+ 'code' => 'some code',
151
+ 'output' => 'some output',
152
+ 'status' => 'some status'}
153
+ stub_request(:get, url)
154
+ .with(headers: {'User-Agent' => 'http://rubygems.org/gems/eval_in (context)'})
155
+ .to_return(status: 200, body: JSON.dump(result_hash))
156
+ result = described_class.new.fetch_result("http://example.com/some-result.json", context: 'context')
157
+ assert_result result,
158
+ exitstatus: 0,
159
+ language: result_hash['lang'],
160
+ language_friendly: result_hash['lang_friendly'],
161
+ code: result_hash['code'],
162
+ output: result_hash['output'],
163
+ status: result_hash['status'],
164
+ url: url
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe EvalIn::Result do
4
+ it 'initializes with the provided attributes' do
5
+ result = EvalIn::Result.new exitstatus: 123,
6
+ language: 'the language',
7
+ language_friendly: 'the friendly language',
8
+ code: 'the code',
9
+ output: 'the output',
10
+ status: 'the status'
11
+ assert_result result,
12
+ exitstatus: 123,
13
+ language: 'the language',
14
+ language_friendly: 'the friendly language',
15
+ code: 'the code',
16
+ output: 'the output',
17
+ status: 'the status'
18
+ end
19
+
20
+ it 'uses sensible type-correct defaults for missing attributes' do
21
+ assert_result EvalIn::Result.new,
22
+ exitstatus: -1,
23
+ language: '',
24
+ language_friendly: '',
25
+ code: '',
26
+ output: '',
27
+ status: '',
28
+ url: ''
29
+ assert_result EvalIn::Result.new(language: nil,
30
+ language_friendly: nil,
31
+ code: nil,
32
+ output: nil,
33
+ status: nil,
34
+ url: nil),
35
+ exitstatus: -1,
36
+ language: '',
37
+ language_friendly: '',
38
+ code: '',
39
+ output: '',
40
+ status: '',
41
+ url: ''
42
+ end
43
+
44
+ it 'doesn\'t mutate the input attributes' do
45
+ attributes = {status: 'OK'}
46
+ EvalIn::Result.new attributes
47
+ expect(attributes).to eq status: 'OK'
48
+ end
49
+
50
+ it 'logs extra attributes to stderr input' do
51
+ fake_error_stream = StringIO.new
52
+ EvalIn::Result.new a: 1, b: 2, stderr: fake_error_stream
53
+ expect(fake_error_stream.string).to eq "Unexpected attributes! [:a, :b]\n"
54
+ end
55
+
56
+ it 'defaults the error stream to $stderr' do
57
+ expect { EvalIn::Result.new a: 1, b: 2 }.to \
58
+ output("Unexpected attributes! [:a, :b]\n").to_stderr
59
+ end
60
+
61
+ it 'has an as_json representation which dumps all its keys' do
62
+ result = EvalIn::Result.new(language: 'l',
63
+ language_friendly: 'lf',
64
+ code: 'c',
65
+ output: 'o',
66
+ status: 's',
67
+ url: 'u')
68
+ expect(result.as_json).to eq 'exitstatus' => -1,
69
+ 'language' => 'l',
70
+ 'language_friendly' => 'lf',
71
+ 'code' => 'c',
72
+ 'output' => 'o',
73
+ 'status' => 's',
74
+ 'url' => 'u'
75
+ end
76
+
77
+ it 'has a to_json that works correctly' do
78
+ result = EvalIn::Result.new(language: 'l',
79
+ language_friendly: 'lf',
80
+ code: 'c',
81
+ output: 'o',
82
+ status: 's',
83
+ url: 'u')
84
+ after_json = JSON.parse(result.to_json)
85
+ expect(after_json).to eq 'exitstatus' => -1,
86
+ 'language' => 'l',
87
+ 'language_friendly' => 'lf',
88
+ 'code' => 'c',
89
+ 'output' => 'o',
90
+ 'status' => 's',
91
+ 'url' => 'u'
92
+ end
93
+ end
@@ -0,0 +1,23 @@
1
+ require 'eval_in'
2
+ require 'webmock'
3
+ WebMock.disable_net_connect!
4
+
5
+ RSpec.configure do |config|
6
+ config.filter_run_excluding integration: true
7
+
8
+ config.include Module.new {
9
+ def assert_result(result, attributes)
10
+ attributes.each do |key, value|
11
+ expect(result.public_send key).to eq value
12
+ end
13
+ end
14
+
15
+ def success_status_regex
16
+ /OK \([\d.]+ sec real, [\d.]+ sec wall, \d MB, \d+ syscalls\)/
17
+ end
18
+ }
19
+
20
+ config.after do
21
+ WebMock.reset!
22
+ end
23
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eval_in
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Cheek
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-06 00:00:00.000000000 Z
11
+ date: 2014-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -79,8 +79,17 @@ files:
79
79
  - Readme.md
80
80
  - eval_in.gemspec
81
81
  - lib/eval_in.rb
82
- - lib/eval_in/version.rb
82
+ - lib/eval_in/client.rb
83
+ - lib/eval_in/constants.rb
84
+ - lib/eval_in/http.rb
85
+ - lib/eval_in/mock.rb
86
+ - lib/eval_in/result.rb
87
+ - spec/client_spec.rb
83
88
  - spec/eval_in_spec.rb
89
+ - spec/http_spec.rb
90
+ - spec/mock_spec.rb
91
+ - spec/result_spec.rb
92
+ - spec/spec_helper.rb
84
93
  homepage: https://github.com/JoshCheek/eval_in
85
94
  licenses:
86
95
  - WTFPL
@@ -106,5 +115,10 @@ signing_key:
106
115
  specification_version: 4
107
116
  summary: Evaluates code (Ruby and others) safely by sending it to https://eval.in
108
117
  test_files:
118
+ - spec/client_spec.rb
109
119
  - spec/eval_in_spec.rb
120
+ - spec/http_spec.rb
121
+ - spec/mock_spec.rb
122
+ - spec/result_spec.rb
123
+ - spec/spec_helper.rb
110
124
  has_rdoc:
@@ -1,3 +0,0 @@
1
- module EvalIn
2
- VERSION = '0.1.6'
3
- end