neverbounce-cli 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +8 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +1161 -0
  6. data/.travis.yml +27 -0
  7. data/.yardopts +2 -0
  8. data/Gemfile +19 -0
  9. data/Gemfile.lock +60 -0
  10. data/LICENSE +9 -0
  11. data/README.md +138 -0
  12. data/exe/nb-account-info +5 -0
  13. data/exe/nb-jobs-create +5 -0
  14. data/exe/nb-jobs-delete +5 -0
  15. data/exe/nb-jobs-download +5 -0
  16. data/exe/nb-jobs-parse +5 -0
  17. data/exe/nb-jobs-results +5 -0
  18. data/exe/nb-jobs-search +5 -0
  19. data/exe/nb-jobs-start +5 -0
  20. data/exe/nb-jobs-status +5 -0
  21. data/exe/nb-single-check +5 -0
  22. data/lib/never_bounce/cli/feature/basic_initialize.rb +20 -0
  23. data/lib/never_bounce/cli/feature/eigencache.rb +47 -0
  24. data/lib/never_bounce/cli/feature/envars/examples_mapper.rb +38 -0
  25. data/lib/never_bounce/cli/feature/envars/item.rb +41 -0
  26. data/lib/never_bounce/cli/feature/envars.rb +82 -0
  27. data/lib/never_bounce/cli/feature/igetset.rb +41 -0
  28. data/lib/never_bounce/cli/feature/require_attr.rb +25 -0
  29. data/lib/never_bounce/cli/script/account_info.rb +85 -0
  30. data/lib/never_bounce/cli/script/base.rb +101 -0
  31. data/lib/never_bounce/cli/script/error.rb +16 -0
  32. data/lib/never_bounce/cli/script/feature/requires_job_id.rb +28 -0
  33. data/lib/never_bounce/cli/script/feature/uses_pagination.rb +45 -0
  34. data/lib/never_bounce/cli/script/jobs_create/supplied_input_parser.rb +48 -0
  35. data/lib/never_bounce/cli/script/jobs_create.rb +157 -0
  36. data/lib/never_bounce/cli/script/jobs_delete.rb +55 -0
  37. data/lib/never_bounce/cli/script/jobs_download.rb +44 -0
  38. data/lib/never_bounce/cli/script/jobs_parse.rb +73 -0
  39. data/lib/never_bounce/cli/script/jobs_results.rb +105 -0
  40. data/lib/never_bounce/cli/script/jobs_search.rb +135 -0
  41. data/lib/never_bounce/cli/script/jobs_start.rb +73 -0
  42. data/lib/never_bounce/cli/script/jobs_status.rb +96 -0
  43. data/lib/never_bounce/cli/script/manifest.rb +22 -0
  44. data/lib/never_bounce/cli/script/meaningful.rb +233 -0
  45. data/lib/never_bounce/cli/script/request_maker.rb +169 -0
  46. data/lib/never_bounce/cli/script/single_check.rb +146 -0
  47. data/lib/never_bounce/cli/script/table.rb +43 -0
  48. data/lib/never_bounce/cli/user_config/file_content.rb +66 -0
  49. data/lib/never_bounce/cli/user_config.rb +51 -0
  50. data/lib/never_bounce/cli/version.rb +4 -0
  51. data/lib/neverbounce-cli.rb +3 -0
  52. data/neverbounce-cli.gemspec +24 -0
  53. data/spec/lib/never_bounce/cli/feature/basic_initialize_spec.rb +26 -0
  54. data/spec/lib/never_bounce/cli/feature/eigencache_spec.rb +28 -0
  55. data/spec/lib/never_bounce/cli/feature/envars/examples_mapper_spec.rb +26 -0
  56. data/spec/lib/never_bounce/cli/feature/envars/item_spec.rb +9 -0
  57. data/spec/lib/never_bounce/cli/feature/envars_spec.rb +55 -0
  58. data/spec/lib/never_bounce/cli/feature/igetset_spec.rb +45 -0
  59. data/spec/lib/never_bounce/cli/feature/require_attr_spec.rb +25 -0
  60. data/spec/lib/never_bounce/cli/script/account_info_spec.rb +65 -0
  61. data/spec/lib/never_bounce/cli/script/base_spec.rb +56 -0
  62. data/spec/lib/never_bounce/cli/script/feature/requires_job_id_spec.rb +17 -0
  63. data/spec/lib/never_bounce/cli/script/feature/uses_pagination_spec.rb +21 -0
  64. data/spec/lib/never_bounce/cli/script/jobs_create/supplied_input_parser_spec.rb +35 -0
  65. data/spec/lib/never_bounce/cli/script/jobs_create_spec.rb +118 -0
  66. data/spec/lib/never_bounce/cli/script/jobs_delete_spec.rb +33 -0
  67. data/spec/lib/never_bounce/cli/script/jobs_download_spec.rb +37 -0
  68. data/spec/lib/never_bounce/cli/script/jobs_parse_spec.rb +45 -0
  69. data/spec/lib/never_bounce/cli/script/jobs_results_spec.rb +40 -0
  70. data/spec/lib/never_bounce/cli/script/jobs_search_spec.rb +59 -0
  71. data/spec/lib/never_bounce/cli/script/jobs_start_spec.rb +45 -0
  72. data/spec/lib/never_bounce/cli/script/jobs_status_spec.rb +37 -0
  73. data/spec/lib/never_bounce/cli/script/manifest_spec.rb +6 -0
  74. data/spec/lib/never_bounce/cli/script/meaningful_spec.rb +93 -0
  75. data/spec/lib/never_bounce/cli/script/request_maker_spec.rb +67 -0
  76. data/spec/lib/never_bounce/cli/script/single_check_spec.rb +94 -0
  77. data/spec/lib/never_bounce/cli/script/spec_helper.rb +59 -0
  78. data/spec/lib/never_bounce/cli/script/table_spec.rb +6 -0
  79. data/spec/lib/never_bounce/cli/user_config/file_content_spec.rb +43 -0
  80. data/spec/lib/never_bounce/cli/user_config_spec.rb +12 -0
  81. data/spec/lib/never_bounce/cli_spec.rb +6 -0
  82. data/spec/spec_helper.rb +51 -0
  83. metadata +193 -0
@@ -0,0 +1,4 @@
1
+
2
+ module NeverBounce; module CLI
3
+ VERSION = "1.0.0"
4
+ end; end
@@ -0,0 +1,3 @@
1
+
2
+ # Main gem require. Load all libraries.
3
+ Dir[File.join(__dir__, "**/*.rb")].each { |fn| require fn }
@@ -0,0 +1,24 @@
1
+
2
+ require_relative "lib/never_bounce/cli/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "neverbounce-cli"
6
+ s.summary = "The official NeverBounce CLI written in Ruby"
7
+
8
+ s.authors = ["NeverBounce"]
9
+ s.email = ["support@neverbounce.com"]
10
+ s.homepage = "https://neverbounce.com"
11
+ s.license = "MIT"
12
+ s.version = NeverBounce::CLI::VERSION
13
+
14
+ s.required_ruby_version = ">= 2.0.0"
15
+
16
+ s.bindir = "exe"
17
+ s.executables = `git ls-files -- exe/*`.split("\n").map { |f| File.basename(f) }
18
+ s.files = `git ls-files`.split("\n")
19
+ s.require_paths = ["lib"]
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+
22
+ s.add_dependency("neverbounce-api", "~> 1.0.0")
23
+ s.add_dependency("terminal-table", "~> 1.8.0")
24
+ end
@@ -0,0 +1,26 @@
1
+
2
+ describe NeverBounce::CLI::Feature::BasicInitialize do
3
+ let(:klass) do
4
+ feature = described_class
5
+
6
+ Class.new do
7
+ feature.load(self)
8
+
9
+ attr_accessor :a, :b
10
+
11
+ private
12
+
13
+ def c=(value)
14
+ @c = value
15
+ end
16
+ end
17
+ end
18
+
19
+ it "generally works" do
20
+ r = klass.new(a: 12, b: "34")
21
+ expect(r.a).to eq 12
22
+ expect(r.b).to eq "34"
23
+
24
+ expect { klass.new(c: 12) }.to raise_error(NoMethodError, /\bprivate method `c='/)
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+
2
+ describe NeverBounce::CLI::Feature::Eigencache do
3
+ let(:klass) do
4
+ feature = described_class
5
+
6
+ Class.new do
7
+ feature.load(self)
8
+
9
+ def a
10
+ _cache[:a] ||= "a"
11
+ end
12
+
13
+ def a=(value)
14
+ _cache[:a] = value
15
+ end
16
+ end
17
+ end
18
+
19
+ it "generally works" do
20
+ r = klass.new
21
+ expect(r.a).to eq "a"
22
+ expect(r.inspect).not_to match "@a"
23
+
24
+ r.a = "a+"
25
+ expect(r.a).to eq "a+"
26
+ expect(r.inspect).not_to match "@a"
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+
2
+ describe NeverBounce::CLI::Feature::Envars::ExamplesMapper do
3
+ def m(input)
4
+ described_class.new.process(input)
5
+ end
6
+
7
+ it "generally works" do
8
+ # Bad input.
9
+ [
10
+ [[1, abc: "xyz"], ArgumentError, "Unknown element format: {:abc=>\"xyz\"}"],
11
+ ].each do |input, error, message|
12
+ expect {m(input)}.to raise_error(error, message)
13
+ end
14
+
15
+ # Good input.
16
+ [
17
+ [[], {values: []}],
18
+ [[1], {values: [1]}],
19
+ [["y", default: "N"], {values: ["y", "N"], default: "N"}],
20
+ [[1, default: 2], {values: [1, 2], default: 2}],
21
+ [[1, {default: 2}, {default: 3}], {values: [1, 2, 3], default: 3}],
22
+ ].each do |input, expected|
23
+ expect([input, m(input)]).to eq([input, expected])
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+
2
+ describe NeverBounce::CLI::Feature::Envars::Item do
3
+ it "has defaults" do
4
+ r = described_class.new
5
+ expect(r.examples).to eq []
6
+ expect(r.default).to be nil
7
+ expect(r.is_mandatory).to be false
8
+ end
9
+ end
@@ -0,0 +1,55 @@
1
+
2
+ describe NeverBounce::CLI::Feature::Envars do
3
+ describe "#define_envar" do
4
+ it "generally works" do
5
+ feature = described_class
6
+
7
+ parent = Class.new do
8
+ feature.load(self)
9
+ define_envar "p.var1", "p.cmt1"
10
+ end
11
+
12
+ r = Class.new(parent) do
13
+ define_envar "var1", "cmt1"
14
+ define_envar "var2", "cmt2", is_mandatory: true
15
+ define_envar "var3", "cmt3", examples: ["a", "b"]
16
+ define_envar "var4", "cmt4", examples: ["y", "N"], default: "N"
17
+ end.new
18
+
19
+ # Do some "matrix" checks on the items.
20
+ envars = r.class.envars
21
+ expect(envars.size).to eq 5
22
+ expect(envars.map(&:name)).to eq ["p.var1", "var1", "var2", "var3", "var4"]
23
+ expect(envars.map(&:comment)).to eq ["p.cmt1", "cmt1", "cmt2", "cmt3", "cmt4"]
24
+ expect(envars.map(&:is_mandatory)).to eq [false, false, true, false, false]
25
+ expect(envars.map(&:examples)).to eq [[], [], [], ["a", "b"], ["y", "N"]]
26
+ expect(envars.map(&:default)).to eq [nil, nil, nil, nil, "N"]
27
+ end
28
+ end
29
+
30
+ describe "#envar" do
31
+ it "generally works" do
32
+ feature = described_class
33
+
34
+ parent = Class.new do
35
+ feature.load(self)
36
+ envar "p.var1", "p.cmt1"
37
+ end
38
+
39
+ r = Class.new(parent) do
40
+ envar "var1", "cmt1"
41
+ envar "var2*", "cmt2"
42
+ envar "var3", "cmt3", ["a", "b"]
43
+ envar "var4", "cmt4", ["y", default: "N"]
44
+ end.new
45
+
46
+ envars = r.class.envars
47
+ expect(envars.size).to eq 5
48
+ expect(envars.map(&:name)).to eq ["p.var1", "var1", "var2", "var3", "var4"]
49
+ expect(envars.map(&:comment)).to eq ["p.cmt1", "cmt1", "cmt2", "cmt3", "cmt4"]
50
+ expect(envars.map(&:is_mandatory)).to eq [false, false, true, false, false]
51
+ expect(envars.map(&:examples)).to eq [[], [], [], ["a", "b"], ["y", "N"]]
52
+ expect(envars.map(&:default)).to eq [nil, nil, nil, nil, "N"]
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,45 @@
1
+
2
+ describe NeverBounce::CLI::Feature::Igetset do
3
+ let(:klass) do
4
+ feature = described_class
5
+
6
+ Class.new do
7
+ feature.load(self)
8
+
9
+ attr_writer :is_a, :is_b
10
+
11
+ def is_a
12
+ igetset(:is_a) do
13
+ seq << :is_a_block
14
+ false
15
+ end
16
+ end
17
+
18
+ def is_b
19
+ igetset(:is_a) do
20
+ seq << :is_b_block
21
+ nil
22
+ end
23
+ end
24
+
25
+ # Store call sequence here.
26
+ def seq
27
+ @log ||= []
28
+ end
29
+ end
30
+ end
31
+
32
+ it "generally works" do
33
+ r = klass.new
34
+ expect(r.seq).to eq []
35
+ expect(r.is_a).to be false
36
+ expect(r.is_a).to be false
37
+ expect(r.seq).to eq [:is_a_block] # Block evaluation is logged once.
38
+
39
+ r = klass.new
40
+ expect(r.seq).to eq []
41
+ expect(r.is_b).to be nil
42
+ expect(r.is_b).to be nil
43
+ expect(r.seq).to eq [:is_b_block]
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+
2
+ module NeverBounce; module CLI; module Feature
3
+ describe RequireAttr do
4
+ let(:klass) do
5
+ feature = described_class
6
+ Class.new do
7
+ feature.load(self)
8
+
9
+ attr_accessor :a
10
+
11
+ def b
12
+ v = require_attr :a
13
+ [v, "b"]
14
+ end
15
+ end
16
+ end
17
+
18
+ it "generally works" do
19
+ r = klass.new
20
+ expect { r.b }.to raise_error(RuntimeError, "Attribute must be set: a")
21
+ r.a = "a"
22
+ expect(r.b).to eq ["a", "b"]
23
+ end
24
+ end
25
+ end; end; end
@@ -0,0 +1,65 @@
1
+
2
+ module NeverBounce; module CLI; module Script
3
+ describe AccountInfo do
4
+ include_dir_context __dir__
5
+
6
+ def margv(*args)
7
+ ["API_KEY=abc"] + args
8
+ end
9
+
10
+ it_behaves_like "instantiatable"
11
+ it_behaves_like "a script supporting `--help`", signatures: [/nb-account-info/]
12
+
13
+ describe "#request_curl" do
14
+ it "generally works" do
15
+ r = goodo(argv: margv)
16
+ expect(r.request_curl).to eq ["--request", "GET", "--url", "https://api.neverbounce.com/v4/account/info", "--header", "Content-Type: application/json", "--data-binary", "{\"key\":\"abc\"}"]
17
+ end
18
+ end
19
+
20
+ describe "output" do
21
+ context "when monthly subscription" do
22
+ it "generally works" do
23
+ r = goodo(argv: margv).tap do |_|
24
+ _.session.server_content_type = "application/json"
25
+ _.session.server_raw = '{"credits_info":{"free_credits_remaining":1000,"free_credits_used":0,"monthly_api_usage":0},"execution_time":405,"job_counts":{"completed":3,"processing":0,"queued":0,"under_review":0},"status":"success"}'
26
+ end
27
+
28
+ expect { r.main }.not_to raise_error
29
+ lines = r.stdout.tap(&:rewind).to_a
30
+ expect(lines).to be_any { |s| s =~ /^Response:$/ }
31
+ expect(lines).to be_any { |s| s =~ cell("ExecTime") }
32
+ expect(lines).to be_any { |s| s =~ /^Credits:$/ }
33
+ expect(lines).to be_any { |s| s =~ cell("FreeRmn") }
34
+ expect(lines).to be_any { |s| s =~ cell("FreeUsed") }
35
+ expect(lines).to be_any { |s| s =~ cell("MonthlyUsage") }
36
+ expect(lines).to be_any { |s| s =~ /^JobCounts:$/ }
37
+ expect(lines).to be_any { |s| s =~ cell("Completed") }
38
+ expect(lines).to be_any { |s| s =~ cell("UnderReview") }
39
+ end
40
+ end
41
+
42
+ context "when paid subscription" do
43
+ it "generally works" do
44
+ r = goodo(argv: margv).tap do |_|
45
+ _.session.server_content_type = "application/json"
46
+ _.session.server_raw = '{"status":"success","credits_info":{"paid_credits_used":0,"free_credits_used":0,"paid_credits_remaining":1000000,"free_credits_remaining":959},"job_counts":{"completed":1,"under_review":0,"queued":0,"processing":0},"execution_time":525}'
47
+ end
48
+
49
+ expect { r.main }.not_to raise_error
50
+ lines = r.stdout.tap(&:rewind).to_a
51
+ expect(lines).to be_any { |s| s =~ /^Response:$/ }
52
+ expect(lines).to be_any { |s| s =~ cell("ExecTime") }
53
+ expect(lines).to be_any { |s| s =~ /^Credits:$/ }
54
+ expect(lines).to be_any { |s| s =~ cell("FreeRmn") }
55
+ expect(lines).to be_any { |s| s =~ cell("FreeUsed") }
56
+ expect(lines).to be_any { |s| s =~ cell("PaidRmn") }
57
+ expect(lines).to be_any { |s| s =~ cell("PaidUsed") }
58
+ expect(lines).to be_any { |s| s =~ /^JobCounts:$/ }
59
+ expect(lines).to be_any { |s| s =~ cell("Completed") }
60
+ expect(lines).to be_any { |s| s =~ cell("UnderReview") }
61
+ end
62
+ end
63
+ end # describe "output"
64
+ end
65
+ end; end; end
@@ -0,0 +1,56 @@
1
+
2
+ describe NeverBounce::CLI::Script::Base do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+
7
+ describe ".env_value_truthy?" do
8
+ it "generally works" do
9
+ [
10
+ ["", false],
11
+ [" ", false],
12
+ [0, false],
13
+ ["0", false],
14
+ ["kk", false],
15
+ ["n", false],
16
+ ["N", false],
17
+
18
+ [1, true],
19
+ ["1", true],
20
+ ["y", true],
21
+ ["Y", true],
22
+ ["yes", true],
23
+ ["YES", true],
24
+ ["true", true],
25
+ ].each do |input, expected|
26
+ expect([input, described_class.env_value_truthy?(input)]).to eq [input, expected]
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "#env_truthy?" do
32
+ it "generally works" do
33
+ r = newo(env: {
34
+ # Load a few values, not necessarily every possible one.
35
+ "na" => "",
36
+ "nb" => "0",
37
+ "nc" => "n",
38
+ "ya" => "1",
39
+ "yb" => "y",
40
+ "yc" => "yes",
41
+ })
42
+
43
+ [
44
+ ["na", false],
45
+ ["nb", false],
46
+ ["nc", false],
47
+ ["ya", true],
48
+ [:ya, true],
49
+ ["yb", true],
50
+ ["yc", true],
51
+ ].each do |k, expected|
52
+ expect([k, r.env_truthy?(k)]).to eq [k, expected]
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module NeverBounce; module CLI; module Script; module Feature
3
+ describe RequiresJobId do
4
+ let(:klass) do
5
+ feature = described_class
6
+ Class.new(Meaningful) do
7
+ feature.load(self)
8
+ end
9
+ end
10
+
11
+ it "generally works" do
12
+ r = klass.new(env: {})
13
+ expect(r.envar_text).to eq "- ID - Job ID (\"276816\")"
14
+ expect { r.job_id }.to raise_error Script::UsageError, /use `ID=`/
15
+ end
16
+ end
17
+ end; end; end; end
@@ -0,0 +1,21 @@
1
+
2
+ module NeverBounce; module CLI; module Script; module Feature
3
+ describe UsesPagination do
4
+ let(:klass) do
5
+ feature = described_class
6
+ Class.new(Meaningful) do
7
+ feature.load(self)
8
+ end
9
+ end
10
+
11
+ it "generally works" do
12
+ r = klass.new(env: {
13
+ "PAGE" => "12",
14
+ "PER_PAGE" => "34",
15
+ })
16
+ expect(r.envar_text).to eq "- PAGE - Fetch page number N ([1], 5)\n- PER_PAGE - Paginate results N items per page (10, [1000])"
17
+ expect(r.page).to eq 12
18
+ expect(r.per_page).to eq 34
19
+ end
20
+ end
21
+ end; end; end; end
@@ -0,0 +1,35 @@
1
+
2
+ module NeverBounce; module CLI; module Script; class JobsCreate
3
+ describe SuppliedInputParser do
4
+ include_dir_context __dir__
5
+
6
+ describe "#separator" do
7
+ it "generally works" do
8
+ expect(newo.separator).to eq /[;,\n]/
9
+ expect(newo(separator: ";").separator).to eq ";"
10
+ end
11
+ end
12
+
13
+ describe "#process" do
14
+ it "generally works" do
15
+ # Invalid input.
16
+ [
17
+ ["", ArgumentError, /\bempty\b/i],
18
+ ].each do |input, *raise_args|
19
+ expect {newo[input]}.to raise_error(*raise_args)
20
+ end
21
+
22
+ # Valid input.
23
+ [
24
+ ["abc", [["abc", ""]]],
25
+ [" abc def ", [["abc", "def"]]],
26
+ ["alice@isp.com Alice Roberts;bob.smith@gmail.com Bob Smith", [["alice@isp.com", "Alice Roberts"], ["bob.smith@gmail.com", "Bob Smith"]]],
27
+ [" alice@isp.com Alice Roberts ; bob.smith@gmail.com Bob Smith ", [["alice@isp.com", "Alice Roberts"], ["bob.smith@gmail.com", "Bob Smith"]]],
28
+ ["alice@isp.com Alice Roberts;,\n", [["alice@isp.com", "Alice Roberts"]]],
29
+ ].each do |input, expected|
30
+ expect([input, newo[input]]).to eq [input, expected]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end; end; end; end