Selkie.git | t/ | 106-subscribe-prime-reentry.rakutest


use Test;
use lib 'lib';

use Selkie::Store;

# subscribe-path-callback primes by invoking the callback synchronously.
# A callback that re-subscribes its own id on some condition (the
# App::Cantina AvatarList cache-miss re-arm, 2026-08-11) used to recurse
# without bound inside the hosting tick — a silent app-wide freeze. The
# store now latches ids whose prime is on the call stack: the nested
# subscription still registers, but its prime is skipped.

plan 3;

subtest 'self-re-subscribing prime terminates and stays armed' => {
    plan 4;
    my $store = Selkie::Store.new;
    my $calls = 0;

    sub arm() {
        $store.subscribe-path-callback(
            'reentrant', ['ui', 'ready'],
            -> $v {
                $calls++;
                # Unconditional worst case: every prime re-arms.
                arm() unless $v.defined;
            },
            Selkie::Widget,
        );
    }

    lives-ok { arm() }, 'the re-arming prime returns instead of recursing';
    is $calls, 1, 'callback ran exactly once (nested prime skipped)';
    is $store.subscription-count, 1, 'subscription is registered once';

    $store.assoc-in('ui', 'ready', value => 'pixels');
    $store.tick;
    is $calls, 2, 'the armed subscription still fires on a real change';
};

subtest 'mutual-cycle primes terminate' => {
    plan 2;
    my $store = Selkie::Store.new;
    my @order;

    sub arm-a() {
        $store.subscribe-path-callback('cycle-a', ['a'], -> $ {
            @order.push('a');
            arm-b();
        }, Selkie::Widget);
    }
    sub arm-b() {
        $store.subscribe-path-callback('cycle-b', ['b'], -> $ {
            @order.push('b');
            arm-a();
        }, Selkie::Widget);
    }

    lives-ok { arm-a() }, 'a→b→a subscription cycle terminates';
    # a primes, arms b; b primes, tries to re-arm a — a is still priming,
    # so its nested prime is skipped and the cycle ends.
    is-deeply @order, ['a', 'b'], 'each id primed exactly once';
};

subtest 'independent nested subscription still primes' => {
    plan 2;
    my $store = Selkie::Store.new;
    my Bool $inner-primed = False;

    $store.subscribe-path-callback('outer', ['x'], -> $ {
        $store.subscribe-path-callback('inner', ['y'], -> $ {
            $inner-primed = True;
        }, Selkie::Widget);
    }, Selkie::Widget);

    ok $inner-primed,
        'a different id subscribed from a prime still gets its prime';
    is $store.subscription-count, 2, 'both subscriptions registered';
};

done-testing;