itunes_store_transporter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +127 -0
- data/bin/itms +243 -0
- data/lib/itunes/store/transporter.rb +236 -0
- data/lib/itunes/store/transporter/command.rb +103 -0
- data/lib/itunes/store/transporter/command/lookup.rb +55 -0
- data/lib/itunes/store/transporter/command/option.rb +21 -0
- data/lib/itunes/store/transporter/command/providers.rb +35 -0
- data/lib/itunes/store/transporter/command/schema.rb +28 -0
- data/lib/itunes/store/transporter/command/status.rb +34 -0
- data/lib/itunes/store/transporter/command/upload.rb +27 -0
- data/lib/itunes/store/transporter/command/verify.rb +37 -0
- data/lib/itunes/store/transporter/command/version.rb +30 -0
- data/lib/itunes/store/transporter/errors.rb +67 -0
- data/lib/itunes/store/transporter/output_parser.rb +83 -0
- data/lib/itunes/store/transporter/shell.rb +94 -0
- data/lib/itunes/store/transporter/version.rb +7 -0
- data/spec/command_spec.rb +511 -0
- data/spec/errors_spec.rb +52 -0
- data/spec/output_parser_spec.rb +71 -0
- data/spec/shell_spec.rb +52 -0
- data/spec/spec_helper.rb +75 -0
- data/spec/transporter_spec.rb +106 -0
- metadata +123 -0
data/spec/errors_spec.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ITunes::Store::Transporter::TransporterMessage do
|
4
|
+
subject { ITunes::Store::Transporter::TransporterMessage.new("some message", 1) }
|
5
|
+
its(:code) { should == 1 }
|
6
|
+
its(:message) { should == "some message" }
|
7
|
+
|
8
|
+
describe "#to_s" do
|
9
|
+
it "includes the code and message" do
|
10
|
+
subject.to_s.should == "some message (1)"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "error predicate methods" do
|
15
|
+
method_codes = {
|
16
|
+
:bad_data? => [3000, 3999],
|
17
|
+
:invalid_data? => [4000, 4999],
|
18
|
+
:missing_data? => [5000, 5999],
|
19
|
+
:unsupported_feature? => [6000, 6999],
|
20
|
+
:schema_error? => [8000, 8999],
|
21
|
+
:asset_error? => [9000, 9999]
|
22
|
+
}
|
23
|
+
|
24
|
+
method_codes.each do |method, codes|
|
25
|
+
[codes.first, codes.last].each do |code|
|
26
|
+
|
27
|
+
context "code #{code}" do
|
28
|
+
subject { described_class.new("", code) }
|
29
|
+
its(method) { should be_true }
|
30
|
+
its(:validation_error?) { should be_true }
|
31
|
+
|
32
|
+
(method_codes.keys - [method]).each do |other|
|
33
|
+
its(other) { should be_false }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe ITunes::Store::Transporter::ExecutionError do
|
42
|
+
subject { described_class.new(messages, 1) }
|
43
|
+
let(:messages) { 2.times.inject([]) { |err, i| err << ITunes::Store::Transporter::TransporterMessage.new("message #{i}", i) } }
|
44
|
+
its(:exitstatus) { should == 1 }
|
45
|
+
its(:errors) { should == messages }
|
46
|
+
|
47
|
+
describe "#to_s" do
|
48
|
+
it "includes each messages and its code" do
|
49
|
+
subject.to_s.should == "message 0 (0), message 1 (1)"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ITunes::Store::Transporter::OutputParser do
|
4
|
+
describe "parsing errors" do
|
5
|
+
# TODO: test various error message/code formats handled by the parser
|
6
|
+
|
7
|
+
context "without an error code" do
|
8
|
+
before(:all) { @parser = described_class.new(fixture("errors_and_warnings.no_error_number")) }
|
9
|
+
|
10
|
+
subject { @parser }
|
11
|
+
its(:warnings) { should be_empty }
|
12
|
+
its(:errors) { should have(2).items }
|
13
|
+
|
14
|
+
describe "the first error" do
|
15
|
+
subject { @parser.errors.first }
|
16
|
+
its(:code) { should be_nil }
|
17
|
+
its(:message) { should == "An error occurred while doing fun stuff" }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "the second error" do
|
21
|
+
subject { @parser.errors.last }
|
22
|
+
its(:code) { should be_nil }
|
23
|
+
its(:message) { should == "An exception has occurred: network timeout" }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with an error code" do
|
28
|
+
before(:all) { @parser = described_class.new(fixture("errors_and_warnings.with_error_number")) }
|
29
|
+
|
30
|
+
subject { @parser }
|
31
|
+
its(:warnings) { should be_empty }
|
32
|
+
its(:errors) { should have(2).items }
|
33
|
+
|
34
|
+
|
35
|
+
describe "the first error" do
|
36
|
+
subject { @parser.errors.first }
|
37
|
+
its(:code) { should == 4000 }
|
38
|
+
its(:message) { should == "This is error 4000" }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "the second error" do
|
42
|
+
subject { @parser.errors.last }
|
43
|
+
its(:code) { should == 5000 }
|
44
|
+
its(:message) { should == "This is error 5000" }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with duplicate messages" do
|
49
|
+
before(:all) { @parser = described_class.new(fixture("errors_and_warnings.duplicate_errors")) }
|
50
|
+
|
51
|
+
it "does not include duplicates" do
|
52
|
+
errors = @parser.errors.map { |e| e.message }
|
53
|
+
errors.should == ["Error 1", "Error 2", "Error 3"]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "parsing warnings" do
|
59
|
+
before(:all) { @parser = described_class.new(fixture("errors_and_warnings.single_warning")) }
|
60
|
+
|
61
|
+
subject { @parser }
|
62
|
+
its(:errors) { should be_empty }
|
63
|
+
its(:warnings) { should have(1).item }
|
64
|
+
|
65
|
+
describe "the warning" do
|
66
|
+
subject { @parser.warnings.first }
|
67
|
+
its(:code) { should == 4010 }
|
68
|
+
its(:message) { should == "You've been warned!" }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/spec/shell_spec.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "rbconfig"
|
3
|
+
|
4
|
+
describe ITunes::Store::Transporter::Shell do
|
5
|
+
it "yields stdout and stderr as they become available" do
|
6
|
+
ruby = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["ruby_install_name"])
|
7
|
+
temp = Tempfile.new ""
|
8
|
+
# sleep else poll() (select) can favor the 1st FD in the read array, causing the
|
9
|
+
# test to fail on some platforms
|
10
|
+
temp.write(<<-CODE)
|
11
|
+
$stdout.puts "OUT 1"
|
12
|
+
$stdout.flush
|
13
|
+
sleep 1
|
14
|
+
$stderr.puts "ERR 1"
|
15
|
+
$stderr.flush
|
16
|
+
sleep 1
|
17
|
+
$stdout.puts "OUT 2"
|
18
|
+
$stdout.flush
|
19
|
+
sleep 1
|
20
|
+
$stderr.puts "ERR 2"
|
21
|
+
CODE
|
22
|
+
|
23
|
+
temp.flush
|
24
|
+
|
25
|
+
output = []
|
26
|
+
expect = [ [ :stdout, "OUT 1" ],
|
27
|
+
[ :stderr, "ERR 1" ],
|
28
|
+
[ :stdout, "OUT 2" ],
|
29
|
+
[ :stderr, "ERR 2" ] ]
|
30
|
+
|
31
|
+
described_class.new(ruby).exec([temp.path]) do |line, stream|
|
32
|
+
output << [ stream, line.chomp! ]
|
33
|
+
end
|
34
|
+
|
35
|
+
output.should == expect
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when on Windows" do
|
39
|
+
before(:all) { ENV["PROGRAMFILES"] = "C:\\" }
|
40
|
+
it "selects the Windows executable" do
|
41
|
+
described_class.stub(:windows? => true)
|
42
|
+
described_class.new.path.should match /#{described_class::WINDOWS_EXE}\Z/
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "when on anything but Windows" do
|
47
|
+
it "selects the right executable" do
|
48
|
+
described_class.stub(:windows? => false)
|
49
|
+
described_class.new.path.should match /#{described_class::EXE_NAME}\Z/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "yaml"
|
3
|
+
require "tempfile"
|
4
|
+
require "fileutils"
|
5
|
+
require "itunes/store/transporter"
|
6
|
+
|
7
|
+
module SpecHelper
|
8
|
+
def create_options(options = {})
|
9
|
+
{ :username => "uzer",
|
10
|
+
:password => "_Gcod3" }.merge(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO: options for package contents?
|
14
|
+
def create_package(options = {})
|
15
|
+
Dir.mktmpdir ["",".itmsp"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def expect_shell_args(*expected)
|
19
|
+
ITunes::Store::Transporter::Shell.any_instance.stub(:exec) { |*arg| arg.first.should include(*expected) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def fixture(path)
|
23
|
+
Fixture.for(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Set up output streams and exit code
|
27
|
+
def mock_output(options = {})
|
28
|
+
outputs = []
|
29
|
+
exitcode = options[:exit] || 0
|
30
|
+
|
31
|
+
# Load a fixture for the given stream
|
32
|
+
[:stderr, :stdout].each do |fd|
|
33
|
+
fixture = options[fd]
|
34
|
+
next unless fixture
|
35
|
+
lines = Array === fixture ? fixture : Fixture.for(fixture)
|
36
|
+
outputs << [ lines, fd ]
|
37
|
+
end
|
38
|
+
|
39
|
+
ITunes::Store::Transporter::Shell.any_instance.stub(:exec) do |*options|
|
40
|
+
block = options.pop
|
41
|
+
outputs.each do |lines, fd|
|
42
|
+
lines.each { |line| block.call(line, fd) }
|
43
|
+
end
|
44
|
+
exitcode
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module Fixture
|
49
|
+
class << self
|
50
|
+
def for(path)
|
51
|
+
type, name = path.split ".", 2
|
52
|
+
raise "Unknown fixture '#{path}'" unless fixtures[type].include?(name)
|
53
|
+
fixtures[type][name].split("\n")
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def fixtures
|
58
|
+
@fixtures ||= load_fixtures
|
59
|
+
end
|
60
|
+
|
61
|
+
def load_fixtures
|
62
|
+
init = Hash.new { |h, k| h[k] = {} }
|
63
|
+
Dir[File.join(File.expand_path(File.dirname(__FILE__)), "fixtures", "*.yml")].inject(init) do |fixtures, path|
|
64
|
+
name = File.basename(path, ".yml")
|
65
|
+
fixtures[name] = YAML.load_file(path)
|
66
|
+
fixtures
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
RSpec.configure do |config|
|
74
|
+
config.include(SpecHelper)
|
75
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "itunes/store/transporter"
|
3
|
+
|
4
|
+
shared_examples_for "a transporter method" do
|
5
|
+
it "uses the default options" do
|
6
|
+
defaults = create_options
|
7
|
+
config = {
|
8
|
+
:path => "/",
|
9
|
+
:print_stderr => true,
|
10
|
+
:print_stdout => true
|
11
|
+
}
|
12
|
+
|
13
|
+
s = stub(command)
|
14
|
+
s.should_receive(:run)
|
15
|
+
|
16
|
+
klass = ITunes::Store::Transporter::Command.const_get(command)
|
17
|
+
klass.should_receive(:new).with(config, defaults).and_return(s)
|
18
|
+
|
19
|
+
described_class.new(config.merge(defaults)).send(method, options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
shared_examples_for "a transporter method without a package argument" do
|
24
|
+
it_behaves_like "a transporter method"
|
25
|
+
|
26
|
+
it "executes the underlying command" do
|
27
|
+
ITunes::Store::Transporter::Command.const_get(command).any_instance.should_receive(:run).with(options)
|
28
|
+
subject.send(method, options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
shared_examples_for "a transporter method with a package argument" do
|
33
|
+
it_behaves_like "a transporter method"
|
34
|
+
|
35
|
+
it "executes the underlying command" do
|
36
|
+
ITunes::Store::Transporter::Command.const_get(command).any_instance.should_receive(:run).with(:package => "package.itmsp")
|
37
|
+
subject.send(method, "package.itmsp")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
describe ITunes::Store::Transporter do
|
43
|
+
let(:options) { {} }
|
44
|
+
|
45
|
+
describe "#new" do
|
46
|
+
context "when the options are not a Hash or nil" do
|
47
|
+
it "raises an ArgumentError" do
|
48
|
+
lambda { described_class.new(123) }.should raise_exception(ArgumentError, /must be/)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#lookup" do
|
54
|
+
let(:method) { :lookup }
|
55
|
+
let(:command) { "Lookup" }
|
56
|
+
|
57
|
+
it_behaves_like "a transporter method without a package argument"
|
58
|
+
|
59
|
+
it "executes the underlying command" do
|
60
|
+
ITunes::Store::Transporter::Command::Lookup.any_instance.should_receive(:run)
|
61
|
+
subject.lookup
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#providers" do
|
66
|
+
let(:method) { :providers }
|
67
|
+
let(:command) { "Providers" }
|
68
|
+
|
69
|
+
it_behaves_like "a transporter method without a package argument"
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#schema" do
|
73
|
+
let(:method) { :schema }
|
74
|
+
let(:command) { "Schema" }
|
75
|
+
|
76
|
+
it_behaves_like "a transporter method without a package argument"
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#status" do
|
80
|
+
let(:method) { :status }
|
81
|
+
let(:command) { "Status" }
|
82
|
+
|
83
|
+
it_behaves_like "a transporter method without a package argument"
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#upload" do
|
87
|
+
let(:method) { :upload }
|
88
|
+
let(:command) { "Upload" }
|
89
|
+
|
90
|
+
it_behaves_like "a transporter method with a package argument"
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#verify" do
|
94
|
+
let(:method) { :verify }
|
95
|
+
let(:command) { "Verify" }
|
96
|
+
|
97
|
+
it_behaves_like "a transporter method with a package argument"
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#version" do
|
101
|
+
let(:method) { :version }
|
102
|
+
let(:command) { "Version" }
|
103
|
+
|
104
|
+
it_behaves_like "a transporter method without a package argument"
|
105
|
+
end
|
106
|
+
end
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: itunes_store_transporter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Skye Shaw
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-10 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: childprocess
|
16
|
+
requirement: &70193863731280 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.3.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70193863731280
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: optout
|
27
|
+
requirement: &70193863730780 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.0.2
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70193863730780
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rake
|
38
|
+
requirement: &70193863730320 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.9.2
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70193863730320
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: &70193863729860 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.8.0
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70193863729860
|
58
|
+
description: ! " iTunes::Store::Transporter is a wrapper around Apple's iTMSTransporter
|
59
|
+
program. It allows you to upload packages to the \n Apple Store, validate them,
|
60
|
+
retrieve status information, lookup metadata, and more!\n"
|
61
|
+
email: sshaw@lucas.cis.temple.edu
|
62
|
+
executables:
|
63
|
+
- itms
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files:
|
66
|
+
- README.rdoc
|
67
|
+
files:
|
68
|
+
- lib/itunes/store/transporter/command/lookup.rb
|
69
|
+
- lib/itunes/store/transporter/command/option.rb
|
70
|
+
- lib/itunes/store/transporter/command/providers.rb
|
71
|
+
- lib/itunes/store/transporter/command/schema.rb
|
72
|
+
- lib/itunes/store/transporter/command/status.rb
|
73
|
+
- lib/itunes/store/transporter/command/upload.rb
|
74
|
+
- lib/itunes/store/transporter/command/verify.rb
|
75
|
+
- lib/itunes/store/transporter/command/version.rb
|
76
|
+
- lib/itunes/store/transporter/command.rb
|
77
|
+
- lib/itunes/store/transporter/errors.rb
|
78
|
+
- lib/itunes/store/transporter/output_parser.rb
|
79
|
+
- lib/itunes/store/transporter/shell.rb
|
80
|
+
- lib/itunes/store/transporter/version.rb
|
81
|
+
- lib/itunes/store/transporter.rb
|
82
|
+
- README.rdoc
|
83
|
+
- spec/command_spec.rb
|
84
|
+
- spec/errors_spec.rb
|
85
|
+
- spec/output_parser_spec.rb
|
86
|
+
- spec/shell_spec.rb
|
87
|
+
- spec/spec_helper.rb
|
88
|
+
- spec/transporter_spec.rb
|
89
|
+
- bin/itms
|
90
|
+
homepage: http://github.com/sshaw/itunes_store_transporter
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.8.10
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Upload and manage your assets in the iTunes Store using the iTunes Store's
|
115
|
+
Transporter (iTMSTransporter).
|
116
|
+
test_files:
|
117
|
+
- spec/command_spec.rb
|
118
|
+
- spec/errors_spec.rb
|
119
|
+
- spec/output_parser_spec.rb
|
120
|
+
- spec/shell_spec.rb
|
121
|
+
- spec/spec_helper.rb
|
122
|
+
- spec/transporter_spec.rb
|
123
|
+
has_rdoc:
|