App-Ariza.git | t/ | 07-cli.rakutest


use v6.d;
use Test;

use App::Ariza;
use App::Ariza::Resources;

plan 7;

sub tmp-dir(--> IO::Path) {
    my $dir = $*TMPDIR.add("ariza-cli-{$*PID}-{(^1_000_000).pick}");
    $dir.mkdir;
    $dir;
}

sub rm-rf(IO::Path $d) {
    return unless $d.d;
    .&rm-rf for $d.dir.grep(*.d);
    .unlink for $d.dir.grep(*.f);
    $d.rmdir;
}

my $root = checkout-root();
my $bin  = $root.add('bin/ariza');

# Mirror this process's own -I flags into the child, rather than
# hardcoding lib/. `prove6 -Ilib` and `prove6 -I.` resolve the
# distribution differently — the latter reads META6.json, so
# $?DISTRIBUTION carries a version and the former does not — and the
# child has to agree with the parent about which it is.
my @inc = $*REPO.repo-chain
    .grep(* ~~ CompUnit::Repository::FileSystem)
    .map({ .prefix.absolute });
@inc = ($root.add('lib').absolute,) unless @inc;

#| Run bin/ariza the way a user would, against this checkout.
sub ariza(*@args) {
    my @flags = @inc.map({ |('-I', $_) });
    my $proc = run $*EXECUTABLE, |@flags, $bin.absolute, |@args, :out, :err;
    my $out = $proc.out.slurp(:close);
    my $err = $proc.err.slurp(:close);
    ($proc.exitcode, $out, $err)
}

subtest 'the distribution knows itself', {
    plan 3;
    ok App::Ariza.dist.defined, 'dist is the $?DISTRIBUTION object';
    ok App::Ariza.dist-version.defined, 'dist-version is defined';
    isnt App::Ariza.dist-version, '', 'and not empty';
};

subtest 'bin/ariza is a runnable script', {
    plan 2;
    ok $bin.f, 'bin/ariza exists';
    is $bin.lines.head.trim, '#!/usr/bin/env raku', 'with a raku shebang';
};

subtest 'ariza version', {
    plan 2;
    my ($code, $out, $) = ariza('version');
    is $code, 0, 'exits clean';
    is $out.trim, App::Ariza.dist-version,
       'and prints the same version the facade reports';
};

subtest 'ariza help', {
    plan 7;
    my ($code, $out, $) = ariza('help');
    isnt $code, 0, 'help exits non-zero, so a bare mistype is a shell error';
    ok $out.contains('--out-dir'), 'usage lists a verb\'s options';
    ok $out.contains('ariza version'), 'and the other verbs';
    ok $out.contains('ariza bundle'), 'including bundle';
    ok $out.contains('ariza smoke'), 'and smoke';
    ok $out.contains('ariza installers'), 'and installers';
    ok $out.contains('ariza scaffold-ci'), 'and scaffold-ci';
};

subtest 'ariza installers', {
    plan 6;
    my $out-dir = tmp-dir;
    my $app-dir = tmp-dir;
    LEAVE { rm-rf($out-dir); rm-rf($app-dir) }

    $app-dir.add('ariza.toml').spurt(q:to/TOML/);
    [app]
    name = "App::Kelpie"
    exec = "kelpie"
    display = "Kelpie"
    [bundle]
    platforms = ["macos-arm64", "windows-x86_64"]
    [installer]
    repo = "m-doughty/App-Kelpie"
    TOML

    my ($code, $out, $err) = ariza('installers', "--app={$app-dir.absolute}",
                                   "--out-dir={$out-dir.absolute}");
    is $code, 0, 'exits clean';
    is $out-dir.dir.map(*.basename).sort.List,
        ('install.ps1', 'install.sh', 'uninstall.ps1', 'uninstall.sh'),
        'wrote all four scripts';
    ok $out.contains('rendered 4 installers'), 'and said so';
    is $err.trim, '', 'with nothing on stderr for a clean manifest';

    # The ref the one-liner names, asserted through the CLI rather than
    # the library: `bin/ariza` declares its own `--branch` default, so a
    # library default of HEAD proves nothing about what a user who runs
    # the command actually gets. The two disagreed exactly once — the
    # library moved to HEAD, the script kept `main`, and the installers
    # rendered for a real release pointed at a branch that repository
    # does not have. Every generated script is checked, because only one
    # of the four is what the failure showed up in.
    nok $out-dir.dir.grep(*.f).first({ .slurp.contains('/main/') }),
        'and no generated script names a hardcoded branch: the CLI '
        ~ 'default is HEAD too, not just the library default';

    # An app with no [installer] table has nowhere to download from, and
    # is told that rather than handed a script that 404s.
    $app-dir.add('ariza.toml').spurt(
        "[app]\nname = \"A\"\nexec = \"a\"\ndisplay = \"A\"\n"
      ~ "[bundle]\nplatforms = [\"macos-arm64\"]\n");
    my ($bad, $, $bad-err) = ariza('installers', "--app={$app-dir.absolute}",
                                   "--out-dir={$out-dir.absolute}");
    isnt $bad, 0, 'an app with no installer.repo is a non-zero exit, not a stack trace';
};

subtest 'ariza scaffold-ci', {
    plan 6;
    my $app-dir = tmp-dir;
    LEAVE { rm-rf($app-dir) }

    $app-dir.add('ariza.toml').spurt(q:to/TOML/);
    [app]
    name = "App::Kelpie"
    exec = "kelpie"
    display = "Kelpie"
    [bundle]
    platforms = ["macos-arm64", "windows-x86_64"]
    [installer]
    repo = "m-doughty/App-Kelpie"
    TOML

    my ($code, $out, $err) = ariza('scaffold-ci', "--app={$app-dir.absolute}");
    is $code, 0, 'exits clean';
    is $app-dir.add('.github/workflows').dir.map(*.basename).sort.List,
        ('release.yml', 'test.yml'),
        'and writes both workflows into the app repository it was given';
    ok $out.contains('scaffolded 2 of 2 workflows'), 'saying what it did';
    is $err.trim, '', 'with nothing on stderr for a clean manifest';

    # The second run is the one that matters: a test workflow someone has
    # edited is not ariza's to overwrite.
    my ($again, $again-out, $) = ariza('scaffold-ci', "--app={$app-dir.absolute}");
    is $again, 0, 'a second run exits clean too';
    ok $again-out.contains('kept') && $again-out.contains('scaffolded 1 of 2'),
        'keeping test.yml and regenerating release.yml';
};

subtest 'ariza bundle and ariza smoke fail like commands, not like stack traces', {
    plan 5;
    my $dir = tmp-dir;
    LEAVE { rm-rf($dir) }

    # The retired installer-script renderer. A verb that no longer exists
    # is a usage error like any other typo, not a half-working command.
    my ($retired, $retired-out, $) = ariza('render-legacy');
    ok $retired != 0 && !$retired-out.contains('render-legacy'),
       'render-legacy is gone, and the usage does not offer it';

    # `--app` is required, and a missing one is a usage error rather
    # than an exception from somewhere inside the build.
    my ($no-app, $, $) = ariza('bundle');
    isnt $no-app, 0, 'bundle without --app exits non-zero';

    my ($no-dist, $, $err) = ariza('bundle', "--app={$dir.absolute}",
                                   "--out-dir={$dir.absolute}");
    isnt $no-dist, 0, 'and a directory that is not an app checkout does too';
    ok $err.contains('ariza:'), 'reporting it as an ariza error, one line';

    my ($no-archive, $, $smoke-err) = ariza('smoke',
        "--archive={$dir.add('nothing.tar.gz').absolute}");
    isnt $no-archive, 0, 'smoke on a missing archive exits non-zero';
};