neverbounce-cli 1.0.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.
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