summaryrefslogtreecommitdiff
path: root/xt/cli
diff options
context:
space:
mode:
Diffstat (limited to 'xt/cli')
-rw-r--r--xt/cli/bundle.t19
-rw-r--r--xt/cli/check.t96
-rw-r--r--xt/cli/cpanfile.t44
-rw-r--r--xt/cli/deployment.t26
-rw-r--r--xt/cli/deps_phase.t29
-rw-r--r--xt/cli/exec.t84
-rw-r--r--xt/cli/freeze.t24
-rw-r--r--xt/cli/help.t25
-rw-r--r--xt/cli/install.t56
-rw-r--r--xt/cli/json_pp.t23
-rw-r--r--xt/cli/mirror.t38
-rw-r--r--xt/cli/mismatch.t24
-rw-r--r--xt/cli/no_cpanfile.t13
-rw-r--r--xt/cli/perl.t23
-rw-r--r--xt/cli/snapshot.t65
-rw-r--r--xt/cli/subdir.t25
-rw-r--r--xt/cli/tree.t23
-rw-r--r--xt/cli/update.t78
-rw-r--r--xt/cli/version.t12
-rw-r--r--xt/cli/without.t67
20 files changed, 794 insertions, 0 deletions
diff --git a/xt/cli/bundle.t b/xt/cli/bundle.t
new file mode 100644
index 0000000..aa84f24
--- /dev/null
+++ b/xt/cli/bundle.t
@@ -0,0 +1,19 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+{
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.12';
+EOF
+
+ $app->run("install");
+ $app->run("bundle");
+
+ ok -f ($app->dir . "/vendor/cache/authors/id/D/DO/DOY/Try-Tiny-0.12.tar.gz");
+}
+
+done_testing;
+
diff --git a/xt/cli/check.t b/xt/cli/check.t
new file mode 100644
index 0000000..dd00ed9
--- /dev/null
+++ b/xt/cli/check.t
@@ -0,0 +1,96 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+subtest 'carton check fails when there is no lock' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.11';
+EOF
+
+ $app->run("check");
+ like $app->stderr, qr/find cpanfile\.snapshot/;
+};
+
+subtest 'carton install and check' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.11';
+EOF
+
+ $app->run("install");
+
+ $app->run("check");
+ like $app->stdout, qr/are satisfied/;
+
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.11/;
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '0.16';
+EOF
+
+ $app->run("check");
+ like $app->stdout, qr/not satisfied/;
+
+ TODO: {
+ local $TODO = 'exec does not verify lock';
+ $app->run("exec", "perl", "use Try::Tiny");
+ like $app->stderr, qr/\.snapshot/;
+ }
+
+ $app->run("install");
+
+ $app->run("check");
+ like $app->stdout, qr/are satisfied/;
+
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.\d\d/;
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '10.00';
+EOF
+
+ $app->run("check");
+ like $app->stdout, qr/not satisfied/;
+
+ $app->run("install");
+ like $app->stderr, qr/failed/;
+
+ $app->run("check");
+ like $app->stdout, qr/not satisfied/;
+};
+
+subtest 'detect unused modules' => sub {
+ my $app = cli;
+ $app->write_cpanfile("requires 'Try::Tiny';");
+
+ $app->run("install");
+ $app->write_cpanfile("");
+
+
+ TODO: {
+ local $TODO = "Can't detect superflous modules";
+ $app->run("install");
+ $app->run("list");
+ is $app->stdout, "";
+
+ $app->run("check");
+ like $app->stdout, qr/unused/;
+ }
+};
+
+subtest 'detect downgrade' => sub {
+ my $app = cli;
+ $app->write_cpanfile("requires 'URI';");
+ $app->run("install");
+
+ $app->write_cpanfile("requires 'URI', '== 1.59';");
+ $app->run("check");
+
+ like $app->stdout, qr/not satisfied/;
+ like $app->stdout, qr/URI has version .* Needs == 1\.59/;
+};
+
+done_testing;
+
diff --git a/xt/cli/cpanfile.t b/xt/cli/cpanfile.t
new file mode 100644
index 0000000..b663b37
--- /dev/null
+++ b/xt/cli/cpanfile.t
@@ -0,0 +1,44 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+subtest 'carton install --cpanfile' => sub {
+ my $app = cli();
+ $app->write_file('cpanfile.foo', <<EOF);
+requires 'Try::Tiny', '== 0.11';
+EOF
+ $app->run("install", "--cpanfile", "cpanfile.foo");
+ $app->run("check", "--cpanfile", "cpanfile.foo");
+
+ ok !$app->dir->child('cpanfile.snapshot')->exists;
+ ok $app->dir->child('cpanfile.foo.snapshot')->exists;
+
+ like $app->stdout, qr/are satisfied/;
+
+ local $ENV{PERL_CARTON_CPANFILE} = $app->dir->child('cpanfile.foo')->absolute;
+
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.11/;
+
+ $app->run("exec", "perl", "-e", "use Try::Tiny\ 1");
+ like $app->stderr, qr/Try::Tiny .* 0\.11/;
+};
+
+subtest 'PERL_CARTON_CPANFILE' => sub {
+ my $app = cli();
+
+ local $ENV{PERL_CARTON_CPANFILE} = $app->dir->child('cpanfile.foo')->absolute;
+
+ $app->write_file('cpanfile.foo', <<EOF);
+requires 'Try::Tiny', '== 0.11';
+EOF
+
+ $app->run("install");
+ $app->run("list");
+
+ like $app->stdout, qr/Try-Tiny-0\.11/;
+ ok $app->dir->child('cpanfile.foo.snapshot')->exists;
+};
+
+done_testing;
+
diff --git a/xt/cli/deployment.t b/xt/cli/deployment.t
new file mode 100644
index 0000000..175f87a
--- /dev/null
+++ b/xt/cli/deployment.t
@@ -0,0 +1,26 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+{
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.11';
+EOF
+
+ $app->run("install", "--deployment");
+ like $app->stderr, qr/deployment requires cpanfile\.snapshot/;
+
+ $app->run("install");
+ $app->clean_local;
+
+ $app->run("install", "--deployment");
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.11/;
+
+ $app->run("exec", "perl", "-e", "use Try::Tiny 2;");
+ like $app->stderr, qr/Try::Tiny.* version 0\.11/;
+}
+
+done_testing;
+
diff --git a/xt/cli/deps_phase.t b/xt/cli/deps_phase.t
new file mode 100644
index 0000000..862dc76
--- /dev/null
+++ b/xt/cli/deps_phase.t
@@ -0,0 +1,29 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+{
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+on test => sub {
+ requires 'Test::NoWarnings';
+ recommends 'Test::Pretty';
+};
+on develop => sub {
+ requires 'Path::Tiny';
+};
+EOF
+
+ $app->run("install");
+
+ $app->run("list");
+ like $app->stdout, qr/Test-NoWarnings/;
+ like $app->stdout, qr/Path-Tiny/;
+ unlike $app->stdout, qr/Test-Pretty/;
+}
+
+done_testing;
+
+
+
diff --git a/xt/cli/exec.t b/xt/cli/exec.t
new file mode 100644
index 0000000..233f790
--- /dev/null
+++ b/xt/cli/exec.t
@@ -0,0 +1,84 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+subtest 'carton exec without a command', sub {
+ my $app = cli();
+ $app->write_cpanfile('');
+ $app->run("install");
+ $app->run("exec");
+ like $app->stderr, qr/carton exec needs a command/;
+ is $app->exit_code, 255;
+};
+
+subtest 'exec without cpanfile', sub {
+ my $app = cli();
+ $app->run("exec", "perl", "-e", 1);
+ like $app->stderr, qr/Can't locate cpanfile/;
+ is $app->exit_code, 255;
+};
+
+subtest 'exec without a snapshot', sub {
+ my $app = cli();
+ $app->write_cpanfile();
+ $app->run("exec", "perl", "-e", 1);
+ like $app->stderr, qr/cpanfile\.snapshot/;
+ is $app->exit_code, 255;
+};
+
+subtest 'carton exec', sub {
+ my $app = cli();
+ $app->write_cpanfile('');
+ $app->run("install");
+
+ TODO: {
+ local $TODO = "exec now does not strip site_perl";
+ $app->run("exec", "perl", "-e", "use Try::Tiny");
+ like $app->stderr, qr/Can't locate Try\/Tiny.pm/;
+ }
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.11';
+EOF
+
+ $app->run("install");
+
+ $app->run("exec", "--", "perl", "-e", 'use Try::Tiny; print $Try::Tiny::VERSION, "\n"');
+ like $app->stdout, qr/0\.11/;
+
+ $app->run("exec", "perl", "-e", 'use Try::Tiny; print $Try::Tiny::VERSION, "\n"');
+ like $app->stdout, qr/0\.11/, "No need for -- as well";
+
+ $app->run("exec", "perl", "-MTry::Tiny", "-e", 'print $Try::Tiny::VERSION, "\n"');
+ like $app->stdout, qr/0\.11/;
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny';
+requires 'App::Ack', '== 2.02';
+EOF
+
+ $app->run("install");
+ $app->run("exec", "--", "ack", "--version");
+
+ like $app->stdout, qr/ack 2\.02/;
+};
+
+subtest 'carton exec perl -Ilib', sub {
+ my $app = cli();
+ $app->write_cpanfile('');
+ $app->run("install");
+
+ $app->dir->child("lib")->mkpath;
+ $app->dir->child("lib/FooBarBaz.pm")->spew("package FooBarBaz; 1");
+
+ $app->run("exec", "perl", "-Ilib", "-e", 'use FooBarBaz; print "foo"');
+ like $app->stdout, qr/foo/;
+ unlike $app->stderr, qr/exec -Ilib is deprecated/;
+
+ $app->run("exec", "-Ilib", "perl", "-e", 'print "foo"');
+ like $app->stdout, qr/foo/;
+ like $app->stderr, qr/exec -Ilib is deprecated/;
+};
+
+done_testing;
+
diff --git a/xt/cli/freeze.t b/xt/cli/freeze.t
new file mode 100644
index 0000000..f02a2c3
--- /dev/null
+++ b/xt/cli/freeze.t
@@ -0,0 +1,24 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+{
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.11';
+EOF
+
+ $app->run("install");
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.11/;
+
+ $app->clean_local;
+
+ $app->run("install");
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.11/;
+}
+
+done_testing;
+
diff --git a/xt/cli/help.t b/xt/cli/help.t
new file mode 100644
index 0000000..0ad0896
--- /dev/null
+++ b/xt/cli/help.t
@@ -0,0 +1,25 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+{
+ my $app = cli();
+ $app->run("help");
+ like $app->stdout, qr/Carton - Perl module/;
+
+ $app->run("-h");
+ like $app->stdout, qr/Carton - Perl module/;
+
+ $app->run("help", "install");
+ like $app->stdout, qr/Install the dependencies/;
+
+ $app->run("install", "-h");
+ like $app->stdout, qr/Install the dependencies/;
+
+ $app->run("help", "foobarbaz");
+ is $app->stdout, '';
+ like $app->stderr, qr/No documentation found/;
+}
+
+done_testing;
+
diff --git a/xt/cli/install.t b/xt/cli/install.t
new file mode 100644
index 0000000..926f3de
--- /dev/null
+++ b/xt/cli/install.t
@@ -0,0 +1,56 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+subtest 'carton install with version range' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'CPAN::Test::Dummy::Perl5::Deps::VersionRange';
+EOF
+
+ $app->run("install");
+ $app->run("tree");
+ like $app->stdout, qr/Try::Tiny/;
+ unlike $app->stderr, qr/Could not parse snapshot file/;
+};
+
+subtest 'meta info for ancient modules' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'Algorithm::Diff';
+EOF
+
+ $app->run("install");
+ $app->run("list");
+
+ like $app->stdout, qr/Algorithm-Diff/;
+};
+
+subtest 'meta info for modules with version->declare' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'CPAN::Test::Dummy::Perl5::VersionDeclare', 'v0.0.1';
+EOF
+
+ $app->run("install");
+ $app->run("check");
+
+ like $app->stdout, qr/are satisfied/;
+ unlike $app->stderr, qr/is not installed/;
+};
+
+subtest 'meta info for modules with qv()' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'CPAN::Test::Dummy::Perl5::VersionQV', 'v0.1.0';
+EOF
+
+ $app->run("install");
+ $app->run("check");
+
+ like $app->stdout, qr/are satisfied/;
+ unlike $app->stderr, qr/is not installed/;
+};
+
+done_testing;
+
diff --git a/xt/cli/json_pp.t b/xt/cli/json_pp.t
new file mode 100644
index 0000000..0c64395
--- /dev/null
+++ b/xt/cli/json_pp.t
@@ -0,0 +1,23 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+plan skip_all => "perl <= 5.14" if $] >= 5.015;
+
+{
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'JSON';
+requires 'CPAN::Meta', '2.12';
+EOF
+
+ $app->run("install");
+ $app->clean_local;
+
+ $app->run("install", "--deployment");
+ unlike $app->stderr, qr/JSON::PP is not in range/;
+}
+
+done_testing;
+
diff --git a/xt/cli/mirror.t b/xt/cli/mirror.t
new file mode 100644
index 0000000..5ab9a29
--- /dev/null
+++ b/xt/cli/mirror.t
@@ -0,0 +1,38 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+my $cwd = Path::Tiny->cwd;
+
+{
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'Hash::MultiValue';
+EOF
+
+ local $ENV{PERL_CARTON_MIRROR} = "$cwd/xt/mirror";
+ $app->run("install");
+
+ $app->run("list");
+ like $app->stdout, qr/^Hash-MultiValue-0.08/m;
+}
+
+{
+ # fallback to CPAN
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'PSGI';
+EOF
+
+ local $ENV{PERL_CARTON_MIRROR} = "$cwd/xt/mirror";
+ $app->run("install");
+
+ $app->run("list");
+ like $app->stdout, qr/^PSGI-/m;
+}
+
+done_testing;
+
+
+
diff --git a/xt/cli/mismatch.t b/xt/cli/mismatch.t
new file mode 100644
index 0000000..11d5af4
--- /dev/null
+++ b/xt/cli/mismatch.t
@@ -0,0 +1,24 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+{
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'Data::Dumper' => '== 2.139';
+requires 'Test::Differences' => '== 0.61';
+EOF
+
+ $app->run("install");
+ $app->run("list");
+
+ like $app->stdout, qr/Data-Dumper-2\.139/;
+ like $app->stdout, qr/Test-Differences-0\.61/;
+
+ $app->run("check");
+ like $app->stdout, qr/are satisfied/;
+}
+
+done_testing;
+
diff --git a/xt/cli/no_cpanfile.t b/xt/cli/no_cpanfile.t
new file mode 100644
index 0000000..e12d2c3
--- /dev/null
+++ b/xt/cli/no_cpanfile.t
@@ -0,0 +1,13 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+{
+ my $app = cli();
+ $app->run("install");
+ like $app->stderr, qr/Can't locate cpanfile/;
+ is $app->exit_code, 255;
+}
+
+done_testing;
+
diff --git a/xt/cli/perl.t b/xt/cli/perl.t
new file mode 100644
index 0000000..a7e0143
--- /dev/null
+++ b/xt/cli/perl.t
@@ -0,0 +1,23 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+{
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'perl', '5.8.5';
+requires 'Hash::MultiValue';
+EOF
+
+ $app->run("install");
+ like $app->stdout, qr/Complete/;
+
+ $app->run("list");
+ like $app->stdout, qr/Hash-MultiValue-/;
+}
+
+done_testing;
+
+
+
diff --git a/xt/cli/snapshot.t b/xt/cli/snapshot.t
new file mode 100644
index 0000000..b036af7
--- /dev/null
+++ b/xt/cli/snapshot.t
@@ -0,0 +1,65 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+subtest 'snapshot file has canonical representation' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.11';
+requires 'Getopt::Long', '2.41';
+EOF
+
+ $app->run("install");
+
+ my $content = $app->dir->child('cpanfile.snapshot')->slurp;
+ for (1..3) {
+ $app->dir->child('cpanfile.snapshot')->remove;
+ $app->run("install");
+ is $content, $app->dir->child('cpanfile.snapshot')->slurp;
+ }
+};
+
+subtest 'Bad snapshot version' => sub {
+ my $app = cli();
+ $app->write_cpanfile('');
+ $app->write_file('cpanfile.snapshot', <<EOF);
+# carton snapshot format: version 111
+EOF
+
+ $app->run("install");
+ like $app->stderr, qr/Could not parse/;
+};
+
+subtest 'Bad snapshot file' => sub {
+ my $app = cli();
+ $app->write_cpanfile('');
+ $app->write_file('cpanfile.snapshot', <<EOF);
+# carton snapshot format: version 1.0
+DISTRIBUTIONS
+ Foo-Bar-1
+ unknown: foo
+EOF
+
+ $app->run("install");
+ like $app->stderr, qr/Could not parse/;
+};
+
+subtest 'snapshot file support separate CRLF' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.11';
+requires 'Getopt::Long', '2.41';
+EOF
+
+ $app->run("install");
+
+ my $content = $app->dir->child('cpanfile.snapshot')->slurp;
+ $content =~ s/\n/\r\n/g;
+ $app->write_file('cpanfile.snapshot', $content);
+
+ $app->run("install");
+ ok !$app->stderr;
+};
+
+done_testing;
+
diff --git a/xt/cli/subdir.t b/xt/cli/subdir.t
new file mode 100644
index 0000000..84d244f
--- /dev/null
+++ b/xt/cli/subdir.t
@@ -0,0 +1,25 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+subtest 'carton exec in subdir', sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny';
+EOF
+ $app->run('install');
+
+ $app->dir->child('x')->mkpath;
+
+ $app->run_in_dir('x' => 'list');
+ like $app->stdout, qr/Try-Tiny/;
+
+ $app->run_in_dir('x' => 'check');
+ like $app->stdout, qr/are satisfied/;
+
+ $app->run_in_dir('x' => 'install');
+ like $app->stdout, qr/Complete/;
+ unlike $app->stderr, qr/failed/;
+};
+
+done_testing;
diff --git a/xt/cli/tree.t b/xt/cli/tree.t
new file mode 100644
index 0000000..bc7ce8c
--- /dev/null
+++ b/xt/cli/tree.t
@@ -0,0 +1,23 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+{
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'HTML::Parser';
+EOF
+
+ $app->run("install");
+ $app->run("tree");
+
+ is $app->exit_code, 0;
+ like $app->stdout, qr/^HTML::Parser \(HTML-Parser-/m;
+ like $app->stdout, qr/^ HTML::Tagset \(HTML-Tagset-/m;
+}
+
+done_testing;
+
+
+
diff --git a/xt/cli/update.t b/xt/cli/update.t
new file mode 100644
index 0000000..22745bd
--- /dev/null
+++ b/xt/cli/update.t
@@ -0,0 +1,78 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+subtest 'carton update NonExistentModule' => sub {
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.09';
+EOF
+
+ $app->run("install");
+ $app->run("update", "XYZ");
+ like $app->stderr, qr/Could not find module XYZ/;
+};
+
+subtest 'carton update upgrades a dist' => sub {
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.09';
+EOF
+
+ $app->run("install");
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.09/;
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '>= 0.09, <= 0.12';
+EOF
+
+ $app->run("install");
+ $app->run("check");
+ like $app->stdout, qr/are satisfied/;
+
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.09/;
+
+ $app->run("update", "Try::Tiny");
+ like $app->stdout, qr/installed Try-Tiny-0\.12.*upgraded from 0\.09/;
+
+ $app->run("check");
+ like $app->stdout, qr/are satisfied/;
+
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.12/;
+};
+
+subtest 'downgrade a distribution' => sub {
+ my $app = cli();
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '0.16';
+EOF
+ $app->run("install");
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.\d\d/;
+
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '== 0.09';
+EOF
+ $app->run("update");
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.09/;
+
+ TODO: {
+ local $TODO = 'collecting wrong install info';
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny', '0.09';
+EOF
+ $app->run("install");
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-0\.09/;
+ }
+};
+
+done_testing;
+
diff --git a/xt/cli/version.t b/xt/cli/version.t
new file mode 100644
index 0000000..d99b9d6
--- /dev/null
+++ b/xt/cli/version.t
@@ -0,0 +1,12 @@
+use strict;
+use Test::More;
+
+use xt::CLI;
+
+my $app = cli();
+$app->run("version");
+
+like $app->stdout, qr/carton $Carton::VERSION/;
+
+done_testing;
+
diff --git a/xt/cli/without.t b/xt/cli/without.t
new file mode 100644
index 0000000..d9c51f7
--- /dev/null
+++ b/xt/cli/without.t
@@ -0,0 +1,67 @@
+use strict;
+use Test::More;
+use xt::CLI;
+
+subtest 'carton install --without develop' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny';
+
+on 'develop' => sub {
+ requires 'Hash::MultiValue', '== 0.14';
+};
+EOF
+
+ $app->run("install");
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-/;
+ like $app->stdout, qr/Hash-MultiValue-0\.14/;
+
+ $app->run("exec", "perl", "-e", "use Hash::MultiValue\ 1");
+ like $app->stderr, qr/Hash::MultiValue .* version 0.14/;
+
+ $app->clean_local;
+
+ $app->run("install", "--without", "develop");
+ $app->run("list");
+ like $app->stdout, qr/Try-Tiny-/;
+
+ TODO: {
+ local $TODO = "--without is not remembered for list";
+ unlike $app->stdout, qr/Hash-MultiValue-/;
+ }
+
+ $app->run("exec", "perl", "-e", "use Hash::MultiValue\ 1");
+ unlike $app->stderr, qr/Hash::MultiValue .* version 0.14/;
+};
+
+subtest 'without features' => sub {
+ my $app = cli();
+ $app->write_cpanfile(<<EOF);
+requires 'Try::Tiny';
+
+feature 'stream' => sub {
+ requires 'Stream::Buffered', '== 0.01';
+};
+EOF
+
+ $app->run("install");
+ $app->run("list");
+ like $app->stdout, qr/Stream-Buffered-0\.01/;
+
+ $app->clean_local;
+
+ $app->run("install", "--deployment");
+ $app->run("exec", "perl", "-e", "use Stream::Buffered 1");
+ like $app->stderr, qr/Stream::Buffered .* version 0\.01/;
+
+ $app->clean_local;
+
+ $app->run("install", "--without", "stream");
+
+ $app->run("exec", "perl", "-e", "use Stream::Buffered 1");
+ unlike $app->stderr, qr/Stream::Buffered .* version 0\.01/;
+};
+
+done_testing;
+