### ----------------------------------------------------
### -- WWW::GCloud
### -- Licenses: Artistic-2.0
### -- Authors: Vadim Belman
### -- File: t/020-cpromise.rakutest
### ----------------------------------------------------
use v6.e.PREVIEW;
use Test::Async;

use WWW::GCloud::CPromise;
use WWW::GCloud;

plan 4;

if WWW::GCloud::CORE-PROMISE-OK {
    skip "this version of Rakudo already implements 'andthen' and 'orelse' in its Promise", 4;
}
else {
    my %test-status;
    my $slock = Lock.new;

    my sub get-results($p1) {
        %test-status = ();

        my sub record-result($p, $key) {
            $slock.protect: {
                %test-status{$key}<status> = $p.status;
                if $p.status == Kept {
                    %test-status{$key}<result> = $p.result;
                }
                else {
                    %test-status{$key}<cause> = ~$p.cause;
                }
            }
        }

        $p1.then: { record-result($_, "independent-then"); };

        $p1
            .andthen({ record-result($_, "andthen1"); .result < -10 ?? die "andthen1 dies" !! .result * 2 })
            .andthen({ record-result($_, "andthen2"); .result < 0 ?? die "andthen2 dies" !! .result })
            .orelse({
                record-result($_, "orelse1");
                .cause.message.contains("andthen2")
                    ??  die "orelse1 dies"
                    !! "problem 1"
            })
            .orelse({ record-result($_, "orelse2"); "problem 2" })
            .then({ record-result($_, "final-then"); "final: " ~ .result })
    }

    my sub test-conditionals(Str:D $message, Str:D :$method, :$value, :$rc, :%status) is test-assertion {
        subtest $message, {
            plan 2;

            subtest "immediate", {
                plan 3;
                my $p1 = Promise.new but WWW::GCloud::CPromise;
                $p1."$method"($value);
                my $p2 = get-results($p1);

                isa-ok $p2, WWW::GCloud::CPromise, "CPromise role is not lost";
                is await($p2), $rc, "final promise result";
                is-deeply %test-status, %status, "thens ran as expected";
            }

            subtest "planned", {
                plan 3;
                my $p1 = Promise.new but WWW::GCloud::CPromise;
                my $p2 = get-results($p1);
                $p1."$method"($value);

                isa-ok $p2, WWW::GCloud::CPromise, "CPromise role is not lost";
                is await($p2), $rc, "final promise result";
                is-deeply %test-status, %status, "thens ran as expected";
            }
        }
    }

    test-conditionals "simple keep",
        method => 'keep',
        value => 42,
        rc => 'final: 84',
        status => {
            andthen1 => { result => 42, status => Kept },
            andthen2 => { result => 84, status => Kept },
            final-then => { result => 84, status => Kept },
            independent-then => { result => 42, status => Kept },
        };

    test-conditionals "simple break",
        method => 'break',
        value => 'something is wrong',
        rc => 'final: problem 1',
        status => {
            final-then => { result => 'problem 1', status => Kept },
            independent-then => { cause => 'something is wrong', status => Broken },
            orelse1 => { cause => 'something is wrong', status => Broken }
        };

    test-conditionals "the first .andthen dies",
        method => 'keep',
        value => -13,
        rc => 'final: problem 1',
        status => {
            andthen1 => { result => -13, status => Kept, },
            final-then => { result => 'problem 1', status => Kept },
            independent-then => { result => -13, status => Kept },
            orelse1 => { cause => 'andthen1 dies', status => Broken }
        };

    test-conditionals "the second .andthen and the first .orelse dies",
        method => 'keep',
        value => -5,
        rc => 'final: problem 2',
        status => {
            andthen1 => { result => -5, status => Kept, },
            andthen2 => { result => -10, status => Kept, },
            final-then => { result => 'problem 2', status => Kept },
            independent-then => { result => -5, status => Kept },
            orelse1 => { cause => 'andthen2 dies', status => Broken },
            orelse2 => { cause => 'orelse1 dies', status => Broken }
        };

}


